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