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