HeadsetClientStateMachine.java revision 05462fe44531f887fe91ac173ec645e91b236025
1/*
2 * Copyright (c) 2014 The Android Open Source Project
3 * Copyright (C) 2012 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18/**
19 * Bluetooth Headset Client StateMachine
20 *                      (Disconnected)
21 *                           | ^  ^
22 *                   CONNECT | |  | DISCONNECTED
23 *                           V |  |
24 *                   (Connecting) |
25 *                           |    |
26 *                 CONNECTED |    | DISCONNECT
27 *                           V    |
28 *                        (Connected)
29 *                           |    ^
30 *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
31 *                           V    |
32 *                         (AudioOn)
33 */
34
35package com.android.bluetooth.hfpclient;
36
37import android.bluetooth.BluetoothAdapter;
38import android.bluetooth.BluetoothDevice;
39import android.bluetooth.BluetoothHeadsetClient;
40import android.bluetooth.BluetoothHeadsetClientCall;
41import android.bluetooth.BluetoothProfile;
42import android.bluetooth.BluetoothUuid;
43import android.content.Context;
44import android.content.Intent;
45import android.media.AudioManager;
46import android.os.Bundle;
47import android.os.Message;
48import android.os.ParcelUuid;
49import android.os.SystemClock;
50import android.util.Log;
51import android.util.Pair;
52import android.telecom.TelecomManager;
53
54import com.android.bluetooth.Utils;
55import com.android.bluetooth.btservice.AdapterService;
56import com.android.bluetooth.btservice.ProfileService;
57import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
58import com.android.internal.util.IState;
59import com.android.internal.util.State;
60import com.android.internal.util.StateMachine;
61
62import java.util.ArrayList;
63import java.util.Arrays;
64import java.util.HashSet;
65import java.util.Hashtable;
66import java.util.Iterator;
67import java.util.LinkedList;
68import java.util.List;
69import java.util.Queue;
70import java.util.Set;
71
72import com.android.bluetooth.R;
73
74final class HeadsetClientStateMachine extends StateMachine {
75    private static final String TAG = "HeadsetClientStateMachine";
76    private static final boolean DBG = false;
77
78    static final int NO_ACTION = 0;
79
80    // external actions
81    static final int CONNECT = 1;
82    static final int DISCONNECT = 2;
83    static final int CONNECT_AUDIO = 3;
84    static final int DISCONNECT_AUDIO = 4;
85    static final int VOICE_RECOGNITION_START = 5;
86    static final int VOICE_RECOGNITION_STOP = 6;
87    static final int SET_MIC_VOLUME = 7;
88    static final int SET_SPEAKER_VOLUME = 8;
89    static final int DIAL_NUMBER = 10;
90    static final int ACCEPT_CALL = 12;
91    static final int REJECT_CALL = 13;
92    static final int HOLD_CALL = 14;
93    static final int TERMINATE_CALL = 15;
94    static final int ENTER_PRIVATE_MODE = 16;
95    static final int SEND_DTMF = 17;
96    static final int EXPLICIT_CALL_TRANSFER = 18;
97    static final int LAST_VTAG_NUMBER = 19;
98    static final int DISABLE_NREC = 20;
99
100    // internal actions
101    static final int QUERY_CURRENT_CALLS = 50;
102    static final int QUERY_OPERATOR_NAME = 51;
103    static final int SUBSCRIBER_INFO = 52;
104    // special action to handle terminating specific call from multiparty call
105    static final int TERMINATE_SPECIFIC_CALL = 53;
106
107    static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec.
108    static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec.
109
110    private static final int STACK_EVENT = 100;
111
112    public static final Integer HF_ORIGINATED_CALL_ID = new Integer(-1);
113    private long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds
114
115    private final Disconnected mDisconnected;
116    private final Connecting mConnecting;
117    private final Connected mConnected;
118    private final AudioOn mAudioOn;
119
120    private final HeadsetClientService mService;
121
122    // Set of calls that represent the accurate state of calls that exists on AG and the calls that
123    // are currently in process of being notified to the AG from HF.
124    private final Hashtable<Integer, BluetoothHeadsetClientCall> mCalls = new Hashtable<>();
125    // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls
126    // which is eventually used to inform the telephony stack of any changes to call on HF.
127    private final Hashtable<Integer, BluetoothHeadsetClientCall> mCallsUpdate = new Hashtable<>();
128
129    private int mIndicatorNetworkState;
130    private int mIndicatorNetworkType;
131    private int mIndicatorNetworkSignal;
132    private int mIndicatorBatteryLevel;
133
134    private int mIndicatorCall;
135    private int mIndicatorCallSetup;
136    private int mIndicatorCallHeld;
137    private boolean mVgsFromStack = false;
138    private boolean mVgmFromStack = false;
139
140    private String mOperatorName;
141    private String mSubscriberInfo;
142
143    private int mVoiceRecognitionActive;
144    private int mInBandRingtone;
145
146    private int mMaxAmVcVol;
147    private int mMinAmVcVol;
148
149    // queue of send actions (pair action, action_data)
150    private Queue<Pair<Integer, Object>> mQueuedActions;
151
152    // last executed command, before action is complete e.g. waiting for some
153    // indicator
154    private Pair<Integer, Object> mPendingAction;
155
156    private final AudioManager mAudioManager;
157    private int mAudioState;
158    // Indicates whether audio can be routed to the device.
159    private boolean mAudioRouteAllowed;
160    private boolean mAudioWbs;
161    private final BluetoothAdapter mAdapter;
162    private boolean mNativeAvailable;
163    private TelecomManager mTelecomManager;
164
165    // currently connected device
166    private BluetoothDevice mCurrentDevice = null;
167
168    // general peer features and call handling features
169    private int mPeerFeatures;
170    private int mChldFeatures;
171
172    static {
173        classInitNative();
174    }
175
176    public void dump(StringBuilder sb) {
177        ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
178        ProfileService.println(sb, "mAudioOn: " + mAudioOn);
179        ProfileService.println(sb, "mAudioState: " + mAudioState);
180        ProfileService.println(sb, "mAudioWbs: " + mAudioWbs);
181        ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState);
182        ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType);
183        ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal);
184        ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel);
185        ProfileService.println(sb, "mIndicatorCall: " + mIndicatorCall);
186        ProfileService.println(sb, "mIndicatorCallSetup: " + mIndicatorCallSetup);
187        ProfileService.println(sb, "mIndicatorCallHeld: " + mIndicatorCallHeld);
188        ProfileService.println(sb, "mVgsFromStack: " + mVgsFromStack);
189        ProfileService.println(sb, "mVgmFromStack: " + mVgmFromStack);
190        ProfileService.println(sb, "mOperatorName: " + mOperatorName);
191        ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo);
192        ProfileService.println(sb, "mVoiceRecognitionActive: " + mVoiceRecognitionActive);
193        ProfileService.println(sb, "mInBandRingtone: " + mInBandRingtone);
194
195        ProfileService.println(sb, "mCalls:");
196        if (mCalls != null) {
197            for (BluetoothHeadsetClientCall call : mCalls.values()) {
198                ProfileService.println(sb, "  " + call);
199            }
200        }
201
202        ProfileService.println(sb, "mCallsUpdate:");
203        if (mCallsUpdate != null) {
204            for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) {
205                ProfileService.println(sb, "  " + call);
206            }
207        }
208    }
209
210    private void clearPendingAction() {
211        mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0);
212    }
213
214    private void addQueuedAction(int action) {
215        addQueuedAction(action, 0);
216    }
217
218    private void addQueuedAction(int action, Object data) {
219        mQueuedActions.add(new Pair<Integer, Object>(action, data));
220    }
221
222    private void addQueuedAction(int action, int data) {
223        mQueuedActions.add(new Pair<Integer, Object>(action, data));
224    }
225
226    private BluetoothHeadsetClientCall getCall(int... states) {
227        if (DBG) {
228            Log.d(TAG, "getFromCallsWithStates states:" + Arrays.toString(states));
229        }
230        for (BluetoothHeadsetClientCall c : mCalls.values()) {
231            for (int s : states) {
232                if (c.getState() == s) {
233                    return c;
234                }
235            }
236        }
237        return null;
238    }
239
240    private int callsInState(int state) {
241        int i = 0;
242        for (BluetoothHeadsetClientCall c : mCalls.values()) {
243            if (c.getState() == state) {
244                i++;
245            }
246        }
247
248        return i;
249    }
250
251    private void updateCallsMultiParty() {
252        boolean multi = callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) > 1;
253
254        for (BluetoothHeadsetClientCall c : mCalls.values()) {
255            if (c.getState() == BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) {
256                if (c.isMultiParty() == multi) {
257                    continue;
258                }
259
260                c.setMultiParty(multi);
261                sendCallChangedIntent(c);
262            } else {
263                if (c.isMultiParty()) {
264                    c.setMultiParty(false);
265                    sendCallChangedIntent(c);
266                }
267            }
268        }
269    }
270
271    private void setCallState(BluetoothHeadsetClientCall c, int state) {
272        if (state == c.getState()) {
273            return;
274        }
275        c.setState(state);
276        sendCallChangedIntent(c);
277    }
278
279    private void sendCallChangedIntent(BluetoothHeadsetClientCall c) {
280        if (DBG) {
281            Log.d(TAG, "sendCallChangedIntent " + c);
282        }
283        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
284        intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
285        mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
286    }
287
288    private void updateCallIndicator(int call) {
289        if (DBG) {
290            Log.d(TAG, "updateCallIndicator " + call);
291        }
292        mIndicatorCall = call;
293        sendMessage(QUERY_CURRENT_CALLS);
294    }
295
296    private void updateCallSetupIndicator(int callSetup) {
297        if (DBG) {
298            Log.d(TAG, "updateCallSetupIndicator " + callSetup + " " + mPendingAction.first);
299        }
300        mIndicatorCallSetup = callSetup;
301        sendMessage(QUERY_CURRENT_CALLS);
302    }
303
304    private void updateCallHeldIndicator(int callHeld) {
305        if (DBG) {
306            Log.d(TAG, "updateCallHeld " + callHeld);
307        }
308        mIndicatorCallHeld = callHeld;
309        sendMessage(QUERY_CURRENT_CALLS);
310    }
311
312    private void updateRespAndHold(int resp_and_hold) {
313        if (DBG) {
314            Log.d(TAG, "updatRespAndHold " + resp_and_hold);
315        }
316        sendMessage(QUERY_CURRENT_CALLS);
317    }
318
319    private void updateClip(String number) {
320        if (DBG) {
321            Log.d(TAG, "updateClip " + number);
322        }
323        sendMessage(QUERY_CURRENT_CALLS);
324    }
325
326    private void updateCCWA(String number) {
327        if (DBG) {
328            Log.d(TAG, "updateCCWA " + number);
329        }
330        sendMessage(QUERY_CURRENT_CALLS);
331    }
332
333    private boolean queryCallsStart() {
334        if (DBG) {
335            Log.d(TAG, "queryCallsStart");
336        }
337        clearPendingAction();
338        queryCurrentCallsNative();
339        addQueuedAction(QUERY_CURRENT_CALLS, 0);
340        return true;
341    }
342
343    private void queryCallsDone() {
344        if (DBG) {
345            Log.d(TAG, "queryCallsDone");
346        }
347        Iterator<Hashtable.Entry<Integer, BluetoothHeadsetClientCall>> it;
348
349        // mCalls has two types of calls:
350        // (a) Calls that are received from AG of a previous iteration of queryCallsStart()
351        // (b) Calls that are outgoing initiated from HF
352        // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of
353        // queryCallsStart().
354        //
355        // We use the following steps to make sure that calls are update correctly.
356        //
357        // If there are no calls initiated from HF (i.e. ID = -1) then:
358        // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are
359        // informed of the change calls (if any changes).
360        // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and
361        // the calls should be terminated
362        // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls.
363        //
364        // If there is an outgoing HF call, it is important to associate that call with one of the
365        // mCallsUpdated calls hence,
366        // 1. If from the above procedure we get N extra calls (i.e. {3}):
367        // choose the first call as the one to associate with the HF call.
368
369        // Create set of IDs for added calls, removed calls and consitent calls.
370        // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map
371        // itself (i.e. removing an element from Set removes it from the Map hence use copy).
372        Set<Integer> currCallIdSet = new HashSet<Integer>();
373        currCallIdSet.addAll(mCalls.keySet());
374        // Remove the entry for unassigned call.
375        currCallIdSet.remove(HF_ORIGINATED_CALL_ID);
376
377        Set<Integer> newCallIdSet = new HashSet<Integer>();
378        newCallIdSet.addAll(mCallsUpdate.keySet());
379
380        // Added.
381        Set<Integer> callAddedIds = new HashSet<Integer>();
382        callAddedIds.addAll(newCallIdSet);
383        callAddedIds.removeAll(currCallIdSet);
384
385        // Removed.
386        Set<Integer> callRemovedIds = new HashSet<Integer>();
387        callRemovedIds.addAll(currCallIdSet);
388        callRemovedIds.removeAll(newCallIdSet);
389
390        // Retained.
391        Set<Integer> callRetainedIds = new HashSet<Integer>();
392        callRetainedIds.addAll(currCallIdSet);
393        callRetainedIds.retainAll(newCallIdSet);
394
395        if (DBG) {
396            Log.d(TAG, "currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet +
397                " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds +
398                " callRetainedIds " + callRetainedIds);
399        }
400
401        // First thing is to try to associate the outgoing HF with a valid call.
402        Integer hfOriginatedAssoc = -1;
403        if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) {
404            BluetoothHeadsetClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID);
405            long cCreationElapsed = c.getCreationElapsedMilli();
406            if (callAddedIds.size() > 0) {
407                if (DBG) {
408                    Log.d(TAG, "Associating the first call with HF originated call");
409                }
410                hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0];
411                mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID));
412                mCalls.remove(HF_ORIGINATED_CALL_ID);
413
414                // Adjust this call in above sets.
415                callAddedIds.remove(hfOriginatedAssoc);
416                callRetainedIds.add(hfOriginatedAssoc);
417            } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) {
418                Log.w(TAG, "Outgoing call did not see a response, clear the calls and send CHUP");
419                terminateCall();
420
421                // Clean out the state.
422                for (Integer idx : mCalls.keySet()) {
423                    BluetoothHeadsetClientCall c1 = mCalls.get(idx);
424                    c1.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
425                    sendCallChangedIntent(c);
426                }
427                mCalls.clear();
428            }
429        }
430
431        if (DBG) {
432            Log.d(TAG, "ADJUST: currCallIdSet " + mCalls.keySet() + " newCallIdSet " +
433                newCallIdSet + " callAddedIds " + callAddedIds + " callRemovedIds " +
434                callRemovedIds + " callRetainedIds " + callRetainedIds);
435        }
436
437        // Terminate & remove the calls that are done.
438        for (Integer idx : callRemovedIds) {
439            BluetoothHeadsetClientCall c = mCalls.remove(idx);
440            c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
441            sendCallChangedIntent(c);
442        }
443
444        // Add the new calls.
445        for (Integer idx : callAddedIds) {
446            BluetoothHeadsetClientCall c = mCallsUpdate.get(idx);
447            mCalls.put(idx, c);
448            sendCallChangedIntent(c);
449        }
450
451        // Update the existing calls.
452        for (Integer idx : callRetainedIds) {
453            BluetoothHeadsetClientCall cOrig = mCalls.get(idx);
454            BluetoothHeadsetClientCall cUpdate = mCallsUpdate.get(idx);
455
456            // Update the necessary fields.
457            cOrig.setNumber(cUpdate.getNumber());
458            cOrig.setState(cUpdate.getState());
459            cOrig.setMultiParty(cUpdate.isMultiParty());
460
461            // Send update with original object (UUID, idx).
462            sendCallChangedIntent(cOrig);
463        }
464
465        if (loopQueryCalls()) {
466            sendMessageDelayed(QUERY_CURRENT_CALLS, 1523);
467        }
468
469        mCallsUpdate.clear();
470    }
471
472    private void queryCallsUpdate(int id, int state, String number, boolean multiParty,
473            boolean outgoing) {
474        if (DBG) {
475            Log.d(TAG, "queryCallsUpdate: " + id);
476        }
477        mCallsUpdate.put(id, new BluetoothHeadsetClientCall(mCurrentDevice, id, state, number,
478                multiParty, outgoing));
479    }
480
481    // helper function for determining if query calls should be looped
482    private boolean loopQueryCalls() {
483        if (DBG) {
484            Log.d(TAG, "loopQueryCalls, starting call query loop");
485        }
486        if (mCalls.size() > 0) {
487            return true;
488        }
489
490        // Workaround for Windows Phone 7.8 not sending callsetup=0 after
491        // rejecting incoming call in 3WC use case (when no active calls present).
492        // Fixes both, AG and HF rejecting the call.
493        BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING);
494        if (c != null && mIndicatorCallSetup == HeadsetClientHalConstants.CALLSETUP_NONE)
495            return true;
496
497        return false;
498    }
499
500    private void acceptCall(int flag, boolean retry) {
501        int action;
502
503        if (DBG) {
504            Log.d(TAG, "acceptCall: (" + flag + ")");
505        }
506
507        BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
508                BluetoothHeadsetClientCall.CALL_STATE_WAITING);
509        if (c == null) {
510            c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
511                    BluetoothHeadsetClientCall.CALL_STATE_HELD);
512
513            if (c == null) {
514                return;
515            }
516        }
517
518        switch (c.getState()) {
519            case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
520                if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
521                    return;
522                }
523
524                // Some NOKIA phones with Windows Phone 7.8 and MeeGo requires CHLD=1
525                // for accepting incoming call if it is the only call present after
526                // second active remote has disconnected (3WC scenario - call state
527                // changes from waiting to incoming). On the other hand some Android
528                // phones and iPhone requires ATA. Try to handle those gently by
529                // first issuing ATA. Failing means that AG is probably one of those
530                // phones that requires CHLD=1. Handle this case when we are retrying.
531                // Accepting incoming calls when there is held one and
532                // no active should also be handled by ATA.
533                action = HeadsetClientHalConstants.CALL_ACTION_ATA;
534
535                if (mCalls.size() == 1 && retry) {
536                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
537                }
538                break;
539            case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
540                if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) {
541                    // if no active calls present only plain accept is allowed
542                    if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
543                        return;
544                    }
545
546                    // Some phones (WP7) require ATA instead of CHLD=2
547                    // to accept waiting call if no active calls are present.
548                    if (retry) {
549                        action = HeadsetClientHalConstants.CALL_ACTION_ATA;
550                    } else {
551                        action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
552                    }
553                    break;
554                }
555
556                // if active calls are present action must be selected
557                if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
558                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
559                } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
560                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
561                } else {
562                    return;
563                }
564                break;
565            case BluetoothHeadsetClientCall.CALL_STATE_HELD:
566                if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
567                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
568                } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
569                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
570                } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) {
571                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3;
572                } else {
573                    action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
574                }
575                break;
576            case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
577                action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1;
578                break;
579            case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
580            case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
581            case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
582            default:
583                return;
584        }
585
586        if (handleCallActionNative(action, 0)) {
587            addQueuedAction(ACCEPT_CALL, action);
588        } else {
589            Log.e(TAG, "ERROR: Couldn't accept a call, action:" + action);
590        }
591    }
592
593    private void rejectCall() {
594        int action;
595
596        if (DBG) {
597            Log.d(TAG, "rejectCall");
598        }
599
600        BluetoothHeadsetClientCall c =
601                getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
602                BluetoothHeadsetClientCall.CALL_STATE_WAITING,
603                BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
604                BluetoothHeadsetClientCall.CALL_STATE_HELD);
605        if (c == null) {
606            return;
607        }
608
609        switch (c.getState()) {
610            case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
611                action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
612                break;
613            case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
614            case BluetoothHeadsetClientCall.CALL_STATE_HELD:
615                action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
616                break;
617            case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
618                action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2;
619                break;
620            case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
621            case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
622            case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
623            default:
624                return;
625        }
626
627        if (handleCallActionNative(action, 0)) {
628            addQueuedAction(REJECT_CALL, action);
629        } else {
630            Log.e(TAG, "ERROR: Couldn't reject a call, action:" + action);
631        }
632    }
633
634    private void holdCall() {
635        int action;
636
637        if (DBG) {
638            Log.d(TAG, "holdCall");
639        }
640
641        BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING);
642        if (c != null) {
643            action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0;
644        } else {
645            c = getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
646            if (c == null) {
647                return;
648            }
649
650            action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
651        }
652
653        if (handleCallActionNative(action, 0)) {
654            addQueuedAction(HOLD_CALL, action);
655        } else {
656            Log.e(TAG, "ERROR: Couldn't hold a call, action:" + action);
657        }
658    }
659
660    private void terminateCall() {
661        if (DBG) {
662            Log.d(TAG, "terminateCall");
663        }
664
665        int action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
666
667        BluetoothHeadsetClientCall c = getCall(
668                BluetoothHeadsetClientCall.CALL_STATE_DIALING,
669                BluetoothHeadsetClientCall.CALL_STATE_ALERTING,
670                BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
671        if (c != null) {
672            if (handleCallActionNative(action, 0)) {
673                addQueuedAction(TERMINATE_CALL, action);
674            } else {
675                Log.e(TAG, "ERROR: Couldn't terminate outgoing call");
676            }
677        }
678    }
679
680    private void enterPrivateMode(int idx) {
681        if (DBG) {
682            Log.d(TAG, "enterPrivateMode: " + idx);
683        }
684
685        BluetoothHeadsetClientCall c = mCalls.get(idx);
686
687        if (c == null) {
688            return;
689        }
690
691        if (c.getState() != BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) {
692            return;
693        }
694
695        if (!c.isMultiParty()) {
696            return;
697        }
698
699        if (handleCallActionNative(HeadsetClientHalConstants.CALL_ACTION_CHLD_2x, idx)) {
700            addQueuedAction(ENTER_PRIVATE_MODE, c);
701        } else {
702            Log.e(TAG, "ERROR: Couldn't enter private " + " id:" + idx);
703        }
704    }
705
706    private void explicitCallTransfer() {
707        if (DBG) {
708            Log.d(TAG, "explicitCallTransfer");
709        }
710
711        // can't transfer call if there is not enough call parties
712        if (mCalls.size() < 2) {
713            return;
714        }
715
716        if (handleCallActionNative(HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) {
717            addQueuedAction(EXPLICIT_CALL_TRANSFER);
718        } else {
719            Log.e(TAG, "ERROR: Couldn't transfer call");
720        }
721    }
722
723    public Bundle getCurrentAgFeatures()
724    {
725        Bundle b = new Bundle();
726        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) ==
727                HeadsetClientHalConstants.PEER_FEAT_3WAY) {
728            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
729        }
730        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) ==
731                HeadsetClientHalConstants.PEER_FEAT_VREC) {
732            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
733        }
734        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VTAG) ==
735                HeadsetClientHalConstants.PEER_FEAT_VTAG) {
736            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT, true);
737        }
738        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) ==
739                HeadsetClientHalConstants.PEER_FEAT_REJECT) {
740            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
741        }
742        if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) ==
743                HeadsetClientHalConstants.PEER_FEAT_ECC) {
744            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
745        }
746
747        // add individual CHLD support extras
748        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) ==
749                HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
750            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
751        }
752        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) ==
753                HeadsetClientHalConstants.CHLD_FEAT_REL) {
754            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
755        }
756        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) ==
757                HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
758            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
759        }
760        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) ==
761                HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
762            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
763        }
764        if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) ==
765                HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
766            b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
767        }
768
769        return b;
770    }
771
772    private HeadsetClientStateMachine(HeadsetClientService context) {
773        super(TAG);
774        mService = context;
775
776        mAdapter = BluetoothAdapter.getDefaultAdapter();
777        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
778        mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
779        mAudioWbs = false;
780
781        mAudioRouteAllowed = context.getResources().getBoolean(
782                R.bool.headset_client_initial_audio_route_allowed);
783
784        mTelecomManager = (TelecomManager) context.getSystemService(context.TELECOM_SERVICE);
785
786        mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
787        mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
788        mIndicatorNetworkSignal = 0;
789        mIndicatorBatteryLevel = 0;
790
791        // all will be set on connected
792        mIndicatorCall = -1;
793        mIndicatorCallSetup = -1;
794        mIndicatorCallHeld = -1;
795
796        mMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
797        mMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
798
799        mOperatorName = null;
800        mSubscriberInfo = null;
801
802        mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
803        mInBandRingtone = HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED;
804
805        mQueuedActions = new LinkedList<Pair<Integer, Object>>();
806        clearPendingAction();
807
808        mCalls.clear();
809        mCallsUpdate.clear();
810
811        initializeNative();
812        mNativeAvailable = true;
813
814        mDisconnected = new Disconnected();
815        mConnecting = new Connecting();
816        mConnected = new Connected();
817        mAudioOn = new AudioOn();
818
819        addState(mDisconnected);
820        addState(mConnecting);
821        addState(mConnected);
822        addState(mAudioOn, mConnected);
823
824        setInitialState(mDisconnected);
825    }
826
827    static HeadsetClientStateMachine make(HeadsetClientService context) {
828        if (DBG) {
829            Log.d(TAG, "make");
830        }
831        HeadsetClientStateMachine hfcsm = new HeadsetClientStateMachine(context);
832        hfcsm.start();
833        return hfcsm;
834    }
835
836    public void doQuit() {
837        quitNow();
838    }
839
840    public void cleanup() {
841        if (mNativeAvailable) {
842            cleanupNative();
843            mNativeAvailable = false;
844        }
845    }
846
847    private int hfToAmVol(int hfVol) {
848        int amRange = mMaxAmVcVol - mMinAmVcVol;
849        int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
850        int amOffset =
851            (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange;
852        int amVol = mMinAmVcVol + amOffset;
853        Log.d(TAG, "HF -> AM " + hfVol + " " + amVol);
854        return amVol;
855    }
856
857    private int amToHfVol(int amVol) {
858        int amRange = mMaxAmVcVol - mMinAmVcVol;
859        int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
860        int hfOffset = (hfRange * (amVol - mMinAmVcVol)) / amRange;
861        int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset;
862        Log.d(TAG, "AM -> HF " + amVol + " " + hfVol);
863        return hfVol;
864    }
865
866    private class Disconnected extends State {
867        @Override
868        public void enter() {
869            Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what);
870
871            // cleanup
872            mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
873            mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
874            mIndicatorNetworkSignal = 0;
875            mIndicatorBatteryLevel = 0;
876
877            mAudioWbs = false;
878
879            // will be set on connect
880            mIndicatorCall = -1;
881            mIndicatorCallSetup = -1;
882            mIndicatorCallHeld = -1;
883
884            mOperatorName = null;
885            mSubscriberInfo = null;
886
887            mQueuedActions = new LinkedList<Pair<Integer, Object>>();
888            clearPendingAction();
889
890            mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
891            mInBandRingtone = HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED;
892
893            mCalls.clear();
894            mCallsUpdate.clear();
895
896            mPeerFeatures = 0;
897            mChldFeatures = 0;
898
899            removeMessages(QUERY_CURRENT_CALLS);
900        }
901
902        @Override
903        public synchronized boolean processMessage(Message message) {
904            Log.d(TAG, "Disconnected process message: " + message.what);
905
906            if (mCurrentDevice != null) {
907                Log.e(TAG, "ERROR: current device not null in Disconnected");
908                return NOT_HANDLED;
909            }
910
911            switch (message.what) {
912                case CONNECT:
913                    BluetoothDevice device = (BluetoothDevice) message.obj;
914
915                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
916                            BluetoothProfile.STATE_DISCONNECTED);
917
918                    if (!connectNative(getByteAddress(device))) {
919                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
920                                BluetoothProfile.STATE_CONNECTING);
921                        break;
922                    }
923
924                    mCurrentDevice = device;
925                    transitionTo(mConnecting);
926                    break;
927                case DISCONNECT:
928                    // ignore
929                    break;
930                case STACK_EVENT:
931                    StackEvent event = (StackEvent) message.obj;
932                    if (DBG) {
933                        Log.d(TAG, "Stack event type: " + event.type);
934                    }
935                    switch (event.type) {
936                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
937                            if (DBG) {
938                                Log.d(TAG, "Disconnected: Connection " + event.device
939                                        + " state changed:" + event.valueInt);
940                            }
941                            processConnectionEvent(event.valueInt, event.device);
942                            break;
943                        default:
944                            Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type);
945                            break;
946                    }
947                    break;
948                default:
949                    return NOT_HANDLED;
950            }
951            return HANDLED;
952        }
953
954        // in Disconnected state
955        private void processConnectionEvent(int state, BluetoothDevice device)
956        {
957            switch (state) {
958                case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
959                    Log.w(TAG, "HFPClient Connecting from Disconnected state");
960                    if (okToConnect(device)) {
961                        Log.i(TAG, "Incoming AG accepted");
962                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
963                                BluetoothProfile.STATE_DISCONNECTED);
964                        mCurrentDevice = device;
965                        transitionTo(mConnecting);
966                    } else {
967                        Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device)
968                                +
969                                " bondState=" + device.getBondState());
970                        // reject the connection and stay in Disconnected state
971                        // itself
972                        disconnectNative(getByteAddress(device));
973                        // the other profile connection should be initiated
974                        AdapterService adapterService = AdapterService.getAdapterService();
975                        if (adapterService != null) {
976                            adapterService.connectOtherProfile(device,
977                                    AdapterService.PROFILE_CONN_REJECTED);
978                        }
979                    }
980                    break;
981                case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
982                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
983                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
984                default:
985                    Log.i(TAG, "ignoring state: " + state);
986                    break;
987            }
988        }
989
990        @Override
991        public void exit() {
992            if (DBG) {
993                Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what);
994            }
995        }
996    }
997
998    private class Connecting extends State {
999        @Override
1000        public void enter() {
1001            if (DBG) {
1002                Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what);
1003            }
1004        }
1005
1006        @Override
1007        public synchronized boolean processMessage(Message message) {
1008            if (DBG) {
1009                Log.d(TAG, "Connecting process message: " + message.what);
1010            }
1011
1012            boolean retValue = HANDLED;
1013            switch (message.what) {
1014                case CONNECT:
1015                case CONNECT_AUDIO:
1016                case DISCONNECT:
1017                    deferMessage(message);
1018                    break;
1019                case STACK_EVENT:
1020                    StackEvent event = (StackEvent) message.obj;
1021                    if (DBG) {
1022                        Log.d(TAG, "Connecting: event type: " + event.type);
1023                    }
1024                    switch (event.type) {
1025                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1026                            if (DBG) {
1027                                Log.d(TAG, "Connecting: Connection " + event.device + " state changed:"
1028                                        + event.valueInt);
1029                            }
1030                            processConnectionEvent(event.valueInt, event.valueInt2,
1031                                    event.valueInt3, event.device);
1032                            break;
1033                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1034                        case EVENT_TYPE_VR_STATE_CHANGED:
1035                        case EVENT_TYPE_NETWORK_STATE:
1036                        case EVENT_TYPE_ROAMING_STATE:
1037                        case EVENT_TYPE_NETWORK_SIGNAL:
1038                        case EVENT_TYPE_BATTERY_LEVEL:
1039                        case EVENT_TYPE_CALL:
1040                        case EVENT_TYPE_CALLSETUP:
1041                        case EVENT_TYPE_CALLHELD:
1042                        case EVENT_TYPE_RESP_AND_HOLD:
1043                        case EVENT_TYPE_CLIP:
1044                        case EVENT_TYPE_CALL_WAITING:
1045                        case EVENT_TYPE_VOLUME_CHANGED:
1046                        case EVENT_TYPE_IN_BAND_RING:
1047                            deferMessage(message);
1048                            break;
1049                        case EVENT_TYPE_CMD_RESULT:
1050                        case EVENT_TYPE_SUBSCRIBER_INFO:
1051                        case EVENT_TYPE_CURRENT_CALLS:
1052                        case EVENT_TYPE_OPERATOR_NAME:
1053                        default:
1054                            Log.e(TAG, "Connecting: ignoring stack event: " + event.type);
1055                            break;
1056                    }
1057                    break;
1058                default:
1059                    return NOT_HANDLED;
1060            }
1061            return retValue;
1062        }
1063
1064        // in Connecting state
1065        private void processConnectionEvent(
1066                int state, int peer_feat, int chld_feat, BluetoothDevice device) {
1067            switch (state) {
1068                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1069                    broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
1070                            BluetoothProfile.STATE_CONNECTING);
1071                    mCurrentDevice = null;
1072                    transitionTo(mDisconnected);
1073                    break;
1074
1075                case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1076                    Log.w(TAG, "HFPClient Connected from Connecting state");
1077
1078                    mPeerFeatures = peer_feat;
1079                    mChldFeatures = chld_feat;
1080
1081                    // We do not support devices which do not support enhanced call status (ECS).
1082                    if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) {
1083                        disconnectNative(getByteAddress(device));
1084                        return;
1085                    }
1086
1087                    broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1088                        BluetoothProfile.STATE_CONNECTING);
1089
1090                    // Send AT+NREC to remote if supported by audio
1091                    if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED &&
1092                            ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR) ==
1093                                    HeadsetClientHalConstants.PEER_FEAT_ECNR)) {
1094                        if (sendATCmdNative(HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC,
1095                            1 , 0, null)) {
1096                            addQueuedAction(DISABLE_NREC);
1097                        } else {
1098                            Log.e(TAG, "Failed to send NREC");
1099                        }
1100                    }
1101                    transitionTo(mConnected);
1102
1103                    int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1104                    sendMessage(
1105                            obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
1106                    // Mic is either in ON state (full volume) or OFF state. There is no way in
1107                    // Android to change the MIC volume.
1108                    sendMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME,
1109                            mAudioManager.isMicrophoneMute() ? 0 : 15, 0));
1110
1111                    // query subscriber info
1112                    sendMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO);
1113                    break;
1114
1115                case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
1116                    if (!mCurrentDevice.equals(device)) {
1117                        Log.w(TAG, "incoming connection event, device: " + device);
1118
1119                        broadcastConnectionState(mCurrentDevice,
1120                                BluetoothProfile.STATE_DISCONNECTED,
1121                                BluetoothProfile.STATE_CONNECTING);
1122                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1123                                BluetoothProfile.STATE_DISCONNECTED);
1124
1125                        mCurrentDevice = device;
1126                    }
1127                    break;
1128                case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
1129                    /* outgoing connecting started */
1130                    if (DBG) {
1131                        Log.d(TAG, "outgoing connection started, ignore");
1132                    }
1133                    break;
1134                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
1135                default:
1136                    Log.e(TAG, "Incorrect state: " + state);
1137                    break;
1138            }
1139        }
1140
1141        @Override
1142        public void exit() {
1143            if (DBG) {
1144                Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what);
1145            }
1146        }
1147    }
1148
1149    private class Connected extends State {
1150        @Override
1151        public void enter() {
1152            if (DBG) {
1153                Log.d(TAG, "Enter Connected: " + getCurrentMessage().what);
1154            }
1155
1156            mAudioWbs = false;
1157        }
1158
1159        @Override
1160        public synchronized boolean processMessage(Message message) {
1161            if (DBG) {
1162                Log.d(TAG, "Connected process message: " + message.what);
1163            }
1164            if (DBG) {
1165                if (mCurrentDevice == null) {
1166                    Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
1167                    return NOT_HANDLED;
1168                }
1169            }
1170
1171            switch (message.what) {
1172                case CONNECT:
1173                    BluetoothDevice device = (BluetoothDevice) message.obj;
1174                    if (mCurrentDevice.equals(device)) {
1175                        // already connected to this device, do nothing
1176                        break;
1177                    }
1178
1179                    if (!disconnectNative(getByteAddress(mCurrentDevice))) {
1180                        // if succeed this will be handled from disconnected
1181                        // state
1182                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1183                                BluetoothProfile.STATE_DISCONNECTED);
1184                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1185                                BluetoothProfile.STATE_CONNECTING);
1186                        break;
1187                    }
1188
1189                    // will be handled when entered disconnected
1190                    deferMessage(message);
1191                    break;
1192                case DISCONNECT:
1193                    BluetoothDevice dev = (BluetoothDevice) message.obj;
1194                    if (!mCurrentDevice.equals(dev)) {
1195                        break;
1196                    }
1197                    broadcastConnectionState(dev, BluetoothProfile.STATE_DISCONNECTING,
1198                            BluetoothProfile.STATE_CONNECTED);
1199                    if (!disconnectNative(getByteAddress(dev))) {
1200                        // disconnecting failed
1201                        broadcastConnectionState(dev, BluetoothProfile.STATE_CONNECTED,
1202                                BluetoothProfile.STATE_DISCONNECTED);
1203                        break;
1204                    }
1205                    break;
1206                case CONNECT_AUDIO:
1207                    // TODO: handle audio connection failure
1208                    if (!connectAudioNative(getByteAddress(mCurrentDevice))) {
1209                        Log.e(TAG, "ERROR: Couldn't connect Audio.");
1210                    }
1211                    break;
1212                case DISCONNECT_AUDIO:
1213                    // TODO: handle audio disconnection failure
1214                    if (!disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1215                        Log.e(TAG, "ERROR: Couldn't connect Audio.");
1216                    }
1217                    break;
1218                case VOICE_RECOGNITION_START:
1219                    if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) {
1220                        if (startVoiceRecognitionNative()) {
1221                            addQueuedAction(VOICE_RECOGNITION_START);
1222                        } else {
1223                            Log.e(TAG, "ERROR: Couldn't start voice recognition");
1224                        }
1225                    }
1226                    break;
1227                case VOICE_RECOGNITION_STOP:
1228                    if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) {
1229                        if (stopVoiceRecognitionNative()) {
1230                            addQueuedAction(VOICE_RECOGNITION_STOP);
1231                        } else {
1232                            Log.e(TAG, "ERROR: Couldn't stop voice recognition");
1233                        }
1234                    }
1235                    break;
1236                // Called only for Mute/Un-mute - Mic volume change is not allowed.
1237                case SET_MIC_VOLUME:
1238                    if (mVgmFromStack) {
1239                        mVgmFromStack = false;
1240                        break;
1241                    }
1242                    if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_MIC, message.arg1)) {
1243                        addQueuedAction(SET_MIC_VOLUME);
1244                    }
1245                    break;
1246                case SET_SPEAKER_VOLUME:
1247                    // This message should always contain the volume in AudioManager max normalized.
1248                    int amVol = message.arg1;
1249                    int hfVol = amToHfVol(amVol);
1250                    Log.d(TAG,"HF volume is set to " + hfVol);
1251                    mAudioManager.setParameters("hfp_volume=" + hfVol);
1252                    if (mVgsFromStack) {
1253                        mVgsFromStack = false;
1254                        break;
1255                    }
1256                    if (setVolumeNative(HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) {
1257                        addQueuedAction(SET_SPEAKER_VOLUME);
1258                    }
1259                    break;
1260                case DIAL_NUMBER:
1261                    // Add the call as an outgoing call.
1262                    BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj;
1263                    mCalls.put(HF_ORIGINATED_CALL_ID, c);
1264
1265                    if (dialNative(c.getNumber())) {
1266                        addQueuedAction(DIAL_NUMBER, c.getNumber());
1267                        // Start looping on calling current calls.
1268                        sendMessage(QUERY_CURRENT_CALLS);
1269                    } else {
1270                        Log.e(TAG, "ERROR: Cannot dial with a given number:" + (String) message.obj);
1271                        // Set the call to terminated remove.
1272                        c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
1273                        sendCallChangedIntent(c);
1274                        mCalls.remove(HF_ORIGINATED_CALL_ID);
1275                    }
1276                    break;
1277                case ACCEPT_CALL:
1278                    acceptCall(message.arg1, false);
1279                    break;
1280                case REJECT_CALL:
1281                    rejectCall();
1282                    break;
1283                case HOLD_CALL:
1284                    holdCall();
1285                    break;
1286                case TERMINATE_CALL:
1287                    terminateCall();
1288                    break;
1289                case ENTER_PRIVATE_MODE:
1290                    enterPrivateMode(message.arg1);
1291                    break;
1292                case EXPLICIT_CALL_TRANSFER:
1293                    explicitCallTransfer();
1294                    break;
1295                case SEND_DTMF:
1296                    if (sendDtmfNative((byte) message.arg1)) {
1297                        addQueuedAction(SEND_DTMF);
1298                    } else {
1299                        Log.e(TAG, "ERROR: Couldn't send DTMF");
1300                    }
1301                    break;
1302                case SUBSCRIBER_INFO:
1303                    if (retrieveSubscriberInfoNative()) {
1304                        addQueuedAction(SUBSCRIBER_INFO);
1305                    } else {
1306                        Log.e(TAG, "ERROR: Couldn't retrieve subscriber info");
1307                    }
1308                    break;
1309                case LAST_VTAG_NUMBER:
1310                    if (requestLastVoiceTagNumberNative()) {
1311                        addQueuedAction(LAST_VTAG_NUMBER);
1312                    } else {
1313                        Log.e(TAG, "ERROR: Couldn't get last VTAG number");
1314                    }
1315                    break;
1316                case QUERY_CURRENT_CALLS:
1317                    queryCallsStart();
1318                    break;
1319                case STACK_EVENT:
1320                    Intent intent = null;
1321                    StackEvent event = (StackEvent) message.obj;
1322                    if (DBG) {
1323                        Log.d(TAG, "Connected: event type: " + event.type);
1324                    }
1325
1326                    switch (event.type) {
1327                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1328                            if (DBG) {
1329                                Log.d(TAG, "Connected: Connection state changed: " + event.device
1330                                        + ": " + event.valueInt);
1331                            }
1332                            processConnectionEvent(event.valueInt, event.device);
1333                            break;
1334                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1335                            if (DBG) {
1336                                Log.d(TAG, "Connected: Audio state changed: " + event.device + ": "
1337                                        + event.valueInt);
1338                            }
1339                            processAudioEvent(event.valueInt, event.device);
1340                            break;
1341                        case EVENT_TYPE_NETWORK_STATE:
1342                            if (DBG) {
1343                                Log.d(TAG, "Connected: Network state: " + event.valueInt);
1344                            }
1345                            mIndicatorNetworkState = event.valueInt;
1346
1347                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1348                            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS,
1349                                    event.valueInt);
1350
1351                            if (mIndicatorNetworkState ==
1352                                    HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
1353                                mOperatorName = null;
1354                                intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
1355                                        mOperatorName);
1356                            }
1357
1358                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1359                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1360
1361                            if (mIndicatorNetworkState ==
1362                                    HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) {
1363                                if (queryCurrentOperatorNameNative()) {
1364                                    addQueuedAction(QUERY_OPERATOR_NAME);
1365                                } else {
1366                                    Log.e(TAG, "ERROR: Couldn't querry operator name");
1367                                }
1368                            }
1369                            break;
1370                        case EVENT_TYPE_ROAMING_STATE:
1371                            mIndicatorNetworkType = event.valueInt;
1372
1373                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1374                            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING,
1375                                    event.valueInt);
1376                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1377                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1378                            break;
1379                        case EVENT_TYPE_NETWORK_SIGNAL:
1380                            mIndicatorNetworkSignal = event.valueInt;
1381
1382                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1383                            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH,
1384                                    event.valueInt);
1385                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1386                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1387                            break;
1388                        case EVENT_TYPE_BATTERY_LEVEL:
1389                            mIndicatorBatteryLevel = event.valueInt;
1390
1391                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1392                            intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
1393                                    event.valueInt);
1394                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1395                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1396                            break;
1397                        case EVENT_TYPE_OPERATOR_NAME:
1398                            mOperatorName = event.valueString;
1399
1400                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1401                            intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
1402                                    event.valueString);
1403                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1404                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1405                            break;
1406                        case EVENT_TYPE_VR_STATE_CHANGED:
1407                            if (mVoiceRecognitionActive != event.valueInt) {
1408                                mVoiceRecognitionActive = event.valueInt;
1409
1410                                intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1411                                intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION,
1412                                        mVoiceRecognitionActive);
1413                                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1414                                mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1415                            }
1416                            break;
1417                        case EVENT_TYPE_CALL:
1418                            updateCallIndicator(event.valueInt);
1419                            break;
1420                        case EVENT_TYPE_CALLSETUP:
1421                            updateCallSetupIndicator(event.valueInt);
1422                            break;
1423                        case EVENT_TYPE_CALLHELD:
1424                            updateCallHeldIndicator(event.valueInt);
1425                            break;
1426                        case EVENT_TYPE_RESP_AND_HOLD:
1427                            updateRespAndHold(event.valueInt);
1428                            break;
1429                        case EVENT_TYPE_CLIP:
1430                            updateClip(event.valueString);
1431                            break;
1432                        case EVENT_TYPE_CALL_WAITING:
1433                            updateCCWA(event.valueString);
1434                            break;
1435                        case EVENT_TYPE_IN_BAND_RING:
1436                            if (mInBandRingtone != event.valueInt) {
1437                                mInBandRingtone = event.valueInt;
1438                                intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1439                                intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING,
1440                                        mInBandRingtone);
1441                                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1442                                mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1443                            }
1444                            break;
1445                        case EVENT_TYPE_CURRENT_CALLS:
1446                            queryCallsUpdate(
1447                                    event.valueInt,
1448                                    event.valueInt3,
1449                                    event.valueString,
1450                                    event.valueInt4 ==
1451                                            HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI,
1452                                    event.valueInt2 ==
1453                                            HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING);
1454                            break;
1455                        case EVENT_TYPE_VOLUME_CHANGED:
1456                            if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
1457                                Log.d(TAG, "AM volume set to " +
1458                                      hfToAmVol(event.valueInt2));
1459                                mAudioManager.setStreamVolume(
1460                                    AudioManager.STREAM_VOICE_CALL,
1461                                    hfToAmVol(event.valueInt2),
1462                                    AudioManager.FLAG_SHOW_UI);
1463                                mVgsFromStack = true;
1464                            } else if (event.valueInt ==
1465                                HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
1466                                mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
1467
1468                                mVgmFromStack = true;
1469                            }
1470                            break;
1471                        case EVENT_TYPE_CMD_RESULT:
1472                            Pair<Integer, Object> queuedAction = mQueuedActions.poll();
1473
1474                            // should not happen but...
1475                            if (queuedAction == null || queuedAction.first == NO_ACTION) {
1476                                clearPendingAction();
1477                                break;
1478                            }
1479
1480                            if (DBG) {
1481                                Log.d(TAG, "Connected: command result: " + event.valueInt
1482                                        + " queuedAction: " + queuedAction.first);
1483                            }
1484
1485                            switch (queuedAction.first) {
1486                                case VOICE_RECOGNITION_STOP:
1487                                case VOICE_RECOGNITION_START:
1488                                    if (event.valueInt == HeadsetClientHalConstants.CMD_COMPLETE_OK) {
1489                                        if (queuedAction.first == VOICE_RECOGNITION_STOP) {
1490                                            mVoiceRecognitionActive =
1491                                                    HeadsetClientHalConstants.VR_STATE_STOPPED;
1492                                        } else {
1493                                            mVoiceRecognitionActive =
1494                                                    HeadsetClientHalConstants.VR_STATE_STARTED;
1495                                        }
1496                                    }
1497                                    intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1498                                    intent.putExtra(
1499                                            BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION,
1500                                            mVoiceRecognitionActive);
1501                                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1502                                    mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1503                                    break;
1504                                case QUERY_CURRENT_CALLS:
1505                                    queryCallsDone();
1506                                    break;
1507                                case ACCEPT_CALL:
1508                                    if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_OK) {
1509                                        mPendingAction = queuedAction;
1510                                    } else {
1511                                        if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) {
1512                                            if(getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null &&
1513                                                (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_ATA) {
1514                                                acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true);
1515                                                break;
1516                                            } else if(getCall(BluetoothHeadsetClientCall.CALL_STATE_WAITING) != null &&
1517                                                     (Integer) mPendingAction.second == HeadsetClientHalConstants.CALL_ACTION_CHLD_2) {
1518                                                acceptCall(BluetoothHeadsetClient.CALL_ACCEPT_NONE, true);
1519                                                break;
1520                                            }
1521                                        }
1522                                        sendActionResultIntent(event);
1523                                    }
1524                                    break;
1525                                case DIAL_NUMBER:
1526                                case REJECT_CALL:
1527                                case HOLD_CALL:
1528                                case TERMINATE_CALL:
1529                                case ENTER_PRIVATE_MODE:
1530                                case TERMINATE_SPECIFIC_CALL:
1531                                    // if terminating specific succeed no other
1532                                    // event is send
1533                                    if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_OK) {
1534                                        BluetoothHeadsetClientCall sc =
1535                                                (BluetoothHeadsetClientCall) queuedAction.second;
1536                                        setCallState(sc,
1537                                                BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
1538                                        mCalls.remove(sc.getId());
1539                                    } else {
1540                                        sendActionResultIntent(event);
1541                                    }
1542                                    break;
1543                                case LAST_VTAG_NUMBER:
1544                                    if (event.valueInt != BluetoothHeadsetClient.ACTION_RESULT_OK) {
1545                                        sendActionResultIntent(event);
1546                                    }
1547                                    break;
1548                                case DISABLE_NREC:
1549                                    if (event.valueInt != HeadsetClientHalConstants.CMD_COMPLETE_OK) {
1550                                        Log.w(TAG, "Failed to disable AG's EC and NR");
1551                                    }
1552                                    break;
1553                                case SET_MIC_VOLUME:
1554                                case SET_SPEAKER_VOLUME:
1555                                case SUBSCRIBER_INFO:
1556                                case QUERY_OPERATOR_NAME:
1557                                    break;
1558                                default:
1559                                    sendActionResultIntent(event);
1560                                    break;
1561                            }
1562
1563                            break;
1564                        case EVENT_TYPE_SUBSCRIBER_INFO:
1565                            /* TODO should we handle type as well? */
1566                            mSubscriberInfo = event.valueString;
1567                            intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
1568                            intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO,
1569                                    mSubscriberInfo);
1570                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1571                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1572                            break;
1573                        case EVENT_TYPE_LAST_VOICE_TAG_NUMBER:
1574                            intent = new Intent(BluetoothHeadsetClient.ACTION_LAST_VTAG);
1575                            intent.putExtra(BluetoothHeadsetClient.EXTRA_NUMBER,
1576                                    event.valueString);
1577                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1578                            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1579                            break;
1580                        case EVENT_TYPE_RING_INDICATION:
1581                            // Ringing is not handled at this indication and rather should be
1582                            // implemented (by the client of this service). Use the
1583                            // CALL_STATE_INCOMING (and similar) handle ringing.
1584                            break;
1585                        default:
1586                            Log.e(TAG, "Unknown stack event: " + event.type);
1587                            break;
1588                    }
1589
1590                    break;
1591                default:
1592                    return NOT_HANDLED;
1593            }
1594            return HANDLED;
1595        }
1596
1597        private void sendActionResultIntent(StackEvent event) {
1598            Intent intent = new Intent(BluetoothHeadsetClient.ACTION_RESULT);
1599            intent.putExtra(BluetoothHeadsetClient.EXTRA_RESULT_CODE, event.valueInt);
1600            if (event.valueInt == BluetoothHeadsetClient.ACTION_RESULT_ERROR_CME) {
1601                intent.putExtra(BluetoothHeadsetClient.EXTRA_CME_CODE, event.valueInt2);
1602            }
1603            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
1604            mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1605        }
1606
1607        // in Connected state
1608        private void processConnectionEvent(int state, BluetoothDevice device) {
1609            switch (state) {
1610                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1611                    if (DBG) {
1612                        Log.d(TAG, "Connected disconnects.");
1613                    }
1614                    // AG disconnects
1615                    if (mCurrentDevice.equals(device)) {
1616                        broadcastConnectionState(mCurrentDevice,
1617                                BluetoothProfile.STATE_DISCONNECTED,
1618                                BluetoothProfile.STATE_CONNECTED);
1619                        mCurrentDevice = null;
1620                        transitionTo(mDisconnected);
1621                    } else {
1622                        Log.e(TAG, "Disconnected from unknown device: " + device);
1623                    }
1624                    break;
1625                default:
1626                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1627                    break;
1628            }
1629        }
1630
1631        // in Connected state
1632        private void processAudioEvent(int state, BluetoothDevice device) {
1633            // message from old device
1634            if (!mCurrentDevice.equals(device)) {
1635                Log.e(TAG, "Audio changed on disconnected device: " + device);
1636                return;
1637            }
1638
1639            switch (state) {
1640                case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC:
1641                    mAudioWbs = true;
1642                    // fall through
1643                case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED:
1644                    if (!mAudioRouteAllowed) {
1645                        sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
1646                        break;
1647                    }
1648
1649                    // Audio state is split in two parts, the audio focus is maintained by the
1650                    // entity exercising this service (typically the Telecom stack) and audio
1651                    // routing is handled by the bluetooth stack itself. The only reason to do so is
1652                    // because Bluetooth SCO connection from the HF role is not entirely supported
1653                    // for routing and volume purposes.
1654                    // NOTE: All calls here are routed via the setParameters which changes the
1655                    // routing at the Audio HAL level.
1656                    mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
1657
1658                    // We need to set the volume after switching into HFP mode as some Audio HALs
1659                    // reset the volume to a known-default on mode switch.
1660                    final int amVol =
1661                            mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
1662                    final int hfVol = amToHfVol(amVol);
1663
1664                    if (DBG) {
1665                        Log.d(TAG,"hfp_enable=true mAudioWbs is " + mAudioWbs);
1666                    }
1667                    if (mAudioWbs) {
1668                        if (DBG) {
1669                            Log.d(TAG,"Setting sampling rate as 16000");
1670                        }
1671                        mAudioManager.setParameters("hfp_set_sampling_rate=16000");
1672                    }
1673                    else {
1674                        if (DBG) {
1675                            Log.d(TAG,"Setting sampling rate as 8000");
1676                        }
1677                        mAudioManager.setParameters("hfp_set_sampling_rate=8000");
1678                    }
1679                    if (DBG) {
1680                        Log.d(TAG, "hf_volume " + hfVol);
1681                    }
1682                    mAudioManager.setParameters("hfp_enable=true");
1683                    mAudioManager.setParameters("hfp_volume=" + hfVol);
1684                    transitionTo(mAudioOn);
1685                    break;
1686                case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING:
1687                    mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
1688                    broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING,
1689                            BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
1690                    break;
1691                case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
1692                    if (mAudioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTING) {
1693                        mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1694                        broadcastAudioState(device,
1695                                BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1696                                BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
1697                    }
1698                    break;
1699                default:
1700                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1701                    break;
1702            }
1703        }
1704
1705        @Override
1706        public void exit() {
1707            if (DBG) {
1708                Log.d(TAG, "Exit Connected: " + getCurrentMessage().what);
1709            }
1710        }
1711    }
1712
1713    private class AudioOn extends State {
1714        @Override
1715        public void enter() {
1716            if (DBG) {
1717                Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what);
1718            }
1719            broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
1720                BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
1721        }
1722
1723        @Override
1724        public synchronized boolean processMessage(Message message) {
1725            if (DBG) {
1726                Log.d(TAG, "AudioOn process message: " + message.what);
1727            }
1728            if (DBG) {
1729                if (mCurrentDevice == null) {
1730                    Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
1731                    return NOT_HANDLED;
1732                }
1733            }
1734
1735            switch (message.what) {
1736                case DISCONNECT:
1737                    BluetoothDevice device = (BluetoothDevice) message.obj;
1738                    if (!mCurrentDevice.equals(device)) {
1739                        break;
1740                    }
1741                    deferMessage(message);
1742                    /*
1743                     * fall through - disconnect audio first then expect
1744                     * deferred DISCONNECT message in Connected state
1745                     */
1746                case DISCONNECT_AUDIO:
1747                    /*
1748                     * just disconnect audio and wait for
1749                     * EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State
1750                     * Machines state changing
1751                     */
1752                    if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1753                        mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1754                        if (DBG) {
1755                            Log.d(TAG,"hfp_enable=false");
1756                        }
1757                        mAudioManager.setParameters("hfp_enable=false");
1758                        broadcastAudioState(mCurrentDevice,
1759                                BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1760                                BluetoothHeadsetClient.STATE_AUDIO_CONNECTED);
1761                    }
1762                    break;
1763                case STACK_EVENT:
1764                    StackEvent event = (StackEvent) message.obj;
1765                    if (DBG) {
1766                        Log.d(TAG, "AudioOn: event type: " + event.type);
1767                    }
1768                    switch (event.type) {
1769                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1770                            if (DBG) {
1771                                Log.d(TAG, "AudioOn connection state changed" + event.device + ": "
1772                                        + event.valueInt);
1773                            }
1774                            processConnectionEvent(event.valueInt, event.device);
1775                            break;
1776                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1777                            if (DBG) {
1778                                Log.d(TAG, "AudioOn audio state changed" + event.device + ": "
1779                                        + event.valueInt);
1780                            }
1781                            processAudioEvent(event.valueInt, event.device);
1782                            break;
1783                        default:
1784                            return NOT_HANDLED;
1785                    }
1786                    break;
1787                default:
1788                    return NOT_HANDLED;
1789            }
1790            return HANDLED;
1791        }
1792
1793        // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this
1794        private void processConnectionEvent(int state, BluetoothDevice device) {
1795            switch (state) {
1796                case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
1797                    if (mCurrentDevice.equals(device)) {
1798                        processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED,
1799                                device);
1800                        broadcastConnectionState(mCurrentDevice,
1801                                BluetoothProfile.STATE_DISCONNECTED,
1802                                BluetoothProfile.STATE_CONNECTED);
1803                        mCurrentDevice = null;
1804                        transitionTo(mDisconnected);
1805                    } else {
1806                        Log.e(TAG, "Disconnected from unknown device: " + device);
1807                    }
1808                    break;
1809                default:
1810                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1811                    break;
1812            }
1813        }
1814
1815        // in AudioOn state
1816        private void processAudioEvent(int state, BluetoothDevice device) {
1817            if (!mCurrentDevice.equals(device)) {
1818                Log.e(TAG, "Audio changed on disconnected device: " + device);
1819                return;
1820            }
1821
1822            switch (state) {
1823                case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
1824                    if (mAudioState != BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED) {
1825                        mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
1826                        // Audio focus may still be held by the entity controlling the actual call
1827                        // (such as Telecom) and hence this will still keep the call around, there
1828                        // is not much we can do here since dropping the call without user consent
1829                        // even if the audio connection snapped may not be a good idea.
1830                        if (DBG) {
1831                            Log.d(TAG,"hfp_enable=false");
1832                        }
1833                        mAudioManager.setParameters("hfp_enable=false");
1834                        broadcastAudioState(device,
1835                                BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
1836                                BluetoothHeadsetClient.STATE_AUDIO_CONNECTED);
1837                    }
1838
1839                    transitionTo(mConnected);
1840                    break;
1841                default:
1842                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1843                    break;
1844            }
1845        }
1846
1847        @Override
1848        public void exit() {
1849            if (DBG) {
1850                Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what);
1851            }
1852        }
1853    }
1854
1855    /**
1856     * @hide
1857     */
1858    public synchronized int getConnectionState(BluetoothDevice device) {
1859        if (mCurrentDevice == null) {
1860            return BluetoothProfile.STATE_DISCONNECTED;
1861        }
1862
1863        if (!mCurrentDevice.equals(device)) {
1864            return BluetoothProfile.STATE_DISCONNECTED;
1865        }
1866
1867        IState currentState = getCurrentState();
1868        if (currentState == mConnecting) {
1869            return BluetoothProfile.STATE_CONNECTING;
1870        }
1871
1872        if (currentState == mConnected || currentState == mAudioOn) {
1873            return BluetoothProfile.STATE_CONNECTED;
1874        }
1875
1876        Log.e(TAG, "Bad currentState: " + currentState);
1877        return BluetoothProfile.STATE_DISCONNECTED;
1878    }
1879
1880    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
1881        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
1882        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1883        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1884
1885        if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
1886            intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs);
1887        }
1888
1889        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1890        mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1891        if (DBG) {
1892            Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState);
1893        }
1894    }
1895
1896    // This method does not check for error condition (newState == prevState)
1897    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
1898        if (DBG) {
1899            Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState);
1900        }
1901        /*
1902         * Notifying the connection state change of the profile before sending
1903         * the intent for connection state change, as it was causing a race
1904         * condition, with the UI not being updated with the correct connection
1905         * state.
1906         */
1907        mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET_CLIENT,
1908                newState, prevState);
1909        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
1910        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1911        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1912        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1913
1914        // add feature extras when connected
1915        if (newState == BluetoothProfile.STATE_CONNECTED) {
1916            if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) ==
1917                    HeadsetClientHalConstants.PEER_FEAT_3WAY) {
1918                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
1919            }
1920            if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) ==
1921                    HeadsetClientHalConstants.PEER_FEAT_VREC) {
1922                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true);
1923            }
1924            if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VTAG) ==
1925                    HeadsetClientHalConstants.PEER_FEAT_VTAG) {
1926                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT, true);
1927            }
1928            if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) ==
1929                    HeadsetClientHalConstants.PEER_FEAT_REJECT) {
1930                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
1931            }
1932            if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) ==
1933                    HeadsetClientHalConstants.PEER_FEAT_ECC) {
1934                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
1935            }
1936
1937            // add individual CHLD support extras
1938            if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) ==
1939                    HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
1940                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
1941            }
1942            if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) ==
1943                    HeadsetClientHalConstants.CHLD_FEAT_REL) {
1944                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
1945            }
1946            if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) ==
1947                    HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
1948                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
1949            }
1950            if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) ==
1951                    HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
1952                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
1953            }
1954            if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) ==
1955                    HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
1956                intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
1957            }
1958        }
1959        mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
1960    }
1961
1962    boolean isConnected() {
1963        IState currentState = getCurrentState();
1964        return (currentState == mConnected || currentState == mAudioOn);
1965    }
1966
1967    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1968        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
1969        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
1970        int connectionState;
1971        synchronized (this) {
1972            for (BluetoothDevice device : bondedDevices) {
1973                ParcelUuid[] featureUuids = device.getUuids();
1974                if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) {
1975                    continue;
1976                }
1977                connectionState = getConnectionState(device);
1978                for (int state : states) {
1979                    if (connectionState == state) {
1980                        deviceList.add(device);
1981                    }
1982                }
1983            }
1984        }
1985        return deviceList;
1986    }
1987
1988    boolean okToConnect(BluetoothDevice device) {
1989        int priority = mService.getPriority(device);
1990        boolean ret = false;
1991        // check priority and accept or reject the connection. if priority is
1992        // undefined
1993        // it is likely that our SDP has not completed and peer is initiating
1994        // the
1995        // connection. Allow this connection, provided the device is bonded
1996        if ((BluetoothProfile.PRIORITY_OFF < priority) ||
1997                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
1998                (device.getBondState() != BluetoothDevice.BOND_NONE))) {
1999            ret = true;
2000        }
2001        return ret;
2002    }
2003
2004    boolean isAudioOn() {
2005        return (getCurrentState() == mAudioOn);
2006    }
2007
2008    public void setAudioRouteAllowed(boolean allowed) {
2009        mAudioRouteAllowed = allowed;
2010    }
2011
2012    public boolean getAudioRouteAllowed() {
2013        return mAudioRouteAllowed;
2014    }
2015
2016    synchronized int getAudioState(BluetoothDevice device) {
2017        if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
2018            return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
2019        }
2020        return mAudioState;
2021    }
2022
2023    /**
2024     * @hide
2025     */
2026    List<BluetoothDevice> getConnectedDevices() {
2027        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2028        synchronized (this) {
2029            if (isConnected()) {
2030                devices.add(mCurrentDevice);
2031            }
2032        }
2033        return devices;
2034    }
2035
2036    private BluetoothDevice getDevice(byte[] address) {
2037        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
2038    }
2039
2040    private void onConnectionStateChanged(int state, int peer_feat, int chld_feat, byte[] address) {
2041        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
2042        event.valueInt = state;
2043        event.valueInt2 = peer_feat;
2044        event.valueInt3 = chld_feat;
2045        event.device = getDevice(address);
2046        if (DBG) {
2047            Log.d(TAG, "incoming" + event);
2048        }
2049        sendMessage(STACK_EVENT, event);
2050    }
2051
2052    private void onAudioStateChanged(int state, byte[] address) {
2053        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
2054        event.valueInt = state;
2055        event.device = getDevice(address);
2056        if (DBG) {
2057            Log.d(TAG, "incoming" + event);
2058        }
2059        sendMessage(STACK_EVENT, event);
2060    }
2061
2062    private void onVrStateChanged(int state) {
2063        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
2064        event.valueInt = state;
2065        if (DBG) {
2066            Log.d(TAG, "incoming" + event);
2067        }
2068        sendMessage(STACK_EVENT, event);
2069    }
2070
2071    private void onNetworkState(int state) {
2072        StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_STATE);
2073        event.valueInt = state;
2074        if (DBG) {
2075            Log.d(TAG, "incoming" + event);
2076        }
2077        sendMessage(STACK_EVENT, event);
2078    }
2079
2080    private void onNetworkRoaming(int state) {
2081        StackEvent event = new StackEvent(EVENT_TYPE_ROAMING_STATE);
2082        event.valueInt = state;
2083        if (DBG) {
2084            Log.d(TAG, "incoming" + event);
2085        }
2086        sendMessage(STACK_EVENT, event);
2087    }
2088
2089    private void onNetworkSignal(int signal) {
2090        StackEvent event = new StackEvent(EVENT_TYPE_NETWORK_SIGNAL);
2091        event.valueInt = signal;
2092        if (DBG) {
2093            Log.d(TAG, "incoming" + event);
2094        }
2095        sendMessage(STACK_EVENT, event);
2096    }
2097
2098    private void onBatteryLevel(int level) {
2099        StackEvent event = new StackEvent(EVENT_TYPE_BATTERY_LEVEL);
2100        event.valueInt = level;
2101        if (DBG) {
2102            Log.d(TAG, "incoming" + event);
2103        }
2104        sendMessage(STACK_EVENT, event);
2105    }
2106
2107    private void onCurrentOperator(String name) {
2108        StackEvent event = new StackEvent(EVENT_TYPE_OPERATOR_NAME);
2109        event.valueString = name;
2110        if (DBG) {
2111            Log.d(TAG, "incoming" + event);
2112        }
2113        sendMessage(STACK_EVENT, event);
2114    }
2115
2116    /**
2117     * CIEV (Call indicators) notifying if a call is in progress.
2118     *
2119     * Values Include:
2120     * 0 - No call in progress
2121     * 1 - Atleast 1 call is in progress
2122     */
2123    private void onCall(int call) {
2124        StackEvent event = new StackEvent(EVENT_TYPE_CALL);
2125        event.valueInt = call;
2126        if (DBG) {
2127            Log.d(TAG, "incoming" + event);
2128        }
2129        sendMessage(STACK_EVENT, event);
2130    }
2131
2132    /**
2133     * CIEV (Call indicators) notifying if call(s) are getting set up.
2134     *
2135     * Values incldue:
2136     * 0 - No current call is in setup
2137     * 1 - Incoming call process ongoing
2138     * 2 - Outgoing call process ongoing
2139     * 3 - Remote party being alerted for outgoing call
2140     */
2141    private void onCallSetup(int callsetup) {
2142        StackEvent event = new StackEvent(EVENT_TYPE_CALLSETUP);
2143        event.valueInt = callsetup;
2144        if (DBG) {
2145            Log.d(TAG, "incoming" + event);
2146        }
2147        sendMessage(STACK_EVENT, event);
2148    }
2149
2150    /**
2151     * CIEV (Call indicators) notifying call held states.
2152     *
2153     * Values include:
2154     * 0 - No calls held
2155     * 1 - Call is placed on hold or active/held calls wapped (The AG has both an ACTIVE and HELD
2156     * call)
2157     * 2 - Call on hold, no active call
2158     */
2159    private void onCallHeld(int callheld) {
2160        StackEvent event = new StackEvent(EVENT_TYPE_CALLHELD);
2161        event.valueInt = callheld;
2162        if (DBG) {
2163            Log.d(TAG, "incoming" + event);
2164        }
2165        sendMessage(STACK_EVENT, event);
2166    }
2167
2168    private void onRespAndHold(int resp_and_hold) {
2169        StackEvent event = new StackEvent(EVENT_TYPE_RESP_AND_HOLD);
2170        event.valueInt = resp_and_hold;
2171        if (DBG) {
2172            Log.d(TAG, "incoming" + event);
2173        }
2174        sendMessage(STACK_EVENT, event);
2175    }
2176
2177    private void onClip(String number) {
2178        StackEvent event = new StackEvent(EVENT_TYPE_CLIP);
2179        event.valueString = number;
2180        if (DBG) {
2181            Log.d(TAG, "incoming" + event);
2182        }
2183        sendMessage(STACK_EVENT, event);
2184    }
2185
2186    private void onCallWaiting(String number) {
2187        StackEvent event = new StackEvent(EVENT_TYPE_CALL_WAITING);
2188        event.valueString = number;
2189        if (DBG) {
2190            Log.d(TAG, "incoming" + event);
2191        }
2192        sendMessage(STACK_EVENT, event);
2193    }
2194
2195    private void onCurrentCalls(int index, int dir, int state, int mparty, String number) {
2196        StackEvent event = new StackEvent(EVENT_TYPE_CURRENT_CALLS);
2197        event.valueInt = index;
2198        event.valueInt2 = dir;
2199        event.valueInt3 = state;
2200        event.valueInt4 = mparty;
2201        event.valueString = number;
2202        if (DBG) {
2203            Log.d(TAG, "incoming" + event);
2204        }
2205        sendMessage(STACK_EVENT, event);
2206    }
2207
2208    private void onVolumeChange(int type, int volume) {
2209        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
2210        event.valueInt = type;
2211        event.valueInt2 = volume;
2212        if (DBG) {
2213            Log.d(TAG, "incoming" + event);
2214        }
2215        sendMessage(STACK_EVENT, event);
2216    }
2217
2218    private void onCmdResult(int type, int cme) {
2219        StackEvent event = new StackEvent(EVENT_TYPE_CMD_RESULT);
2220        event.valueInt = type;
2221        event.valueInt2 = cme;
2222        if (DBG) {
2223            Log.d(TAG, "incoming" + event);
2224        }
2225        sendMessage(STACK_EVENT, event);
2226    }
2227
2228    private void onSubscriberInfo(String number, int type) {
2229        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_INFO);
2230        event.valueInt = type;
2231        event.valueString = number;
2232        if (DBG) {
2233            Log.d(TAG, "incoming" + event);
2234        }
2235        sendMessage(STACK_EVENT, event);
2236    }
2237
2238    private void onInBandRing(int in_band) {
2239        StackEvent event = new StackEvent(EVENT_TYPE_IN_BAND_RING);
2240        event.valueInt = in_band;
2241        if (DBG) {
2242            Log.d(TAG, "incoming" + event);
2243        }
2244        sendMessage(STACK_EVENT, event);
2245    }
2246
2247    private void onLastVoiceTagNumber(String number) {
2248        StackEvent event = new StackEvent(EVENT_TYPE_LAST_VOICE_TAG_NUMBER);
2249        event.valueString = number;
2250        if (DBG) {
2251            Log.d(TAG, "incoming" + event);
2252        }
2253        sendMessage(STACK_EVENT, event);
2254    }
2255
2256    private void onRingIndication() {
2257        StackEvent event = new StackEvent(EVENT_TYPE_RING_INDICATION);
2258        if (DBG) {
2259            Log.d(TAG, "incoming" + event);
2260        }
2261        sendMessage(STACK_EVENT, event);
2262    }
2263
2264    private String getCurrentDeviceName() {
2265        String defaultName = "<unknown>";
2266        if (mCurrentDevice == null) {
2267            return defaultName;
2268        }
2269        String deviceName = mCurrentDevice.getName();
2270        if (deviceName == null) {
2271            return defaultName;
2272        }
2273        return deviceName;
2274    }
2275
2276    private byte[] getByteAddress(BluetoothDevice device) {
2277        return Utils.getBytesFromAddress(device.getAddress());
2278    }
2279
2280    // Event types for STACK_EVENT message
2281    final private static int EVENT_TYPE_NONE = 0;
2282    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
2283    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
2284    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
2285    final private static int EVENT_TYPE_NETWORK_STATE = 4;
2286    final private static int EVENT_TYPE_ROAMING_STATE = 5;
2287    final private static int EVENT_TYPE_NETWORK_SIGNAL = 6;
2288    final private static int EVENT_TYPE_BATTERY_LEVEL = 7;
2289    final private static int EVENT_TYPE_OPERATOR_NAME = 8;
2290    final private static int EVENT_TYPE_CALL = 9;
2291    final private static int EVENT_TYPE_CALLSETUP = 10;
2292    final private static int EVENT_TYPE_CALLHELD = 11;
2293    final private static int EVENT_TYPE_CLIP = 12;
2294    final private static int EVENT_TYPE_CALL_WAITING = 13;
2295    final private static int EVENT_TYPE_CURRENT_CALLS = 14;
2296    final private static int EVENT_TYPE_VOLUME_CHANGED = 15;
2297    final private static int EVENT_TYPE_CMD_RESULT = 16;
2298    final private static int EVENT_TYPE_SUBSCRIBER_INFO = 17;
2299    final private static int EVENT_TYPE_RESP_AND_HOLD = 18;
2300    final private static int EVENT_TYPE_IN_BAND_RING = 19;
2301    final private static int EVENT_TYPE_LAST_VOICE_TAG_NUMBER = 20;
2302    final private static int EVENT_TYPE_RING_INDICATION= 21;
2303
2304    // for debugging only
2305    private final String EVENT_TYPE_NAMES[] =
2306    {
2307            "EVENT_TYPE_NONE",
2308            "EVENT_TYPE_CONNECTION_STATE_CHANGED",
2309            "EVENT_TYPE_AUDIO_STATE_CHANGED",
2310            "EVENT_TYPE_VR_STATE_CHANGED",
2311            "EVENT_TYPE_NETWORK_STATE",
2312            "EVENT_TYPE_ROAMING_STATE",
2313            "EVENT_TYPE_NETWORK_SIGNAL",
2314            "EVENT_TYPE_BATTERY_LEVEL",
2315            "EVENT_TYPE_OPERATOR_NAME",
2316            "EVENT_TYPE_CALL",
2317            "EVENT_TYPE_CALLSETUP",
2318            "EVENT_TYPE_CALLHELD",
2319            "EVENT_TYPE_CLIP",
2320            "EVENT_TYPE_CALL_WAITING",
2321            "EVENT_TYPE_CURRENT_CALLS",
2322            "EVENT_TYPE_VOLUME_CHANGED",
2323            "EVENT_TYPE_CMD_RESULT",
2324            "EVENT_TYPE_SUBSCRIBER_INFO",
2325            "EVENT_TYPE_RESP_AND_HOLD",
2326            "EVENT_TYPE_IN_BAND_RING",
2327            "EVENT_TYPE_LAST_VOICE_TAG_NUMBER",
2328            "EVENT_TYPE_RING_INDICATION",
2329    };
2330
2331    private class StackEvent {
2332        int type = EVENT_TYPE_NONE;
2333        int valueInt = 0;
2334        int valueInt2 = 0;
2335        int valueInt3 = 0;
2336        int valueInt4 = 0;
2337        String valueString = null;
2338        BluetoothDevice device = null;
2339
2340        private StackEvent(int type) {
2341            this.type = type;
2342        }
2343
2344        @Override
2345        public String toString() {
2346            // event dump
2347            StringBuilder result = new StringBuilder();
2348            result.append("StackEvent {type:" + EVENT_TYPE_NAMES[type]);
2349            result.append(", value1:" + valueInt);
2350            result.append(", value2:" + valueInt2);
2351            result.append(", value3:" + valueInt3);
2352            result.append(", value4:" + valueInt4);
2353            result.append(", string: \"" + valueString + "\"");
2354            result.append(", device:" + device + "}");
2355            return result.toString();
2356        }
2357    }
2358
2359    private native static void classInitNative();
2360
2361    private native void initializeNative();
2362
2363    private native void cleanupNative();
2364
2365    private native boolean connectNative(byte[] address);
2366
2367    private native boolean disconnectNative(byte[] address);
2368
2369    private native boolean connectAudioNative(byte[] address);
2370
2371    private native boolean disconnectAudioNative(byte[] address);
2372
2373    private native boolean startVoiceRecognitionNative();
2374
2375    private native boolean stopVoiceRecognitionNative();
2376
2377    private native boolean setVolumeNative(int volumeType, int volume);
2378
2379    private native boolean dialNative(String number);
2380
2381    private native boolean dialMemoryNative(int location);
2382
2383    private native boolean handleCallActionNative(int action, int index);
2384
2385    private native boolean queryCurrentCallsNative();
2386
2387    private native boolean queryCurrentOperatorNameNative();
2388
2389    private native boolean retrieveSubscriberInfoNative();
2390
2391    private native boolean sendDtmfNative(byte code);
2392
2393    private native boolean requestLastVoiceTagNumberNative();
2394
2395    private native boolean sendATCmdNative(int ATCmd, int val1,
2396            int val2, String arg);
2397
2398    public List<BluetoothHeadsetClientCall> getCurrentCalls() {
2399        return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values());
2400    }
2401
2402    public Bundle getCurrentAgEvents() {
2403        Bundle b = new Bundle();
2404        b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState);
2405        b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal);
2406        b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType);
2407        b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel);
2408        b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName);
2409        b.putInt(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, mVoiceRecognitionActive);
2410        b.putInt(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, mInBandRingtone);
2411        b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo);
2412        return b;
2413    }
2414}
2415