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