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