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