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