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