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