HeadsetStateMachine.java revision 3659dee2b21c7f269a2bef051483093fe07e5682
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * Bluetooth Handset StateMachine
19 *                      (Disconnected)
20 *                           |    ^
21 *                   CONNECT |    | DISCONNECTED
22 *                           V    |
23 *                         (Pending)
24 *                           |    ^
25 *                 CONNECTED |    | CONNECT
26 *                           V    |
27 *                        (Connected)
28 *                           |    ^
29 *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
30 *                           V    |
31 *                         (AudioOn)
32 */
33package com.android.bluetooth.hfp;
34
35import android.bluetooth.BluetoothAdapter;
36import android.bluetooth.BluetoothAssignedNumbers;
37import android.bluetooth.BluetoothDevice;
38import android.bluetooth.BluetoothHeadset;
39import android.bluetooth.BluetoothProfile;
40import android.bluetooth.BluetoothUuid;
41import android.bluetooth.IBluetoothHeadsetPhone;
42import android.content.ComponentName;
43import android.content.Context;
44import android.content.Intent;
45import android.content.ServiceConnection;
46import android.content.ActivityNotFoundException;
47import android.media.AudioManager;
48import android.net.Uri;
49import android.os.IBinder;
50import android.os.IDeviceIdleController;
51import android.os.Message;
52import android.os.ParcelUuid;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.os.PowerManager;
56import android.os.UserHandle;
57import android.os.PowerManager.WakeLock;
58import android.telephony.PhoneNumberUtils;
59import android.util.Log;
60import com.android.bluetooth.Utils;
61import com.android.bluetooth.btservice.AdapterService;
62import com.android.bluetooth.btservice.ProfileService;
63import com.android.internal.util.IState;
64import com.android.internal.util.State;
65import com.android.internal.util.StateMachine;
66import java.util.ArrayList;
67import java.util.HashMap;
68import java.util.List;
69import java.util.Map;
70import java.util.Set;
71import android.os.SystemProperties;
72
73final class HeadsetStateMachine extends StateMachine {
74    private static final String TAG = "HeadsetStateMachine";
75    private static final boolean DBG = false;
76    // For Debugging only
77    private static int sRefCount = 0;
78
79    private static final String HEADSET_NAME = "bt_headset_name";
80    private static final String HEADSET_NREC = "bt_headset_nrec";
81    private static final String HEADSET_WBS = "bt_wbs";
82
83    static final int CONNECT = 1;
84    static final int DISCONNECT = 2;
85    static final int CONNECT_AUDIO = 3;
86    static final int DISCONNECT_AUDIO = 4;
87    static final int VOICE_RECOGNITION_START = 5;
88    static final int VOICE_RECOGNITION_STOP = 6;
89
90    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
91    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
92    static final int INTENT_SCO_VOLUME_CHANGED = 7;
93    static final int SET_MIC_VOLUME = 8;
94    static final int CALL_STATE_CHANGED = 9;
95    static final int INTENT_BATTERY_CHANGED = 10;
96    static final int DEVICE_STATE_CHANGED = 11;
97    static final int SEND_CCLC_RESPONSE = 12;
98    static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
99
100    static final int VIRTUAL_CALL_START = 14;
101    static final int VIRTUAL_CALL_STOP = 15;
102
103    static final int ENABLE_WBS = 16;
104    static final int DISABLE_WBS = 17;
105
106    static final int BIND_RESPONSE = 18;
107
108    private static final int STACK_EVENT = 101;
109    private static final int DIALING_OUT_TIMEOUT = 102;
110    private static final int START_VR_TIMEOUT = 103;
111    private static final int CLCC_RSP_TIMEOUT = 104;
112
113    private static final int CONNECT_TIMEOUT = 201;
114
115    private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
116    private static final int START_VR_TIMEOUT_VALUE = 5000;
117    private static final int CLCC_RSP_TIMEOUT_VALUE = 5000;
118
119    // Max number of HF connections at any time
120    private int max_hf_connections = 1;
121
122    private static final int NBS_CODEC = 1;
123    private static final int WBS_CODEC = 2;
124
125    // Keys are AT commands, and values are the company IDs.
126    private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
127    // Hash for storing the Audio Parameters like NREC for connected headsets
128    private HashMap<BluetoothDevice, HashMap> mHeadsetAudioParam =
129            new HashMap<BluetoothDevice, HashMap>();
130    // Hash for storing the Remotedevice BRSF
131    private HashMap<BluetoothDevice, Integer> mHeadsetBrsf =
132            new HashMap<BluetoothDevice, Integer>();
133
134    private static final ParcelUuid[] HEADSET_UUIDS = {
135            BluetoothUuid.HSP, BluetoothUuid.Handsfree,
136    };
137
138    private Disconnected mDisconnected;
139    private Pending mPending;
140    private Connected mConnected;
141    private AudioOn mAudioOn;
142    // Multi HFP: add new class object
143    private MultiHFPending mMultiHFPending;
144
145    private HeadsetService mService;
146    private PowerManager mPowerManager;
147    private boolean mVirtualCallStarted = false;
148    private boolean mVoiceRecognitionStarted = false;
149    private boolean mWaitingForVoiceRecognition = false;
150    private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition
151
152    private boolean mDialingOut = false;
153    private AudioManager mAudioManager;
154    private AtPhonebook mPhonebook;
155
156    private static Intent sVoiceCommandIntent;
157
158    private HeadsetPhoneState mPhoneState;
159    private int mAudioState;
160    private BluetoothAdapter mAdapter;
161    private IBluetoothHeadsetPhone mPhoneProxy;
162    private boolean mNativeAvailable;
163
164    // Indicates whether audio can be routed to the device.
165    private boolean mAudioRouteAllowed = true;
166
167    // mCurrentDevice is the device connected before the state changes
168    // mTargetDevice is the device to be connected
169    // mIncomingDevice is the device connecting to us, valid only in Pending state
170    //                when mIncomingDevice is not null, both mCurrentDevice
171    //                  and mTargetDevice are null
172    //                when either mCurrentDevice or mTargetDevice is not null,
173    //                  mIncomingDevice is null
174    // Stable states
175    //   No connection, Disconnected state
176    //                  both mCurrentDevice and mTargetDevice are null
177    //   Connected, Connected state
178    //              mCurrentDevice is not null, mTargetDevice is null
179    // Interim states
180    //   Connecting to a device, Pending
181    //                           mCurrentDevice is null, mTargetDevice is not null
182    //   Disconnecting device, Connecting to new device
183    //     Pending
184    //     Both mCurrentDevice and mTargetDevice are not null
185    //   Disconnecting device Pending
186    //                        mCurrentDevice is not null, mTargetDevice is null
187    //   Incoming connections Pending
188    //                        Both mCurrentDevice and mTargetDevice are null
189    private BluetoothDevice mCurrentDevice = null;
190    private BluetoothDevice mTargetDevice = null;
191    private BluetoothDevice mIncomingDevice = null;
192    private BluetoothDevice mActiveScoDevice = null;
193    private BluetoothDevice mMultiDisconnectDevice = null;
194
195    // Multi HFP: Connected devices list holds all currently connected headsets
196    private ArrayList<BluetoothDevice> mConnectedDevicesList = new ArrayList<BluetoothDevice>();
197
198    static {
199        classInitNative();
200
201        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>();
202        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS);
203        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE);
204    }
205
206    private HeadsetStateMachine(HeadsetService context) {
207        super(TAG);
208        mService = context;
209        mVoiceRecognitionStarted = false;
210        mWaitingForVoiceRecognition = false;
211
212        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
213        mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(
214                PowerManager.PARTIAL_WAKE_LOCK, TAG + ":VoiceRecognition");
215        mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
216
217        mDialingOut = false;
218        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
219        mPhonebook = new AtPhonebook(mService, this);
220        mPhoneState = new HeadsetPhoneState(context, this);
221        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
222        mAdapter = BluetoothAdapter.getDefaultAdapter();
223        Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
224        intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
225        if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
226            Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
227        }
228
229        String max_hfp_clients = SystemProperties.get("bt.max.hfpclient.connections");
230        if (!max_hfp_clients.isEmpty() && (Integer.parseInt(max_hfp_clients) == 2)) {
231            max_hf_connections = Integer.parseInt(max_hfp_clients);
232        }
233        Log.d(TAG, "max_hf_connections = " + max_hf_connections);
234        initializeNative(max_hf_connections);
235        mNativeAvailable = true;
236
237        mDisconnected = new Disconnected();
238        mPending = new Pending();
239        mConnected = new Connected();
240        mAudioOn = new AudioOn();
241        // Multi HFP: initialise new class variable
242        mMultiHFPending = new MultiHFPending();
243
244        if (sVoiceCommandIntent == null) {
245            sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
246            sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
247        }
248
249        addState(mDisconnected);
250        addState(mPending);
251        addState(mConnected);
252        addState(mAudioOn);
253        // Multi HFP: add State
254        addState(mMultiHFPending);
255
256        setInitialState(mDisconnected);
257    }
258
259    static HeadsetStateMachine make(HeadsetService context) {
260        Log.d(TAG, "make");
261        HeadsetStateMachine hssm = new HeadsetStateMachine(context);
262        hssm.start();
263        return hssm;
264    }
265
266    public void doQuit() {
267        quitNow();
268    }
269
270    public void cleanup() {
271        if (mPhoneProxy != null) {
272            if (DBG) Log.d(TAG, "Unbinding service...");
273            synchronized (mConnection) {
274                try {
275                    mPhoneProxy = null;
276                    mService.unbindService(mConnection);
277                } catch (Exception re) {
278                    Log.e(TAG, "Error unbinding from IBluetoothHeadsetPhone", re);
279                }
280            }
281        }
282        if (mPhoneState != null) {
283            mPhoneState.listenForPhoneState(false);
284            mPhoneState.cleanup();
285        }
286        if (mPhonebook != null) {
287            mPhonebook.cleanup();
288        }
289        if (mHeadsetAudioParam != null) {
290            mHeadsetAudioParam.clear();
291        }
292        if (mHeadsetBrsf != null) {
293            mHeadsetBrsf.clear();
294        }
295        if (mConnectedDevicesList != null) {
296            mConnectedDevicesList.clear();
297        }
298        if (mNativeAvailable) {
299            cleanupNative();
300            mNativeAvailable = false;
301        }
302    }
303
304    public void dump(StringBuilder sb) {
305        ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
306        ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
307        ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
308        ProfileService.println(sb, "mActiveScoDevice: " + mActiveScoDevice);
309        ProfileService.println(sb, "mMultiDisconnectDevice: " + mMultiDisconnectDevice);
310        ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
311        ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
312        ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
313        ProfileService.println(sb, "StateMachine: " + this.toString());
314        ProfileService.println(sb, "mPhoneState: " + mPhoneState);
315        ProfileService.println(sb, "mAudioState: " + mAudioState);
316    }
317
318    private class Disconnected extends State {
319        @Override
320        public void enter() {
321            log("Enter Disconnected: " + getCurrentMessage().what + ", size: "
322                    + mConnectedDevicesList.size());
323            mPhonebook.resetAtState();
324            mPhoneState.listenForPhoneState(false);
325            mVoiceRecognitionStarted = false;
326            mWaitingForVoiceRecognition = false;
327        }
328
329        @Override
330        public boolean processMessage(Message message) {
331            log("Disconnected process message: " + message.what + ", size: "
332                    + mConnectedDevicesList.size());
333            if (mConnectedDevicesList.size() != 0 || mTargetDevice != null
334                    || mIncomingDevice != null) {
335                Log.e(TAG, "ERROR: mConnectedDevicesList is not empty,"
336                                + "target, or mIncomingDevice not null in Disconnected");
337                return NOT_HANDLED;
338            }
339
340            boolean retValue = HANDLED;
341            switch (message.what) {
342                case CONNECT:
343                    BluetoothDevice device = (BluetoothDevice) message.obj;
344                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
345                            BluetoothProfile.STATE_DISCONNECTED);
346
347                    if (!connectHfpNative(getByteAddress(device))) {
348                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
349                                BluetoothProfile.STATE_CONNECTING);
350                        break;
351                    }
352
353                    synchronized (HeadsetStateMachine.this) {
354                        mTargetDevice = device;
355                        transitionTo(mPending);
356                    }
357                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
358                    //          sends back events consistently
359                    Message m = obtainMessage(CONNECT_TIMEOUT);
360                    m.obj = device;
361                    sendMessageDelayed(m, 30000);
362                    break;
363                case DISCONNECT:
364                    // ignore
365                    break;
366                case INTENT_BATTERY_CHANGED:
367                    processIntentBatteryChanged((Intent) message.obj);
368                    break;
369                case CALL_STATE_CHANGED:
370                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
371                    break;
372                case STACK_EVENT:
373                    StackEvent event = (StackEvent) message.obj;
374                    if (DBG) {
375                        log("event type: " + event.type);
376                    }
377                    switch (event.type) {
378                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
379                            processConnectionEvent(event.valueInt, event.device);
380                            break;
381                        default:
382                            Log.e(TAG, "Unexpected stack event: " + event.type);
383                            break;
384                    }
385                    break;
386                default:
387                    return NOT_HANDLED;
388            }
389            return retValue;
390        }
391
392        @Override
393        public void exit() {
394            log("Exit Disconnected: " + getCurrentMessage().what);
395        }
396
397        // in Disconnected state
398        private void processConnectionEvent(int state, BluetoothDevice device) {
399            Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
400            switch (state) {
401                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
402                    Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
403                    break;
404                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
405                    if (okToConnect(device)) {
406                        Log.i(TAG, "Incoming Hf accepted");
407                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
408                                BluetoothProfile.STATE_DISCONNECTED);
409                        synchronized (HeadsetStateMachine.this) {
410                            mIncomingDevice = device;
411                            transitionTo(mPending);
412                        }
413                    } else {
414                        Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
415                                        + " bondState=" + device.getBondState());
416                        // reject the connection and stay in Disconnected state itself
417                        disconnectHfpNative(getByteAddress(device));
418                        // the other profile connection should be initiated
419                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
420                                BluetoothProfile.STATE_DISCONNECTED);
421                    }
422                    break;
423                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
424                    Log.w(TAG, "HFP Connected from Disconnected state");
425                    if (okToConnect(device)) {
426                        Log.i(TAG, "Incoming Hf accepted");
427                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
428                                BluetoothProfile.STATE_DISCONNECTED);
429                        synchronized (HeadsetStateMachine.this) {
430                            if (!mConnectedDevicesList.contains(device)) {
431                                mConnectedDevicesList.add(device);
432                                Log.d(TAG, "device " + device.getAddress()
433                                                + " is adding in Disconnected state");
434                            }
435                            mCurrentDevice = device;
436                            transitionTo(mConnected);
437                        }
438                        configAudioParameters(device);
439                    } else {
440                        // reject the connection and stay in Disconnected state itself
441                        Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
442                                        + " bondState=" + device.getBondState());
443                        disconnectHfpNative(getByteAddress(device));
444                        // the other profile connection should be initiated
445                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
446                                BluetoothProfile.STATE_DISCONNECTED);
447                    }
448                    break;
449                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
450                    Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
451                    break;
452                default:
453                    Log.e(TAG, "Incorrect state: " + state);
454                    break;
455            }
456        }
457    }
458
459    private class Pending extends State {
460        @Override
461        public void enter() {
462            log("Enter Pending: " + getCurrentMessage().what);
463        }
464
465        @Override
466        public boolean processMessage(Message message) {
467            log("Pending process message: " + message.what + ", size: "
468                    + mConnectedDevicesList.size());
469
470            boolean retValue = HANDLED;
471            switch (message.what) {
472                case CONNECT:
473                case CONNECT_AUDIO:
474                    deferMessage(message);
475                    break;
476                case CONNECT_TIMEOUT:
477                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
478                            getByteAddress(mTargetDevice));
479                    break;
480                case DISCONNECT:
481                    BluetoothDevice device = (BluetoothDevice) message.obj;
482                    if (mCurrentDevice != null && mTargetDevice != null
483                            && mTargetDevice.equals(device)) {
484                        // cancel connection to the mTargetDevice
485                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
486                                BluetoothProfile.STATE_CONNECTING);
487                        synchronized (HeadsetStateMachine.this) {
488                            mTargetDevice = null;
489                        }
490                    } else {
491                        deferMessage(message);
492                    }
493                    break;
494                case INTENT_BATTERY_CHANGED:
495                    processIntentBatteryChanged((Intent) message.obj);
496                    break;
497                case CALL_STATE_CHANGED:
498                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
499                    break;
500                case STACK_EVENT:
501                    StackEvent event = (StackEvent) message.obj;
502                    if (DBG) {
503                        log("event type: " + event.type);
504                    }
505                    switch (event.type) {
506                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
507                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
508                            if (device1 != null && device1.equals(event.device)) {
509                                Log.d(TAG, "remove connect timeout for device = " + device1);
510                                removeMessages(CONNECT_TIMEOUT);
511                            }
512                            processConnectionEvent(event.valueInt, event.device);
513                            break;
514
515                        case EVENT_TYPE_BIND:
516                            processAtBind(event.valueString, event.device);
517                            break;
518
519                        case EVENT_TYPE_BIEV:
520                            processAtBiev(event.valueInt, event.valueInt2, event.device);
521                            break;
522
523                        default:
524                            Log.e(TAG, "Unexpected event: " + event.type);
525                            break;
526                    }
527                    break;
528                default:
529                    return NOT_HANDLED;
530            }
531            return retValue;
532        }
533
534        // in Pending state
535        private void processConnectionEvent(int state, BluetoothDevice device) {
536            Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
537            switch (state) {
538                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
539                    if (mConnectedDevicesList.contains(device)) {
540                        synchronized (HeadsetStateMachine.this) {
541                            mConnectedDevicesList.remove(device);
542                            mHeadsetAudioParam.remove(device);
543                            mHeadsetBrsf.remove(device);
544                            Log.d(TAG, "device " + device.getAddress()
545                                            + " is removed in Pending state");
546                        }
547
548                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
549                                BluetoothProfile.STATE_DISCONNECTING);
550                        synchronized (HeadsetStateMachine.this) {
551                            mCurrentDevice = null;
552                        }
553
554                        processWBSEvent(0, device); /* disable WBS audio parameters */
555
556                        if (mTargetDevice != null) {
557                            if (!connectHfpNative(getByteAddress(mTargetDevice))) {
558                                broadcastConnectionState(mTargetDevice,
559                                        BluetoothProfile.STATE_DISCONNECTED,
560                                        BluetoothProfile.STATE_CONNECTING);
561                                synchronized (HeadsetStateMachine.this) {
562                                    mTargetDevice = null;
563                                    transitionTo(mDisconnected);
564                                }
565                            }
566                        } else {
567                            synchronized (HeadsetStateMachine.this) {
568                                mIncomingDevice = null;
569                                if (mConnectedDevicesList.size() == 0) {
570                                    transitionTo(mDisconnected);
571                                } else {
572                                    processMultiHFConnected(device);
573                                }
574                            }
575                        }
576                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
577                        // outgoing connection failed
578                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
579                                BluetoothProfile.STATE_CONNECTING);
580                        synchronized (HeadsetStateMachine.this) {
581                            mTargetDevice = null;
582                            if (mConnectedDevicesList.size() == 0) {
583                                transitionTo(mDisconnected);
584                            } else {
585                                transitionTo(mConnected);
586                            }
587                        }
588                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
589                        broadcastConnectionState(mIncomingDevice,
590                                BluetoothProfile.STATE_DISCONNECTED,
591                                BluetoothProfile.STATE_CONNECTING);
592                        synchronized (HeadsetStateMachine.this) {
593                            mIncomingDevice = null;
594                            if (mConnectedDevicesList.size() == 0) {
595                                transitionTo(mDisconnected);
596                            } else {
597                                transitionTo(mConnected);
598                            }
599                        }
600                    } else {
601                        Log.e(TAG, "Unknown device Disconnected: " + device);
602                    }
603                    break;
604                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
605                    if (mConnectedDevicesList.contains(device)) {
606                        // disconnection failed
607                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
608                                BluetoothProfile.STATE_DISCONNECTING);
609                        if (mTargetDevice != null) {
610                            broadcastConnectionState(mTargetDevice,
611                                    BluetoothProfile.STATE_DISCONNECTED,
612                                    BluetoothProfile.STATE_CONNECTING);
613                        }
614                        synchronized (HeadsetStateMachine.this) {
615                            mTargetDevice = null;
616                            transitionTo(mConnected);
617                        }
618                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
619                        synchronized (HeadsetStateMachine.this) {
620                            mCurrentDevice = device;
621                            mConnectedDevicesList.add(device);
622                            Log.d(TAG,
623                                    "device " + device.getAddress() + " is added in Pending state");
624                            mTargetDevice = null;
625                            transitionTo(mConnected);
626                        }
627                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
628                                BluetoothProfile.STATE_CONNECTING);
629                        configAudioParameters(device);
630                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
631                        synchronized (HeadsetStateMachine.this) {
632                            mCurrentDevice = device;
633                            mConnectedDevicesList.add(device);
634                            Log.d(TAG,
635                                    "device " + device.getAddress() + " is added in Pending state");
636                            mIncomingDevice = null;
637                            transitionTo(mConnected);
638                        }
639                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
640                                BluetoothProfile.STATE_CONNECTING);
641                        configAudioParameters(device);
642                    } else {
643                        Log.w(TAG, "Some other incoming HF connected in Pending state");
644                        if (okToConnect(device)) {
645                            Log.i(TAG, "Incoming Hf accepted");
646                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
647                                    BluetoothProfile.STATE_DISCONNECTED);
648                            synchronized (HeadsetStateMachine.this) {
649                                mCurrentDevice = device;
650                                mConnectedDevicesList.add(device);
651                                Log.d(TAG, "device " + device.getAddress()
652                                                + " is added in Pending state");
653                            }
654                            configAudioParameters(device);
655                        } else {
656                            // reject the connection and stay in Pending state itself
657                            Log.i(TAG, "Incoming Hf rejected. priority="
658                                            + mService.getPriority(device) + " bondState="
659                                            + device.getBondState());
660                            disconnectHfpNative(getByteAddress(device));
661                        }
662                    }
663                    break;
664                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
665                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
666                        log("current device tries to connect back");
667                        // TODO(BT) ignore or reject
668                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
669                        // The stack is connecting to target device or
670                        // there is an incoming connection from the target device at the same time
671                        // we already broadcasted the intent, doing nothing here
672                        if (DBG) {
673                            log("Stack and target device are connecting");
674                        }
675                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
676                        Log.e(TAG, "Another connecting event on the incoming device");
677                    } else {
678                        // We get an incoming connecting request while Pending
679                        // TODO(BT) is stack handing this case? let's ignore it for now
680                        log("Incoming connection while pending, ignore");
681                    }
682                    break;
683                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
684                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
685                        // we already broadcasted the intent, doing nothing here
686                        if (DBG) {
687                            log("stack is disconnecting mCurrentDevice");
688                        }
689                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
690                        Log.e(TAG, "TargetDevice is getting disconnected");
691                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
692                        Log.e(TAG, "IncomingDevice is getting disconnected");
693                    } else {
694                        Log.e(TAG, "Disconnecting unknow device: " + device);
695                    }
696                    break;
697                default:
698                    Log.e(TAG, "Incorrect state: " + state);
699                    break;
700            }
701        }
702
703        private void processMultiHFConnected(BluetoothDevice device) {
704            log("Pending state: processMultiHFConnected");
705            /* Assign the current activedevice again if the disconnected
706                         device equals to the current active device*/
707            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
708                transitionTo(mConnected);
709                int deviceSize = mConnectedDevicesList.size();
710                mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
711            } else {
712                // The disconnected device is not current active device
713                if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
714                    transitionTo(mAudioOn);
715                else
716                    transitionTo(mConnected);
717            }
718            log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
719            log("Pending state: processMultiHFConnected ,"
720                    + "fake broadcasting for mCurrentDevice");
721            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
722                    BluetoothProfile.STATE_DISCONNECTED);
723        }
724    }
725
726    private class Connected extends State {
727        @Override
728        public void enter() {
729            // Remove pending connection attempts that were deferred during the pending
730            // state. This is to prevent auto connect attempts from disconnecting
731            // devices that previously successfully connected.
732            // TODO: This needs to check for multiple HFP connections, once supported...
733            removeDeferredMessages(CONNECT);
734
735            log("Enter Connected: " + getCurrentMessage().what + ", size: "
736                    + mConnectedDevicesList.size());
737            // start phone state listener here so that the CIND response as part of SLC can be
738            // responded to, correctly.
739            // we may enter Connected from Disconnected/Pending/AudioOn. listenForPhoneState
740            // internally handles multiple calls to start listen
741            mPhoneState.listenForPhoneState(true);
742        }
743
744        @Override
745        public boolean processMessage(Message message) {
746            log("Connected process message: " + message.what + ", size: "
747                    + mConnectedDevicesList.size());
748            if (DBG) {
749                if (mConnectedDevicesList.size() == 0) {
750                    log("ERROR: mConnectedDevicesList is empty in Connected");
751                    return NOT_HANDLED;
752                }
753            }
754
755            boolean retValue = HANDLED;
756            switch (message.what) {
757                case CONNECT: {
758                    BluetoothDevice device = (BluetoothDevice) message.obj;
759                    if (device == null) {
760                        break;
761                    }
762
763                    if (mConnectedDevicesList.contains(device)) {
764                        Log.e(TAG, "ERROR: Connect received for already connected device, Ignore");
765                        break;
766                    }
767
768                    if (mConnectedDevicesList.size() >= max_hf_connections) {
769                        BluetoothDevice DisconnectConnectedDevice = null;
770                        IState CurrentAudioState = getCurrentState();
771                        Log.d(TAG, "Reach to max size, disconnect one of them first");
772                        /* TODO: Disconnect based on CoD */
773                        DisconnectConnectedDevice = mConnectedDevicesList.get(0);
774
775                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
776                                BluetoothProfile.STATE_DISCONNECTED);
777
778                        if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
779                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
780                                    BluetoothProfile.STATE_CONNECTING);
781                            break;
782                        } else {
783                            broadcastConnectionState(DisconnectConnectedDevice,
784                                    BluetoothProfile.STATE_DISCONNECTING,
785                                    BluetoothProfile.STATE_CONNECTED);
786                        }
787
788                        synchronized (HeadsetStateMachine.this) {
789                            mTargetDevice = device;
790                            if (max_hf_connections == 1) {
791                                transitionTo(mPending);
792                            } else {
793                                mMultiDisconnectDevice = DisconnectConnectedDevice;
794                                transitionTo(mMultiHFPending);
795                            }
796                            DisconnectConnectedDevice = null;
797                        }
798                    } else if (mConnectedDevicesList.size() < max_hf_connections) {
799                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
800                                BluetoothProfile.STATE_DISCONNECTED);
801                        if (!connectHfpNative(getByteAddress(device))) {
802                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
803                                    BluetoothProfile.STATE_CONNECTING);
804                            break;
805                        }
806                        synchronized (HeadsetStateMachine.this) {
807                            mTargetDevice = device;
808                            // Transtion to MultiHFPending state for Multi HF connection
809                            transitionTo(mMultiHFPending);
810                        }
811                    }
812                    Message m = obtainMessage(CONNECT_TIMEOUT);
813                    m.obj = device;
814                    sendMessageDelayed(m, 30000);
815                } break;
816                case DISCONNECT: {
817                    BluetoothDevice device = (BluetoothDevice) message.obj;
818                    if (!mConnectedDevicesList.contains(device)) {
819                        break;
820                    }
821                    broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
822                            BluetoothProfile.STATE_CONNECTED);
823                    if (!disconnectHfpNative(getByteAddress(device))) {
824                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
825                                BluetoothProfile.STATE_DISCONNECTED);
826                        break;
827                    }
828
829                    if (mConnectedDevicesList.size() > 1) {
830                        mMultiDisconnectDevice = device;
831                        transitionTo(mMultiHFPending);
832                    } else {
833                        transitionTo(mPending);
834                    }
835                } break;
836                case CONNECT_AUDIO: {
837                    BluetoothDevice device = mCurrentDevice;
838                    if (!isScoAcceptable()) {
839                        Log.w(TAG,"No Active/Held call, MO call setup, not allowing SCO");
840                        break;
841                    }
842                    // TODO(BT) when failure, broadcast audio connecting to disconnected intent
843                    //          check if device matches mCurrentDevice
844                    if (mActiveScoDevice != null) {
845                        log("connectAudioNative in Connected; mActiveScoDevice is not null");
846                        device = mActiveScoDevice;
847                    }
848                    log("connectAudioNative in Connected for device = " + device);
849                    connectAudioNative(getByteAddress(device));
850                } break;
851                case VOICE_RECOGNITION_START:
852                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
853                    break;
854                case VOICE_RECOGNITION_STOP:
855                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
856                    break;
857                case CALL_STATE_CHANGED:
858                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
859                    break;
860                case INTENT_BATTERY_CHANGED:
861                    processIntentBatteryChanged((Intent) message.obj);
862                    break;
863                case DEVICE_STATE_CHANGED:
864                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
865                    break;
866                case SEND_CCLC_RESPONSE:
867                    processSendClccResponse((HeadsetClccResponse) message.obj);
868                    break;
869                case CLCC_RSP_TIMEOUT: {
870                    BluetoothDevice device = (BluetoothDevice) message.obj;
871                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
872                } break;
873                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
874                    processSendVendorSpecificResultCode(
875                            (HeadsetVendorSpecificResultCode) message.obj);
876                    break;
877                case DIALING_OUT_TIMEOUT: {
878                    BluetoothDevice device = (BluetoothDevice) message.obj;
879                    if (mDialingOut) {
880                        mDialingOut = false;
881                        atResponseCodeNative(
882                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
883                    }
884                } break;
885                case VIRTUAL_CALL_START:
886                    initiateScoUsingVirtualVoiceCall();
887                    break;
888                case VIRTUAL_CALL_STOP:
889                    terminateScoUsingVirtualVoiceCall();
890                    break;
891                case ENABLE_WBS: {
892                    BluetoothDevice device = (BluetoothDevice) message.obj;
893                    configureWBSNative(getByteAddress(device), WBS_CODEC);
894                    break;
895                }
896                case DISABLE_WBS: {
897                    BluetoothDevice device = (BluetoothDevice) message.obj;
898                    configureWBSNative(getByteAddress(device), NBS_CODEC);
899                    break;
900                }
901                case BIND_RESPONSE: {
902                    BluetoothDevice device = (BluetoothDevice) message.obj;
903                    bindResponseNative(
904                            (int) message.arg1, message.arg2 == 1, getByteAddress(device));
905                } break;
906                case START_VR_TIMEOUT: {
907                    BluetoothDevice device = (BluetoothDevice) message.obj;
908                    if (mWaitingForVoiceRecognition) {
909                        device = (BluetoothDevice) message.obj;
910                        mWaitingForVoiceRecognition = false;
911                        Log.e(TAG, "Timeout waiting for voice recognition to start");
912                        atResponseCodeNative(
913                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
914                    }
915                } break;
916                case STACK_EVENT:
917                    StackEvent event = (StackEvent) message.obj;
918                    if (DBG) {
919                        log("event type: " + event.type + "event device : " + event.device);
920                    }
921                    switch (event.type) {
922                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
923                            processConnectionEvent(event.valueInt, event.device);
924                            break;
925                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
926                            processAudioEvent(event.valueInt, event.device);
927                            break;
928                        case EVENT_TYPE_VR_STATE_CHANGED:
929                            processVrEvent(event.valueInt, event.device);
930                            break;
931                        case EVENT_TYPE_ANSWER_CALL:
932                            // TODO(BT) could answer call happen on Connected state?
933                            processAnswerCall(event.device);
934                            break;
935                        case EVENT_TYPE_HANGUP_CALL:
936                            // TODO(BT) could hangup call happen on Connected state?
937                            processHangupCall(event.device);
938                            break;
939                        case EVENT_TYPE_VOLUME_CHANGED:
940                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
941                            break;
942                        case EVENT_TYPE_DIAL_CALL:
943                            processDialCall(event.valueString, event.device);
944                            break;
945                        case EVENT_TYPE_SEND_DTMF:
946                            processSendDtmf(event.valueInt, event.device);
947                            break;
948                        case EVENT_TYPE_NOICE_REDUCTION:
949                            processNoiceReductionEvent(event.valueInt, event.device);
950                            break;
951                        case EVENT_TYPE_WBS:
952                            Log.d(TAG, "EVENT_TYPE_WBS codec is " + event.valueInt);
953                            processWBSEvent(event.valueInt, event.device);
954                            break;
955                        case EVENT_TYPE_AT_CHLD:
956                            processAtChld(event.valueInt, event.device);
957                            break;
958                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
959                            processSubscriberNumberRequest(event.device);
960                            break;
961                        case EVENT_TYPE_AT_CIND:
962                            processAtCind(event.device);
963                            break;
964                        case EVENT_TYPE_AT_COPS:
965                            processAtCops(event.device);
966                            break;
967                        case EVENT_TYPE_AT_CLCC:
968                            processAtClcc(event.device);
969                            break;
970                        case EVENT_TYPE_UNKNOWN_AT:
971                            processUnknownAt(event.valueString, event.device);
972                            break;
973                        case EVENT_TYPE_KEY_PRESSED:
974                            processKeyPressed(event.device);
975                            break;
976                        case EVENT_TYPE_BIND:
977                            processAtBind(event.valueString, event.device);
978                            break;
979                        case EVENT_TYPE_BIEV:
980                            processAtBiev(event.valueInt, event.valueInt2, event.device);
981                            break;
982                        default:
983                            Log.e(TAG, "Unknown stack event: " + event.type);
984                            break;
985                    }
986                    break;
987                default:
988                    return NOT_HANDLED;
989            }
990            return retValue;
991        }
992
993        // in Connected state
994        private void processConnectionEvent(int state, BluetoothDevice device) {
995            Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
996            switch (state) {
997                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
998                    if (mConnectedDevicesList.contains(device)) {
999                        processWBSEvent(0, device); /* disable WBS audio parameters */
1000                        synchronized (HeadsetStateMachine.this) {
1001                            mConnectedDevicesList.remove(device);
1002                            mHeadsetAudioParam.remove(device);
1003                            mHeadsetBrsf.remove(device);
1004                            Log.d(TAG, "device " + device.getAddress()
1005                                            + " is removed in Connected state");
1006
1007                            if (mConnectedDevicesList.size() == 0) {
1008                                mCurrentDevice = null;
1009                                transitionTo(mDisconnected);
1010                            } else {
1011                                processMultiHFConnected(device);
1012                            }
1013                        }
1014                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1015                                BluetoothProfile.STATE_CONNECTED);
1016                    } else {
1017                        Log.e(TAG, "Disconnected from unknown device: " + device);
1018                    }
1019                    break;
1020                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1021                    processSlcConnected();
1022                    break;
1023                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1024                    if (mConnectedDevicesList.contains(device)) {
1025                        mIncomingDevice = null;
1026                        mTargetDevice = null;
1027                        break;
1028                    }
1029                    Log.w(TAG, "HFP to be Connected in Connected state");
1030                    if (okToConnect(device)
1031                            && (mConnectedDevicesList.size() < max_hf_connections)) {
1032                        Log.i(TAG, "Incoming Hf accepted");
1033                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1034                                BluetoothProfile.STATE_DISCONNECTED);
1035                        synchronized (HeadsetStateMachine.this) {
1036                            if (!mConnectedDevicesList.contains(device)) {
1037                                mCurrentDevice = device;
1038                                mConnectedDevicesList.add(device);
1039                                Log.d(TAG, "device " + device.getAddress()
1040                                                + " is added in Connected state");
1041                            }
1042                            transitionTo(mConnected);
1043                        }
1044                        configAudioParameters(device);
1045                    } else {
1046                        // reject the connection and stay in Connected state itself
1047                        Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
1048                                        + " bondState=" + device.getBondState());
1049                        disconnectHfpNative(getByteAddress(device));
1050                    }
1051                    break;
1052                default:
1053                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1054                    break;
1055            }
1056        }
1057
1058        // in Connected state
1059        private void processAudioEvent(int state, BluetoothDevice device) {
1060            if (!mConnectedDevicesList.contains(device)) {
1061                Log.e(TAG, "Audio changed on disconnected device: " + device);
1062                return;
1063            }
1064
1065            switch (state) {
1066                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1067                    if (!isScoAcceptable()) {
1068                        Log.e(TAG, "Audio Connected without any listener");
1069                        disconnectAudioNative(getByteAddress(device));
1070                        break;
1071                    }
1072
1073                    // TODO(BT) should I save the state for next broadcast as the prevState?
1074                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1075                    setAudioParameters(device); /*Set proper Audio Paramters.*/
1076                    mAudioManager.setBluetoothScoOn(true);
1077                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1078                            BluetoothHeadset.STATE_AUDIO_CONNECTING);
1079                    mActiveScoDevice = device;
1080                    transitionTo(mAudioOn);
1081                    break;
1082                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1083                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1084                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1085                            BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1086                    break;
1087                // TODO(BT) process other states
1088                default:
1089                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1090                    break;
1091            }
1092        }
1093
1094        private void processSlcConnected() {
1095            if (mPhoneProxy != null) {
1096                try {
1097                    mPhoneProxy.queryPhoneState();
1098                } catch (RemoteException e) {
1099                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1100                }
1101            } else {
1102                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1103            }
1104        }
1105
1106        private void processMultiHFConnected(BluetoothDevice device) {
1107            log("Connect state: processMultiHFConnected");
1108            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1109                log("mActiveScoDevice is disconnected, setting it to null");
1110                mActiveScoDevice = null;
1111            }
1112            /* Assign the current activedevice again if the disconnected
1113                         device equals to the current active device */
1114            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1115                transitionTo(mConnected);
1116                int deviceSize = mConnectedDevicesList.size();
1117                mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1118            } else {
1119                // The disconnected device is not current active device
1120                transitionTo(mConnected);
1121            }
1122            log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1123            log("Connect state: processMultiHFConnected ,"
1124                    + "fake broadcasting for mCurrentDevice");
1125            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1126                    BluetoothProfile.STATE_DISCONNECTED);
1127        }
1128    }
1129
1130    private class AudioOn extends State {
1131        @Override
1132        public void enter() {
1133            log("Enter AudioOn: " + getCurrentMessage().what + ", size: "
1134                    + mConnectedDevicesList.size());
1135        }
1136
1137        @Override
1138        public boolean processMessage(Message message) {
1139            log("AudioOn process message: " + message.what + ", size: "
1140                    + mConnectedDevicesList.size());
1141            if (DBG) {
1142                if (mConnectedDevicesList.size() == 0) {
1143                    log("ERROR: mConnectedDevicesList is empty in AudioOn");
1144                    return NOT_HANDLED;
1145                }
1146            }
1147
1148            boolean retValue = HANDLED;
1149            switch (message.what) {
1150                case CONNECT: {
1151                    BluetoothDevice device = (BluetoothDevice) message.obj;
1152                    if (device == null) {
1153                        break;
1154                    }
1155
1156                    if (mConnectedDevicesList.contains(device)) {
1157                        break;
1158                    }
1159
1160                    if (max_hf_connections == 1) {
1161                        deferMessage(obtainMessage(DISCONNECT, mCurrentDevice));
1162                        deferMessage(obtainMessage(CONNECT, device));
1163                        if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1164                            Log.d(TAG, "Disconnecting SCO audio for device = " + mCurrentDevice);
1165                        } else {
1166                            Log.e(TAG, "disconnectAudioNative failed");
1167                        }
1168                        break;
1169                    }
1170
1171                    if (mConnectedDevicesList.size() >= max_hf_connections) {
1172                        BluetoothDevice DisconnectConnectedDevice = null;
1173                        IState CurrentAudioState = getCurrentState();
1174                        Log.d(TAG, "Reach to max size, disconnect "
1175                                        + "one of them first");
1176                        DisconnectConnectedDevice = mConnectedDevicesList.get(0);
1177
1178                        if (mActiveScoDevice.equals(DisconnectConnectedDevice)) {
1179                            DisconnectConnectedDevice = mConnectedDevicesList.get(1);
1180                        }
1181
1182                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1183                                BluetoothProfile.STATE_DISCONNECTED);
1184
1185                        if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
1186                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1187                                    BluetoothProfile.STATE_CONNECTING);
1188                            break;
1189                        } else {
1190                            broadcastConnectionState(DisconnectConnectedDevice,
1191                                    BluetoothProfile.STATE_DISCONNECTING,
1192                                    BluetoothProfile.STATE_CONNECTED);
1193                        }
1194
1195                        synchronized (HeadsetStateMachine.this) {
1196                            mTargetDevice = device;
1197                            mMultiDisconnectDevice = DisconnectConnectedDevice;
1198                            transitionTo(mMultiHFPending);
1199                            DisconnectConnectedDevice = null;
1200                        }
1201                    } else if (mConnectedDevicesList.size() < max_hf_connections) {
1202                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1203                                BluetoothProfile.STATE_DISCONNECTED);
1204                        if (!connectHfpNative(getByteAddress(device))) {
1205                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1206                                    BluetoothProfile.STATE_CONNECTING);
1207                            break;
1208                        }
1209                        synchronized (HeadsetStateMachine.this) {
1210                            mTargetDevice = device;
1211                            // Transtion to MultilHFPending state for Multi handsfree connection
1212                            transitionTo(mMultiHFPending);
1213                        }
1214                    }
1215                    Message m = obtainMessage(CONNECT_TIMEOUT);
1216                    m.obj = device;
1217                    sendMessageDelayed(m, 30000);
1218                } break;
1219                case CONNECT_TIMEOUT:
1220                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1221                            getByteAddress(mTargetDevice));
1222                    break;
1223                case DISCONNECT: {
1224                    BluetoothDevice device = (BluetoothDevice) message.obj;
1225                    if (!mConnectedDevicesList.contains(device)) {
1226                        break;
1227                    }
1228                    if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1229                        // The disconnected device is active SCO device
1230                        Log.d(TAG, "AudioOn, the disconnected device"
1231                                        + "is active SCO device");
1232                        deferMessage(obtainMessage(DISCONNECT, message.obj));
1233                        // Disconnect BT SCO first
1234                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1235                            log("Disconnecting SCO audio");
1236                        } else {
1237                            // if disconnect BT SCO failed, transition to mConnected state
1238                            transitionTo(mConnected);
1239                        }
1240                    } else {
1241                        /* Do not disconnect BT SCO if the disconnected
1242                           device is not active SCO device */
1243                        Log.d(TAG, "AudioOn, the disconnected device"
1244                                        + "is not active SCO device");
1245                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
1246                                BluetoothProfile.STATE_CONNECTED);
1247                        // Should be still in AudioOn state
1248                        if (!disconnectHfpNative(getByteAddress(device))) {
1249                            Log.w(TAG, "AudioOn, disconnect device failed");
1250                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1251                                    BluetoothProfile.STATE_DISCONNECTING);
1252                            break;
1253                        }
1254                        /* Transtion to MultiHFPending state for Multi
1255                           handsfree connection */
1256                        if (mConnectedDevicesList.size() > 1) {
1257                            mMultiDisconnectDevice = device;
1258                            transitionTo(mMultiHFPending);
1259                        }
1260                    }
1261                } break;
1262                case DISCONNECT_AUDIO:
1263                    if (mActiveScoDevice != null) {
1264                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1265                            log("Disconnecting SCO audio for device = " + mActiveScoDevice);
1266                        } else {
1267                            Log.e(TAG, "disconnectAudioNative failed"
1268                                            + "for device = " + mActiveScoDevice);
1269                        }
1270                    }
1271                    break;
1272                case VOICE_RECOGNITION_START:
1273                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1274                    break;
1275                case VOICE_RECOGNITION_STOP:
1276                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1277                    break;
1278                case INTENT_SCO_VOLUME_CHANGED:
1279                    if (mActiveScoDevice != null) {
1280                        processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1281                    }
1282                    break;
1283                case CALL_STATE_CHANGED:
1284                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
1285                    break;
1286                case INTENT_BATTERY_CHANGED:
1287                    processIntentBatteryChanged((Intent) message.obj);
1288                    break;
1289                case DEVICE_STATE_CHANGED:
1290                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1291                    break;
1292                case SEND_CCLC_RESPONSE:
1293                    processSendClccResponse((HeadsetClccResponse) message.obj);
1294                    break;
1295                case CLCC_RSP_TIMEOUT: {
1296                    BluetoothDevice device = (BluetoothDevice) message.obj;
1297                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1298                    break;
1299                }
1300                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
1301                    processSendVendorSpecificResultCode(
1302                            (HeadsetVendorSpecificResultCode) message.obj);
1303                    break;
1304
1305                case VIRTUAL_CALL_START:
1306                    initiateScoUsingVirtualVoiceCall();
1307                    break;
1308                case VIRTUAL_CALL_STOP:
1309                    terminateScoUsingVirtualVoiceCall();
1310                    break;
1311
1312                case DIALING_OUT_TIMEOUT: {
1313                    if (mDialingOut) {
1314                        BluetoothDevice device = (BluetoothDevice) message.obj;
1315                        mDialingOut = false;
1316                        atResponseCodeNative(
1317                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1318                    }
1319                    break;
1320                }
1321                case START_VR_TIMEOUT: {
1322                    if (mWaitingForVoiceRecognition) {
1323                        BluetoothDevice device = (BluetoothDevice) message.obj;
1324                        mWaitingForVoiceRecognition = false;
1325                        Log.e(TAG, "Timeout waiting for voice recognition"
1326                                        + "to start");
1327                        atResponseCodeNative(
1328                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1329                    }
1330                    break;
1331                }
1332                case STACK_EVENT:
1333                    StackEvent event = (StackEvent) message.obj;
1334                    if (DBG) {
1335                        log("event type: " + event.type);
1336                    }
1337                    switch (event.type) {
1338                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1339                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1340                            if (device1 != null && device1.equals(event.device)) {
1341                                Log.d(TAG, "remove connect timeout for device = " + device1);
1342                                removeMessages(CONNECT_TIMEOUT);
1343                            }
1344                            processConnectionEvent(event.valueInt, event.device);
1345                            break;
1346                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1347                            processAudioEvent(event.valueInt, event.device);
1348                            break;
1349                        case EVENT_TYPE_VR_STATE_CHANGED:
1350                            processVrEvent(event.valueInt, event.device);
1351                            break;
1352                        case EVENT_TYPE_ANSWER_CALL:
1353                            processAnswerCall(event.device);
1354                            break;
1355                        case EVENT_TYPE_HANGUP_CALL:
1356                            processHangupCall(event.device);
1357                            break;
1358                        case EVENT_TYPE_VOLUME_CHANGED:
1359                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
1360                            break;
1361                        case EVENT_TYPE_DIAL_CALL:
1362                            processDialCall(event.valueString, event.device);
1363                            break;
1364                        case EVENT_TYPE_SEND_DTMF:
1365                            processSendDtmf(event.valueInt, event.device);
1366                            break;
1367                        case EVENT_TYPE_NOICE_REDUCTION:
1368                            processNoiceReductionEvent(event.valueInt, event.device);
1369                            break;
1370                        case EVENT_TYPE_AT_CHLD:
1371                            processAtChld(event.valueInt, event.device);
1372                            break;
1373                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1374                            processSubscriberNumberRequest(event.device);
1375                            break;
1376                        case EVENT_TYPE_AT_CIND:
1377                            processAtCind(event.device);
1378                            break;
1379                        case EVENT_TYPE_AT_COPS:
1380                            processAtCops(event.device);
1381                            break;
1382                        case EVENT_TYPE_AT_CLCC:
1383                            processAtClcc(event.device);
1384                            break;
1385                        case EVENT_TYPE_UNKNOWN_AT:
1386                            processUnknownAt(event.valueString, event.device);
1387                            break;
1388                        case EVENT_TYPE_KEY_PRESSED:
1389                            processKeyPressed(event.device);
1390                            break;
1391                        case EVENT_TYPE_BIND:
1392                            processAtBind(event.valueString, event.device);
1393                            break;
1394                        case EVENT_TYPE_BIEV:
1395                            processAtBiev(event.valueInt, event.valueInt2, event.device);
1396                            break;
1397                        default:
1398                            Log.e(TAG, "Unknown stack event: " + event.type);
1399                            break;
1400                    }
1401                    break;
1402                default:
1403                    return NOT_HANDLED;
1404            }
1405            return retValue;
1406        }
1407
1408        // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
1409        private void processConnectionEvent(int state, BluetoothDevice device) {
1410            Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
1411            switch (state) {
1412                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1413                    if (mConnectedDevicesList.contains(device)) {
1414                        if (mActiveScoDevice != null && mActiveScoDevice.equals(device)
1415                                && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1416                            processAudioEvent(HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
1417                        }
1418
1419                        synchronized (HeadsetStateMachine.this) {
1420                            mConnectedDevicesList.remove(device);
1421                            mHeadsetAudioParam.remove(device);
1422                            mHeadsetBrsf.remove(device);
1423                            Log.d(TAG, "device " + device.getAddress()
1424                                            + " is removed in AudioOn state");
1425                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1426                                    BluetoothProfile.STATE_CONNECTED);
1427                            processWBSEvent(0, device); /* disable WBS audio parameters */
1428                            if (mConnectedDevicesList.size() == 0) {
1429                                transitionTo(mDisconnected);
1430                            } else {
1431                                processMultiHFConnected(device);
1432                            }
1433                        }
1434                    } else {
1435                        Log.e(TAG, "Disconnected from unknown device: " + device);
1436                    }
1437                    break;
1438                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1439                    processSlcConnected();
1440                    break;
1441                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1442                    if (mConnectedDevicesList.contains(device)) {
1443                        mIncomingDevice = null;
1444                        mTargetDevice = null;
1445                        break;
1446                    }
1447                    Log.w(TAG, "HFP to be Connected in AudioOn state");
1448                    if (okToConnect(device)
1449                            && (mConnectedDevicesList.size() < max_hf_connections)) {
1450                        Log.i(TAG, "Incoming Hf accepted");
1451                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1452                                BluetoothProfile.STATE_DISCONNECTED);
1453                        synchronized (HeadsetStateMachine.this) {
1454                            if (!mConnectedDevicesList.contains(device)) {
1455                                mCurrentDevice = device;
1456                                mConnectedDevicesList.add(device);
1457                                Log.d(TAG, "device " + device.getAddress()
1458                                                + " is added in AudioOn state");
1459                            }
1460                        }
1461                        configAudioParameters(device);
1462                    } else {
1463                        // reject the connection and stay in Connected state itself
1464                        Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
1465                                        + " bondState=" + device.getBondState());
1466                        disconnectHfpNative(getByteAddress(device));
1467                    }
1468                    break;
1469                default:
1470                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1471                    break;
1472            }
1473        }
1474
1475        // in AudioOn state
1476        private void processAudioEvent(int state, BluetoothDevice device) {
1477            if (!mConnectedDevicesList.contains(device)) {
1478                Log.e(TAG, "Audio changed on disconnected device: " + device);
1479                return;
1480            }
1481
1482            switch (state) {
1483                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1484                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1485                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1486                        mAudioManager.setBluetoothScoOn(false);
1487                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1488                                BluetoothHeadset.STATE_AUDIO_CONNECTED);
1489                    }
1490                    transitionTo(mConnected);
1491                    break;
1492                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1493                    // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
1494                    // broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
1495                    //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
1496                    break;
1497                default:
1498                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1499                    break;
1500            }
1501        }
1502
1503        private void processSlcConnected() {
1504            if (mPhoneProxy != null) {
1505                try {
1506                    mPhoneProxy.queryPhoneState();
1507                } catch (RemoteException e) {
1508                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1509                }
1510            } else {
1511                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1512            }
1513        }
1514
1515        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1516            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1517            if (mPhoneState.getSpeakerVolume() != volumeValue) {
1518                mPhoneState.setSpeakerVolume(volumeValue);
1519                setVolumeNative(
1520                        HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device));
1521            }
1522        }
1523
1524        private void processMultiHFConnected(BluetoothDevice device) {
1525            log("AudioOn state: processMultiHFConnected");
1526            /* Assign the current activedevice again if the disconnected
1527                          device equals to the current active device */
1528            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1529                int deviceSize = mConnectedDevicesList.size();
1530                mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1531            }
1532            if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED) transitionTo(mConnected);
1533
1534            log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1535            log("AudioOn state: processMultiHFConnected ,"
1536                    + "fake broadcasting for mCurrentDevice");
1537            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1538                    BluetoothProfile.STATE_DISCONNECTED);
1539        }
1540    }
1541
1542    /* Add MultiHFPending state when atleast 1 HS is connected
1543            and disconnect/connect new HS */
1544    private class MultiHFPending extends State {
1545        @Override
1546        public void enter() {
1547            log("Enter MultiHFPending: " + getCurrentMessage().what + ", size: "
1548                    + mConnectedDevicesList.size());
1549        }
1550
1551        @Override
1552        public boolean processMessage(Message message) {
1553            log("MultiHFPending process message: " + message.what + ", size: "
1554                    + mConnectedDevicesList.size());
1555
1556            boolean retValue = HANDLED;
1557            switch (message.what) {
1558                case CONNECT:
1559                    deferMessage(message);
1560                    break;
1561
1562                case CONNECT_AUDIO:
1563                    if (mCurrentDevice != null) {
1564                        connectAudioNative(getByteAddress(mCurrentDevice));
1565                    }
1566                    break;
1567                case CONNECT_TIMEOUT:
1568                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1569                            getByteAddress(mTargetDevice));
1570                    break;
1571
1572                case DISCONNECT_AUDIO:
1573                    if (mActiveScoDevice != null) {
1574                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1575                            Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for "
1576                                            + mActiveScoDevice);
1577                        } else {
1578                            Log.e(TAG, "disconnectAudioNative failed"
1579                                            + "for device = " + mActiveScoDevice);
1580                        }
1581                    }
1582                    break;
1583                case DISCONNECT:
1584                    BluetoothDevice device = (BluetoothDevice) message.obj;
1585                    if (mConnectedDevicesList.contains(device) && mTargetDevice != null
1586                            && mTargetDevice.equals(device)) {
1587                        // cancel connection to the mTargetDevice
1588                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1589                                BluetoothProfile.STATE_CONNECTING);
1590                        synchronized (HeadsetStateMachine.this) {
1591                            mTargetDevice = null;
1592                        }
1593                    } else {
1594                        deferMessage(message);
1595                    }
1596                    break;
1597                case VOICE_RECOGNITION_START:
1598                    device = (BluetoothDevice) message.obj;
1599                    if (mConnectedDevicesList.contains(device)) {
1600                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1601                    }
1602                    break;
1603                case VOICE_RECOGNITION_STOP:
1604                    device = (BluetoothDevice) message.obj;
1605                    if (mConnectedDevicesList.contains(device)) {
1606                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1607                    }
1608                    break;
1609                case INTENT_SCO_VOLUME_CHANGED:
1610                    if (mActiveScoDevice != null) {
1611                        processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1612                    }
1613                    break;
1614                case INTENT_BATTERY_CHANGED:
1615                    processIntentBatteryChanged((Intent) message.obj);
1616                    break;
1617                case CALL_STATE_CHANGED:
1618                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
1619                    break;
1620                case DEVICE_STATE_CHANGED:
1621                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1622                    break;
1623                case SEND_CCLC_RESPONSE:
1624                    processSendClccResponse((HeadsetClccResponse) message.obj);
1625                    break;
1626                case CLCC_RSP_TIMEOUT: {
1627                    device = (BluetoothDevice) message.obj;
1628                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1629                } break;
1630                case DIALING_OUT_TIMEOUT:
1631                    if (mDialingOut) {
1632                        device = (BluetoothDevice) message.obj;
1633                        mDialingOut = false;
1634                        atResponseCodeNative(
1635                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1636                    }
1637                    break;
1638                case VIRTUAL_CALL_START:
1639                    device = (BluetoothDevice) message.obj;
1640                    if (mConnectedDevicesList.contains(device)) {
1641                        initiateScoUsingVirtualVoiceCall();
1642                    }
1643                    break;
1644                case VIRTUAL_CALL_STOP:
1645                    device = (BluetoothDevice) message.obj;
1646                    if (mConnectedDevicesList.contains(device)) {
1647                        terminateScoUsingVirtualVoiceCall();
1648                    }
1649                    break;
1650                case START_VR_TIMEOUT:
1651                    if (mWaitingForVoiceRecognition) {
1652                        device = (BluetoothDevice) message.obj;
1653                        mWaitingForVoiceRecognition = false;
1654                        Log.e(TAG, "Timeout waiting for voice"
1655                                        + "recognition to start");
1656                        atResponseCodeNative(
1657                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
1658                    }
1659                    break;
1660                case STACK_EVENT:
1661                    StackEvent event = (StackEvent) message.obj;
1662                    if (DBG) {
1663                        log("event type: " + event.type);
1664                    }
1665                    switch (event.type) {
1666                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1667                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1668                            if (device1 != null && device1.equals(event.device)) {
1669                                Log.d(TAG, "remove connect timeout for device = " + device1);
1670                                removeMessages(CONNECT_TIMEOUT);
1671                            }
1672                            processConnectionEvent(event.valueInt, event.device);
1673                            break;
1674                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1675                            processAudioEvent(event.valueInt, event.device);
1676                            break;
1677                        case EVENT_TYPE_VR_STATE_CHANGED:
1678                            processVrEvent(event.valueInt, event.device);
1679                            break;
1680                        case EVENT_TYPE_ANSWER_CALL:
1681                            // TODO(BT) could answer call happen on Connected state?
1682                            processAnswerCall(event.device);
1683                            break;
1684                        case EVENT_TYPE_HANGUP_CALL:
1685                            // TODO(BT) could hangup call happen on Connected state?
1686                            processHangupCall(event.device);
1687                            break;
1688                        case EVENT_TYPE_VOLUME_CHANGED:
1689                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
1690                            break;
1691                        case EVENT_TYPE_DIAL_CALL:
1692                            processDialCall(event.valueString, event.device);
1693                            break;
1694                        case EVENT_TYPE_SEND_DTMF:
1695                            processSendDtmf(event.valueInt, event.device);
1696                            break;
1697                        case EVENT_TYPE_NOICE_REDUCTION:
1698                            processNoiceReductionEvent(event.valueInt, event.device);
1699                            break;
1700                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1701                            processSubscriberNumberRequest(event.device);
1702                            break;
1703                        case EVENT_TYPE_AT_CIND:
1704                            processAtCind(event.device);
1705                            break;
1706                        case EVENT_TYPE_AT_CHLD:
1707                            processAtChld(event.valueInt, event.device);
1708                            break;
1709                        case EVENT_TYPE_AT_COPS:
1710                            processAtCops(event.device);
1711                            break;
1712                        case EVENT_TYPE_AT_CLCC:
1713                            processAtClcc(event.device);
1714                            break;
1715                        case EVENT_TYPE_UNKNOWN_AT:
1716                            processUnknownAt(event.valueString, event.device);
1717                            break;
1718                        case EVENT_TYPE_KEY_PRESSED:
1719                            processKeyPressed(event.device);
1720                            break;
1721                        case EVENT_TYPE_BIND:
1722                            processAtBind(event.valueString, event.device);
1723                            break;
1724                        case EVENT_TYPE_BIEV:
1725                            processAtBiev(event.valueInt, event.valueInt2, event.device);
1726                            break;
1727                        default:
1728                            Log.e(TAG, "Unexpected event: " + event.type);
1729                            break;
1730                    }
1731                    break;
1732                default:
1733                    return NOT_HANDLED;
1734            }
1735            return retValue;
1736        }
1737
1738        // in MultiHFPending state
1739        private void processConnectionEvent(int state, BluetoothDevice device) {
1740            Log.d(TAG, "processConnectionEvent state = " + state + ", device = " + device);
1741            switch (state) {
1742                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1743                    if (mConnectedDevicesList.contains(device)) {
1744                        if (mMultiDisconnectDevice != null
1745                                && mMultiDisconnectDevice.equals(device)) {
1746                            mMultiDisconnectDevice = null;
1747
1748                            synchronized (HeadsetStateMachine.this) {
1749                                mConnectedDevicesList.remove(device);
1750                                mHeadsetAudioParam.remove(device);
1751                                mHeadsetBrsf.remove(device);
1752                                Log.d(TAG, "device " + device.getAddress()
1753                                                + " is removed in MultiHFPending state");
1754                                broadcastConnectionState(device,
1755                                        BluetoothProfile.STATE_DISCONNECTED,
1756                                        BluetoothProfile.STATE_DISCONNECTING);
1757                            }
1758
1759                            if (mTargetDevice != null) {
1760                                if (!connectHfpNative(getByteAddress(mTargetDevice))) {
1761                                    broadcastConnectionState(mTargetDevice,
1762                                            BluetoothProfile.STATE_DISCONNECTED,
1763                                            BluetoothProfile.STATE_CONNECTING);
1764                                    synchronized (HeadsetStateMachine.this) {
1765                                        mTargetDevice = null;
1766                                        if (mConnectedDevicesList.size() == 0) {
1767                                            // Should be not in this state since it has at least
1768                                            // one HF connected in MultiHFPending state
1769                                            Log.d(TAG,
1770                                                    "Should be not in this state, error handling");
1771                                            transitionTo(mDisconnected);
1772                                        } else {
1773                                            processMultiHFConnected(device);
1774                                        }
1775                                    }
1776                                }
1777                            } else {
1778                                synchronized (HeadsetStateMachine.this) {
1779                                    mIncomingDevice = null;
1780                                    if (mConnectedDevicesList.size() == 0) {
1781                                        transitionTo(mDisconnected);
1782                                    } else {
1783                                        processMultiHFConnected(device);
1784                                    }
1785                                }
1786                            }
1787                        } else {
1788                            /* Another HF disconnected when one HF is connecting */
1789                            synchronized (HeadsetStateMachine.this) {
1790                                mConnectedDevicesList.remove(device);
1791                                mHeadsetAudioParam.remove(device);
1792                                mHeadsetBrsf.remove(device);
1793                                Log.d(TAG, "device " + device.getAddress()
1794                                                + " is removed in MultiHFPending state");
1795                            }
1796                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1797                                    BluetoothProfile.STATE_CONNECTED);
1798                        }
1799                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1800                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1801                                BluetoothProfile.STATE_CONNECTING);
1802                        synchronized (HeadsetStateMachine.this) {
1803                            mTargetDevice = null;
1804                            if (mConnectedDevicesList.size() == 0) {
1805                                transitionTo(mDisconnected);
1806                            } else {
1807                                if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1808                                    transitionTo(mAudioOn);
1809                                else
1810                                    transitionTo(mConnected);
1811                            }
1812                        }
1813                    } else {
1814                        Log.e(TAG, "Unknown device Disconnected: " + device);
1815                    }
1816                    break;
1817                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1818                    /* Outgoing disconnection for device failed */
1819                    if (mConnectedDevicesList.contains(device)) {
1820                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1821                                BluetoothProfile.STATE_DISCONNECTING);
1822                        if (mTargetDevice != null) {
1823                            broadcastConnectionState(mTargetDevice,
1824                                    BluetoothProfile.STATE_DISCONNECTED,
1825                                    BluetoothProfile.STATE_CONNECTING);
1826                        }
1827                        synchronized (HeadsetStateMachine.this) {
1828                            mTargetDevice = null;
1829                            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1830                                transitionTo(mAudioOn);
1831                            else
1832                                transitionTo(mConnected);
1833                        }
1834                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1835                        synchronized (HeadsetStateMachine.this) {
1836                            mCurrentDevice = device;
1837                            mConnectedDevicesList.add(device);
1838                            Log.d(TAG, "device " + device.getAddress()
1839                                            + " is added in MultiHFPending state");
1840                            mTargetDevice = null;
1841                            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1842                                transitionTo(mAudioOn);
1843                            else
1844                                transitionTo(mConnected);
1845                        }
1846
1847                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1848                                BluetoothProfile.STATE_CONNECTING);
1849                        configAudioParameters(device);
1850                    } else {
1851                        Log.w(TAG, "Some other incoming HF connected"
1852                                        + "in Multi Pending state");
1853                        if (okToConnect(device)
1854                                && (mConnectedDevicesList.size() < max_hf_connections)) {
1855                            Log.i(TAG, "Incoming Hf accepted");
1856                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1857                                    BluetoothProfile.STATE_DISCONNECTED);
1858                            synchronized (HeadsetStateMachine.this) {
1859                                if (!mConnectedDevicesList.contains(device)) {
1860                                    mCurrentDevice = device;
1861                                    mConnectedDevicesList.add(device);
1862                                    Log.d(TAG, "device " + device.getAddress()
1863                                                    + " is added in MultiHFPending state");
1864                                }
1865                            }
1866                            configAudioParameters(device);
1867                        } else {
1868                            // reject the connection and stay in Pending state itself
1869                            Log.i(TAG, "Incoming Hf rejected. priority="
1870                                            + mService.getPriority(device) + " bondState="
1871                                            + device.getBondState());
1872                            disconnectHfpNative(getByteAddress(device));
1873                        }
1874                    }
1875                    break;
1876                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1877                    processSlcConnected();
1878                    break;
1879                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
1880                    if (mConnectedDevicesList.contains(device)) {
1881                        Log.e(TAG, "current device tries to connect back");
1882                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1883                        if (DBG) {
1884                            log("Stack and target device are connecting");
1885                        }
1886                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1887                        Log.e(TAG, "Another connecting event on"
1888                                        + "the incoming device");
1889                    }
1890                    break;
1891                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
1892                    if (mConnectedDevicesList.contains(device)) {
1893                        if (DBG) {
1894                            log("stack is disconnecting mCurrentDevice");
1895                        }
1896                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1897                        Log.e(TAG, "TargetDevice is getting disconnected");
1898                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1899                        Log.e(TAG, "IncomingDevice is getting disconnected");
1900                    } else {
1901                        Log.e(TAG, "Disconnecting unknow device: " + device);
1902                    }
1903                    break;
1904                default:
1905                    Log.e(TAG, "Incorrect state: " + state);
1906                    break;
1907            }
1908        }
1909
1910        private void processAudioEvent(int state, BluetoothDevice device) {
1911            if (!mConnectedDevicesList.contains(device)) {
1912                Log.e(TAG, "Audio changed on disconnected device: " + device);
1913                return;
1914            }
1915
1916            switch (state) {
1917                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1918                    if (!isScoAcceptable()) {
1919                        Log.e(TAG, "Audio Connected without any listener");
1920                        disconnectAudioNative(getByteAddress(device));
1921                        break;
1922                    }
1923                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1924                    setAudioParameters(device); /* Set proper Audio Parameters. */
1925                    mAudioManager.setBluetoothScoOn(true);
1926                    mActiveScoDevice = device;
1927                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1928                            BluetoothHeadset.STATE_AUDIO_CONNECTING);
1929                    /* The state should be still in MultiHFPending state when
1930                       audio connected since other device is still connecting/
1931                       disconnecting */
1932                    break;
1933                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1934                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1935                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1936                            BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1937                    break;
1938                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1939                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1940                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1941                        mAudioManager.setBluetoothScoOn(false);
1942                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1943                                BluetoothHeadset.STATE_AUDIO_CONNECTED);
1944                    }
1945                    /* The state should be still in MultiHFPending state when audio
1946                       disconnected since other device is still connecting/
1947                       disconnecting */
1948                    break;
1949
1950                default:
1951                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1952                    break;
1953            }
1954        }
1955
1956        private void processSlcConnected() {
1957            if (mPhoneProxy != null) {
1958                try {
1959                    mPhoneProxy.queryPhoneState();
1960                } catch (RemoteException e) {
1961                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1962                }
1963            } else {
1964                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1965            }
1966        }
1967
1968        private void processMultiHFConnected(BluetoothDevice device) {
1969            log("MultiHFPending state: processMultiHFConnected");
1970            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1971                log("mActiveScoDevice is disconnected, setting it to null");
1972                mActiveScoDevice = null;
1973            }
1974            /* Assign the current activedevice again if the disconnected
1975               device equals to the current active device */
1976            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1977                int deviceSize = mConnectedDevicesList.size();
1978                mCurrentDevice = mConnectedDevicesList.get(deviceSize - 1);
1979            }
1980            // The disconnected device is not current active device
1981            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1982                transitionTo(mAudioOn);
1983            else
1984                transitionTo(mConnected);
1985            log("processMultiHFConnected , the latest mCurrentDevice is:" + mCurrentDevice);
1986            log("MultiHFPending state: processMultiHFConnected ,"
1987                    + "fake broadcasting for mCurrentDevice");
1988            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1989                    BluetoothProfile.STATE_DISCONNECTED);
1990        }
1991
1992        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1993            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1994            if (mPhoneState.getSpeakerVolume() != volumeValue) {
1995                mPhoneState.setSpeakerVolume(volumeValue);
1996                setVolumeNative(
1997                        HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue, getByteAddress(device));
1998            }
1999        }
2000    }
2001
2002    private ServiceConnection mConnection = new ServiceConnection() {
2003        public void onServiceConnected(ComponentName className, IBinder service) {
2004            if (DBG) Log.d(TAG, "Proxy object connected");
2005            mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
2006        }
2007
2008        public void onServiceDisconnected(ComponentName className) {
2009            if (DBG) Log.d(TAG, "Proxy object disconnected");
2010            mPhoneProxy = null;
2011        }
2012    };
2013
2014    // HFP Connection state of the device could be changed by the state machine
2015    // in separate thread while this method is executing.
2016    int getConnectionState(BluetoothDevice device) {
2017        if (getCurrentState() == mDisconnected) {
2018            if (DBG) Log.d(TAG, "currentState is Disconnected");
2019            return BluetoothProfile.STATE_DISCONNECTED;
2020        }
2021
2022        synchronized (this) {
2023            IState currentState = getCurrentState();
2024            if (DBG) Log.d(TAG, "currentState = " + currentState);
2025            if (currentState == mPending) {
2026                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2027                    return BluetoothProfile.STATE_CONNECTING;
2028                }
2029                if (mConnectedDevicesList.contains(device)) {
2030                    return BluetoothProfile.STATE_DISCONNECTING;
2031                }
2032                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2033                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2034                }
2035                return BluetoothProfile.STATE_DISCONNECTED;
2036            }
2037
2038            if (currentState == mMultiHFPending) {
2039                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2040                    return BluetoothProfile.STATE_CONNECTING;
2041                }
2042                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2043                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2044                }
2045                if (mConnectedDevicesList.contains(device)) {
2046                    if ((mMultiDisconnectDevice != null)
2047                            && (!mMultiDisconnectDevice.equals(device))) {
2048                        // The device is still connected
2049                        return BluetoothProfile.STATE_CONNECTED;
2050                    }
2051                    return BluetoothProfile.STATE_DISCONNECTING;
2052                }
2053                return BluetoothProfile.STATE_DISCONNECTED;
2054            }
2055
2056            if (currentState == mConnected || currentState == mAudioOn) {
2057                if (mConnectedDevicesList.contains(device)) {
2058                    return BluetoothProfile.STATE_CONNECTED;
2059                }
2060                return BluetoothProfile.STATE_DISCONNECTED;
2061            } else {
2062                Log.e(TAG, "Bad currentState: " + currentState);
2063                return BluetoothProfile.STATE_DISCONNECTED;
2064            }
2065        }
2066    }
2067
2068    List<BluetoothDevice> getConnectedDevices() {
2069        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2070        synchronized (this) {
2071            for (int i = 0; i < mConnectedDevicesList.size(); i++)
2072                devices.add(mConnectedDevicesList.get(i));
2073        }
2074
2075        return devices;
2076    }
2077
2078    boolean isAudioOn() {
2079        return (getCurrentState() == mAudioOn);
2080    }
2081
2082    boolean isAudioConnected(BluetoothDevice device) {
2083        synchronized (this) {
2084            /*  Additional check for audio state included for the case when PhoneApp queries
2085            Bluetooth Audio state, before we receive the close event from the stack for the
2086            sco disconnect issued in AudioOn state. This was causing a mismatch in the
2087            Incall screen UI. */
2088
2089            if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
2090                    && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2091                return true;
2092            }
2093        }
2094        return false;
2095    }
2096
2097    public void setAudioRouteAllowed(boolean allowed) {
2098        mAudioRouteAllowed = allowed;
2099    }
2100
2101    public boolean getAudioRouteAllowed() {
2102        return mAudioRouteAllowed;
2103    }
2104
2105    int getAudioState(BluetoothDevice device) {
2106        synchronized (this) {
2107            if (mConnectedDevicesList.size() == 0) {
2108                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
2109            }
2110        }
2111        return mAudioState;
2112    }
2113
2114    private void processVrEvent(int state, BluetoothDevice device) {
2115        if (device == null) {
2116            Log.w(TAG, "processVrEvent device is null");
2117            return;
2118        }
2119        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: "
2120                        + mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: "
2121                        + mWaitingForVoiceRecognition + " isInCall: " + isInCall());
2122        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2123            if (!isVirtualCallInProgress() && !isInCall()) {
2124                IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
2125                        ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
2126                if (dic != null) {
2127                    try {
2128                        dic.exitIdle("voice-command");
2129                    } catch (RemoteException e) {
2130                    }
2131                }
2132                try {
2133                    mService.startActivity(sVoiceCommandIntent);
2134                } catch (ActivityNotFoundException e) {
2135                    atResponseCodeNative(
2136                            HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2137                    return;
2138                }
2139                expectVoiceRecognition(device);
2140            } else {
2141                // send error response if call is ongoing
2142                atResponseCodeNative(
2143                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2144                return;
2145            }
2146        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
2147            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
2148                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2149                mVoiceRecognitionStarted = false;
2150                mWaitingForVoiceRecognition = false;
2151                if (!isInCall() && (mActiveScoDevice != null)) {
2152                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2153                    mAudioManager.setParameters("A2dpSuspended=false");
2154                }
2155            } else {
2156                atResponseCodeNative(
2157                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2158            }
2159        } else {
2160            Log.e(TAG, "Bad Voice Recognition state: " + state);
2161        }
2162    }
2163
2164    private void processLocalVrEvent(int state) {
2165        BluetoothDevice device = null;
2166        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2167            boolean needAudio = true;
2168            if (mVoiceRecognitionStarted || isInCall()) {
2169                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall()
2170                                + " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
2171                return;
2172            }
2173            mVoiceRecognitionStarted = true;
2174
2175            if (mWaitingForVoiceRecognition) {
2176                device = getDeviceForMessage(START_VR_TIMEOUT);
2177                if (device == null) return;
2178
2179                Log.d(TAG, "Voice recognition started successfully");
2180                mWaitingForVoiceRecognition = false;
2181                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2182                removeMessages(START_VR_TIMEOUT);
2183            } else {
2184                Log.d(TAG, "Voice recognition started locally");
2185                needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice));
2186                if (mCurrentDevice != null) device = mCurrentDevice;
2187            }
2188
2189            if (needAudio && !isAudioOn()) {
2190                Log.d(TAG, "Initiating audio connection for Voice Recognition");
2191                // At this stage, we need to be sure that AVDTP is not streaming. This is needed
2192                // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
2193                // streaming state while a SCO connection is established.
2194                // This is needed for VoiceDial scenario alone and not for
2195                // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
2196                // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
2197                // Whereas for VoiceDial we want to activate the SCO connection but we are still
2198                // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
2199                mAudioManager.setParameters("A2dpSuspended=true");
2200                if (device != null) {
2201                    connectAudioNative(getByteAddress(device));
2202                } else {
2203                    Log.e(TAG, "device not found for VR");
2204                }
2205            }
2206
2207            if (mStartVoiceRecognitionWakeLock.isHeld()) {
2208                mStartVoiceRecognitionWakeLock.release();
2209            }
2210        } else {
2211            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: "
2212                            + mVoiceRecognitionStarted + " mWaitingForVoiceRecognition: "
2213                            + mWaitingForVoiceRecognition);
2214            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
2215                mVoiceRecognitionStarted = false;
2216                mWaitingForVoiceRecognition = false;
2217
2218                if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice)) && !isInCall()
2219                        && mActiveScoDevice != null) {
2220                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2221                    mAudioManager.setParameters("A2dpSuspended=false");
2222                }
2223            }
2224        }
2225    }
2226
2227    private synchronized void expectVoiceRecognition(BluetoothDevice device) {
2228        mWaitingForVoiceRecognition = true;
2229        Message m = obtainMessage(START_VR_TIMEOUT);
2230        m.obj = getMatchingDevice(device);
2231        sendMessageDelayed(m, START_VR_TIMEOUT_VALUE);
2232
2233        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
2234            mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
2235        }
2236    }
2237
2238    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2239        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
2240        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
2241        int connectionState;
2242        synchronized (this) {
2243            for (BluetoothDevice device : bondedDevices) {
2244                ParcelUuid[] featureUuids = device.getUuids();
2245                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
2246                    continue;
2247                }
2248                connectionState = getConnectionState(device);
2249                for (int i = 0; i < states.length; i++) {
2250                    if (connectionState == states[i]) {
2251                        deviceList.add(device);
2252                    }
2253                }
2254            }
2255        }
2256        return deviceList;
2257    }
2258
2259    private BluetoothDevice getDeviceForMessage(int what) {
2260        if (what == CONNECT_TIMEOUT) {
2261            log("getDeviceForMessage: returning mTargetDevice for what=" + what);
2262            return mTargetDevice;
2263        }
2264        if (mConnectedDevicesList.size() == 0) {
2265            log("getDeviceForMessage: No connected device. what=" + what);
2266            return null;
2267        }
2268        for (BluetoothDevice device : mConnectedDevicesList) {
2269            if (getHandler().hasMessages(what, device)) {
2270                log("getDeviceForMessage: returning " + device);
2271                return device;
2272            }
2273        }
2274        log("getDeviceForMessage: No matching device for " + what + ". Returning null");
2275        return null;
2276    }
2277
2278    private BluetoothDevice getMatchingDevice(BluetoothDevice device) {
2279        for (BluetoothDevice matchingDevice : mConnectedDevicesList) {
2280            if (matchingDevice.equals(device)) {
2281                return matchingDevice;
2282            }
2283        }
2284        return null;
2285    }
2286
2287    // This method does not check for error conditon (newState == prevState)
2288    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
2289        log("Connection state " + device + ": " + prevState + "->" + newState);
2290        if (prevState == BluetoothProfile.STATE_CONNECTED) {
2291            // Headset is disconnecting, stop Virtual call if active.
2292            terminateScoUsingVirtualVoiceCall();
2293        }
2294
2295        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
2296        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2297        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2298        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2299        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2300        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
2301                HeadsetService.BLUETOOTH_PERM);
2302    }
2303
2304    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
2305        if (prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2306            // When SCO gets disconnected during call transfer, Virtual call
2307            // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
2308            terminateScoUsingVirtualVoiceCall();
2309        }
2310        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
2311        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2312        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2313        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2314        mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
2315        log("Audio state " + device + ": " + prevState + "->" + newState);
2316    }
2317
2318    /*
2319     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
2320     */
2321    private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
2322            Object[] arguments, BluetoothDevice device) {
2323        log("broadcastVendorSpecificEventIntent(" + command + ")");
2324        Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
2325        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
2326        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
2327        // assert: all elements of args are Serializable
2328        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
2329        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2330
2331        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
2332                + Integer.toString(companyId));
2333
2334        mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
2335    }
2336
2337    private void configAudioParameters(BluetoothDevice device) {
2338        // Reset NREC on connect event. Headset will override later
2339        HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>();
2340        AudioParamConfig.put("NREC", 1);
2341        mHeadsetAudioParam.put(device, AudioParamConfig);
2342        mAudioManager.setParameters(
2343                HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC + "=on");
2344        Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = "
2345                        + AudioParamConfig.get("NREC"));
2346    }
2347
2348    private void setAudioParameters(BluetoothDevice device) {
2349        // 1. update nrec value
2350        // 2. update headset name
2351        int mNrec = 0;
2352        HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device);
2353        if (AudioParam != null && !AudioParam.isEmpty()) {
2354            mNrec = AudioParam.get("NREC");
2355        } else {
2356            Log.e(TAG, "setAudioParameters: AudioParam not found");
2357        }
2358
2359        if (mNrec == 1) {
2360            Log.d(TAG, "Set NREC: 1 for device:" + device);
2361            mAudioManager.setParameters(HEADSET_NREC + "=on");
2362        } else {
2363            Log.d(TAG, "Set NREC: 0 for device:" + device);
2364            mAudioManager.setParameters(HEADSET_NREC + "=off");
2365        }
2366        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device));
2367    }
2368
2369    private String parseUnknownAt(String atString) {
2370        StringBuilder atCommand = new StringBuilder(atString.length());
2371        String result = null;
2372
2373        for (int i = 0; i < atString.length(); i++) {
2374            char c = atString.charAt(i);
2375            if (c == '"') {
2376                int j = atString.indexOf('"', i + 1); // search for closing "
2377                if (j == -1) { // unmatched ", insert one.
2378                    atCommand.append(atString.substring(i, atString.length()));
2379                    atCommand.append('"');
2380                    break;
2381                }
2382                atCommand.append(atString.substring(i, j + 1));
2383                i = j;
2384            } else if (c != ' ') {
2385                atCommand.append(Character.toUpperCase(c));
2386            }
2387        }
2388        result = atCommand.toString();
2389        return result;
2390    }
2391
2392    private int getAtCommandType(String atCommand) {
2393        int commandType = mPhonebook.TYPE_UNKNOWN;
2394        String atString = null;
2395        atCommand = atCommand.trim();
2396        if (atCommand.length() > 5) {
2397            atString = atCommand.substring(5);
2398            if (atString.startsWith("?")) // Read
2399                commandType = mPhonebook.TYPE_READ;
2400            else if (atString.startsWith("=?")) // Test
2401                commandType = mPhonebook.TYPE_TEST;
2402            else if (atString.startsWith("=")) // Set
2403                commandType = mPhonebook.TYPE_SET;
2404            else
2405                commandType = mPhonebook.TYPE_UNKNOWN;
2406        }
2407        return commandType;
2408    }
2409
2410    /* Method to check if Virtual Call in Progress */
2411    private boolean isVirtualCallInProgress() {
2412        return mVirtualCallStarted;
2413    }
2414
2415    void setVirtualCallInProgress(boolean state) {
2416        mVirtualCallStarted = state;
2417    }
2418
2419    /* NOTE: Currently the VirtualCall API does not support handling of
2420    call transfers. If it is initiated from the handsfree device,
2421    HeadsetStateMachine will end the virtual call by calling
2422    terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
2423    synchronized boolean initiateScoUsingVirtualVoiceCall() {
2424        if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
2425        // 1. Check if the SCO state is idle
2426        if (isInCall() || mVoiceRecognitionStarted) {
2427            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
2428            return false;
2429        }
2430
2431        // 2. Send virtual phone state changed to initialize SCO
2432        processCallState(
2433                new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
2434        processCallState(
2435                new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
2436        processCallState(
2437                new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2438        setVirtualCallInProgress(true);
2439        // Done
2440        if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
2441        return true;
2442    }
2443
2444    synchronized boolean terminateScoUsingVirtualVoiceCall() {
2445        if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
2446
2447        if (!isVirtualCallInProgress()) {
2448            Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"
2449                            + "No present call to terminate");
2450            return false;
2451        }
2452
2453        // 2. Send virtual phone state changed to close SCO
2454        processCallState(
2455                new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2456        setVirtualCallInProgress(false);
2457        // Done
2458        if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
2459        return true;
2460    }
2461
2462    private void processAnswerCall(BluetoothDevice device) {
2463        if (device == null) {
2464            Log.w(TAG, "processAnswerCall device is null");
2465            return;
2466        }
2467
2468        if (mPhoneProxy != null) {
2469            try {
2470                mPhoneProxy.answerCall();
2471            } catch (RemoteException e) {
2472                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2473            }
2474        } else {
2475            Log.e(TAG, "Handsfree phone proxy null for answering call");
2476        }
2477    }
2478
2479    private void processHangupCall(BluetoothDevice device) {
2480        if (device == null) {
2481            Log.w(TAG, "processHangupCall device is null");
2482            return;
2483        }
2484        // Close the virtual call if active. Virtual call should be
2485        // terminated for CHUP callback event
2486        if (isVirtualCallInProgress()) {
2487            terminateScoUsingVirtualVoiceCall();
2488        } else {
2489            if (mPhoneProxy != null) {
2490                try {
2491                    mPhoneProxy.hangupCall();
2492                } catch (RemoteException e) {
2493                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2494                }
2495            } else {
2496                Log.e(TAG, "Handsfree phone proxy null for hanging up call");
2497            }
2498        }
2499    }
2500
2501    private void processDialCall(String number, BluetoothDevice device) {
2502        if (device == null) {
2503            Log.w(TAG, "processDialCall device is null");
2504            return;
2505        }
2506
2507        String dialNumber;
2508        if (mDialingOut) {
2509            if (DBG) log("processDialCall, already dialling");
2510            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2511            return;
2512        }
2513        if ((number == null) || (number.length() == 0)) {
2514            dialNumber = mPhonebook.getLastDialledNumber();
2515            if (dialNumber == null) {
2516                if (DBG) log("processDialCall, last dial number null");
2517                atResponseCodeNative(
2518                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2519                return;
2520            }
2521        } else if (number.charAt(0) == '>') {
2522            // Yuck - memory dialling requested.
2523            // Just dial last number for now
2524            if (number.startsWith(">9999")) { // for PTS test
2525                atResponseCodeNative(
2526                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2527                return;
2528            }
2529            if (DBG) log("processDialCall, memory dial do last dial for now");
2530            dialNumber = mPhonebook.getLastDialledNumber();
2531            if (dialNumber == null) {
2532                if (DBG) log("processDialCall, last dial number null");
2533                atResponseCodeNative(
2534                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2535                return;
2536            }
2537        } else {
2538            // Remove trailing ';'
2539            if (number.charAt(number.length() - 1) == ';') {
2540                number = number.substring(0, number.length() - 1);
2541            }
2542
2543            dialNumber = PhoneNumberUtils.convertPreDial(number);
2544        }
2545        // Check for virtual call to terminate before sending Call Intent
2546        terminateScoUsingVirtualVoiceCall();
2547
2548        Intent intent = new Intent(
2549                Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null));
2550        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2551        mService.startActivity(intent);
2552        // TODO(BT) continue send OK reults code after call starts
2553        //          hold wait lock, start a timer, set wait call flag
2554        //          Get call started indication from bluetooth phone
2555        mDialingOut = true;
2556        Message m = obtainMessage(DIALING_OUT_TIMEOUT);
2557        m.obj = getMatchingDevice(device);
2558        sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE);
2559    }
2560
2561    private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) {
2562        if (device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) {
2563            Log.w(TAG, "ignore processVolumeEvent");
2564            return;
2565        }
2566
2567        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
2568            mPhoneState.setSpeakerVolume(volume);
2569            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
2570            mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
2571        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
2572            mPhoneState.setMicVolume(volume);
2573        } else {
2574            Log.e(TAG, "Bad voluem type: " + volumeType);
2575        }
2576    }
2577
2578    private void processSendDtmf(int dtmf, BluetoothDevice device) {
2579        if (device == null) {
2580            Log.w(TAG, "processSendDtmf device is null");
2581            return;
2582        }
2583
2584        if (mPhoneProxy != null) {
2585            try {
2586                mPhoneProxy.sendDtmf(dtmf);
2587            } catch (RemoteException e) {
2588                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2589            }
2590        } else {
2591            Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
2592        }
2593    }
2594
2595    private void processCallState(HeadsetCallState callState) {
2596        processCallState(callState, false);
2597    }
2598
2599    private void processCallState(HeadsetCallState callState, boolean isVirtualCall) {
2600        mPhoneState.setNumActiveCall(callState.mNumActive);
2601        mPhoneState.setNumHeldCall(callState.mNumHeld);
2602        mPhoneState.setCallState(callState.mCallState);
2603        if (mDialingOut) {
2604            if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) {
2605                BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT);
2606                if (device == null) {
2607                    return;
2608                }
2609                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2610                removeMessages(DIALING_OUT_TIMEOUT);
2611            } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE
2612                    || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) {
2613                mDialingOut = false;
2614            }
2615        }
2616
2617        /* Set ActiveScoDevice to null when call ends */
2618        if ((mActiveScoDevice != null) && !isInCall()
2619                && callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE)
2620            mActiveScoDevice = null;
2621
2622        log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld
2623                + " mCallState: " + callState.mCallState);
2624        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
2625
2626        if (isVirtualCall) {
2627            // virtual call state update
2628            if (getCurrentState() != mDisconnected) {
2629                phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2630                        callState.mCallState, callState.mNumber, callState.mType);
2631            }
2632        } else {
2633            // circuit-switch voice call update
2634            // stop virtual voice call if there is a CSV call ongoing
2635            if (callState.mNumActive > 0 || callState.mNumHeld > 0
2636                    || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) {
2637                terminateScoUsingVirtualVoiceCall();
2638            }
2639
2640            // Specific handling for case of starting MO/MT call while VOIP
2641            // ongoing, terminateScoUsingVirtualVoiceCall() resets callState
2642            // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call
2643            // and get wrong value of callsetup. This case is hit only
2644            // SCO for VOIP call is not terminated via SDK API call.
2645            if (mPhoneState.getCallState() != callState.mCallState) {
2646                mPhoneState.setCallState(callState.mCallState);
2647            }
2648
2649            // at this step: if there is virtual call ongoing, it means there is no CSV call
2650            // let virtual call continue and skip phone state update
2651            if (!isVirtualCallInProgress()) {
2652                if (getCurrentState() != mDisconnected) {
2653                    phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2654                            callState.mCallState, callState.mNumber, callState.mType);
2655                }
2656            }
2657        }
2658    }
2659
2660    // 1 enable noice reduction
2661    // 0 disable noice reduction
2662    private void processNoiceReductionEvent(int enable, BluetoothDevice device) {
2663        HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device);
2664        if (AudioParamNrec != null && !AudioParamNrec.isEmpty()) {
2665            if (enable == 1)
2666                AudioParamNrec.put("NREC", 1);
2667            else
2668                AudioParamNrec.put("NREC", 0);
2669            log("NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC"));
2670        } else {
2671            Log.e(TAG, "processNoiceReductionEvent: AudioParamNrec is null ");
2672        }
2673
2674        if (mActiveScoDevice != null && mActiveScoDevice.equals(device)
2675                && mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2676            setAudioParameters(device);
2677        }
2678    }
2679
2680    // 2 - WBS on
2681    // 1 - NBS on
2682    private void processWBSEvent(int enable, BluetoothDevice device) {
2683        if (enable == 2) {
2684            Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + device.getName() + " - "
2685                            + device.getAddress());
2686            mAudioManager.setParameters(HEADSET_WBS + "=on");
2687        } else {
2688            Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " + device.getName() + " - "
2689                            + device.getAddress());
2690            mAudioManager.setParameters(HEADSET_WBS + "=off");
2691        }
2692    }
2693
2694    private void processAtChld(int chld, BluetoothDevice device) {
2695        if (device == null) {
2696            Log.w(TAG, "processAtChld device is null");
2697            return;
2698        }
2699
2700        if (mPhoneProxy != null) {
2701            try {
2702                if (mPhoneProxy.processChld(chld)) {
2703                    atResponseCodeNative(
2704                            HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2705                } else {
2706                    atResponseCodeNative(
2707                            HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2708                }
2709            } catch (RemoteException e) {
2710                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2711                atResponseCodeNative(
2712                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2713            }
2714        } else {
2715            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
2716            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2717        }
2718    }
2719
2720    private void processSubscriberNumberRequest(BluetoothDevice device) {
2721        if (device == null) {
2722            Log.w(TAG, "processSubscriberNumberRequest device is null");
2723            return;
2724        }
2725
2726        if (mPhoneProxy != null) {
2727            try {
2728                String number = mPhoneProxy.getSubscriberNumber();
2729                if (number != null) {
2730                    atResponseStringNative("+CNUM: ,\"" + number + "\","
2731                                    + PhoneNumberUtils.toaFromString(number) + ",,4",
2732                            getByteAddress(device));
2733                    atResponseCodeNative(
2734                            HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device));
2735                } else {
2736                    Log.e(TAG, "getSubscriberNumber returns null");
2737                    atResponseCodeNative(
2738                            HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2739                }
2740            } catch (RemoteException e) {
2741                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2742                atResponseCodeNative(
2743                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2744            }
2745        } else {
2746            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
2747        }
2748    }
2749
2750    private void processAtCind(BluetoothDevice device) {
2751        int call, call_setup;
2752
2753        if (device == null) {
2754            Log.w(TAG, "processAtCind device is null");
2755            return;
2756        }
2757
2758        /* Handsfree carkits expect that +CIND is properly responded to
2759         Hence we ensure that a proper response is sent
2760         for the virtual call too.*/
2761        if (isVirtualCallInProgress()) {
2762            call = 1;
2763            call_setup = 0;
2764        } else {
2765            // regular phone call
2766            call = mPhoneState.getNumActiveCall();
2767            call_setup = mPhoneState.getNumHeldCall();
2768        }
2769
2770        cindResponseNative(mPhoneState.getService(), call, call_setup, mPhoneState.getCallState(),
2771                mPhoneState.getSignal(), mPhoneState.getRoam(), mPhoneState.getBatteryCharge(),
2772                getByteAddress(device));
2773    }
2774
2775    private void processAtCops(BluetoothDevice device) {
2776        if (device == null) {
2777            Log.w(TAG, "processAtCops device is null");
2778            return;
2779        }
2780
2781        if (mPhoneProxy != null) {
2782            try {
2783                String operatorName = mPhoneProxy.getNetworkOperator();
2784                if (operatorName == null) {
2785                    operatorName = "";
2786                }
2787                copsResponseNative(operatorName, getByteAddress(device));
2788            } catch (RemoteException e) {
2789                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2790                copsResponseNative("", getByteAddress(device));
2791            }
2792        } else {
2793            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
2794            copsResponseNative("", getByteAddress(device));
2795        }
2796    }
2797
2798    private void processAtClcc(BluetoothDevice device) {
2799        if (device == null) {
2800            Log.w(TAG, "processAtClcc device is null");
2801            return;
2802        }
2803
2804        if (mPhoneProxy != null) {
2805            try {
2806                if (isVirtualCallInProgress()) {
2807                    String phoneNumber = "";
2808                    int type = PhoneNumberUtils.TOA_Unknown;
2809                    try {
2810                        phoneNumber = mPhoneProxy.getSubscriberNumber();
2811                        type = PhoneNumberUtils.toaFromString(phoneNumber);
2812                    } catch (RemoteException ee) {
2813                        Log.e(TAG, "Unable to retrieve phone number"
2814                                        + "using IBluetoothHeadsetPhone proxy");
2815                        phoneNumber = "";
2816                    }
2817                    clccResponseNative(
2818                            1, 0, 0, 0, false, phoneNumber, type, getByteAddress(device));
2819                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2820                } else if (!mPhoneProxy.listCurrentCalls()) {
2821                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2822                } else {
2823                    Log.d(TAG, "Starting CLCC response timeout for device: " + device);
2824                    Message m = obtainMessage(CLCC_RSP_TIMEOUT);
2825                    m.obj = getMatchingDevice(device);
2826                    sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE);
2827                }
2828            } catch (RemoteException e) {
2829                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2830                clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2831            }
2832        } else {
2833            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
2834            clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2835        }
2836    }
2837
2838    private void processAtCscs(String atString, int type, BluetoothDevice device) {
2839        log("processAtCscs - atString = " + atString);
2840        if (mPhonebook != null) {
2841            mPhonebook.handleCscsCommand(atString, type, device);
2842        } else {
2843            Log.e(TAG, "Phonebook handle null for At+CSCS");
2844            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2845        }
2846    }
2847
2848    private void processAtCpbs(String atString, int type, BluetoothDevice device) {
2849        log("processAtCpbs - atString = " + atString);
2850        if (mPhonebook != null) {
2851            mPhonebook.handleCpbsCommand(atString, type, device);
2852        } else {
2853            Log.e(TAG, "Phonebook handle null for At+CPBS");
2854            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2855        }
2856    }
2857
2858    private void processAtCpbr(String atString, int type, BluetoothDevice device) {
2859        log("processAtCpbr - atString = " + atString);
2860        if (mPhonebook != null) {
2861            mPhonebook.handleCpbrCommand(atString, type, device);
2862        } else {
2863            Log.e(TAG, "Phonebook handle null for At+CPBR");
2864            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2865        }
2866    }
2867
2868    /**
2869     * Find a character ch, ignoring quoted sections.
2870     * Return input.length() if not found.
2871     */
2872    static private int findChar(char ch, String input, int fromIndex) {
2873        for (int i = fromIndex; i < input.length(); i++) {
2874            char c = input.charAt(i);
2875            if (c == '"') {
2876                i = input.indexOf('"', i + 1);
2877                if (i == -1) {
2878                    return input.length();
2879                }
2880            } else if (c == ch) {
2881                return i;
2882            }
2883        }
2884        return input.length();
2885    }
2886
2887    /**
2888     * Break an argument string into individual arguments (comma delimited).
2889     * Integer arguments are turned into Integer objects. Otherwise a String
2890     * object is used.
2891     */
2892    static private Object[] generateArgs(String input) {
2893        int i = 0;
2894        int j;
2895        ArrayList<Object> out = new ArrayList<Object>();
2896        while (i <= input.length()) {
2897            j = findChar(',', input, i);
2898
2899            String arg = input.substring(i, j);
2900            try {
2901                out.add(new Integer(arg));
2902            } catch (NumberFormatException e) {
2903                out.add(arg);
2904            }
2905
2906            i = j + 1; // move past comma
2907        }
2908        return out.toArray();
2909    }
2910
2911    /**
2912     * @return {@code true} if the given string is a valid vendor-specific AT command.
2913     */
2914    private boolean processVendorSpecificAt(String atString) {
2915        log("processVendorSpecificAt - atString = " + atString);
2916
2917        // Currently we accept only SET type commands.
2918        int indexOfEqual = atString.indexOf("=");
2919        if (indexOfEqual == -1) {
2920            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2921            return false;
2922        }
2923
2924        String command = atString.substring(0, indexOfEqual);
2925        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
2926        if (companyId == null) {
2927            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
2928            return false;
2929        }
2930
2931        String arg = atString.substring(indexOfEqual + 1);
2932        if (arg.startsWith("?")) {
2933            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2934            return false;
2935        }
2936
2937        Object[] args = generateArgs(arg);
2938        broadcastVendorSpecificEventIntent(
2939                command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET, args, mCurrentDevice);
2940        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice));
2941        return true;
2942    }
2943
2944    private void processUnknownAt(String atString, BluetoothDevice device) {
2945        if (device == null) {
2946            Log.w(TAG, "processUnknownAt device is null");
2947            return;
2948        }
2949
2950        // TODO (BT)
2951        log("processUnknownAt - atString = " + atString);
2952        String atCommand = parseUnknownAt(atString);
2953        int commandType = getAtCommandType(atCommand);
2954        if (atCommand.startsWith("+CSCS"))
2955            processAtCscs(atCommand.substring(5), commandType, device);
2956        else if (atCommand.startsWith("+CPBS"))
2957            processAtCpbs(atCommand.substring(5), commandType, device);
2958        else if (atCommand.startsWith("+CPBR"))
2959            processAtCpbr(atCommand.substring(5), commandType, device);
2960        else if (!processVendorSpecificAt(atCommand))
2961            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2962    }
2963
2964    private void processKeyPressed(BluetoothDevice device) {
2965        if (device == null) {
2966            Log.w(TAG, "processKeyPressed device is null");
2967            return;
2968        }
2969
2970        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
2971            if (mPhoneProxy != null) {
2972                try {
2973                    mPhoneProxy.answerCall();
2974                } catch (RemoteException e) {
2975                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2976                }
2977            } else {
2978                Log.e(TAG, "Handsfree phone proxy null for answering call");
2979            }
2980        } else if (mPhoneState.getNumActiveCall() > 0) {
2981            if (!isAudioOn()) {
2982                connectAudioNative(getByteAddress(mCurrentDevice));
2983            } else {
2984                if (mPhoneProxy != null) {
2985                    try {
2986                        mPhoneProxy.hangupCall();
2987                    } catch (RemoteException e) {
2988                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
2989                    }
2990                } else {
2991                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
2992                }
2993            }
2994        } else {
2995            String dialNumber = mPhonebook.getLastDialledNumber();
2996            if (dialNumber == null) {
2997                if (DBG) log("processKeyPressed, last dial number null");
2998                return;
2999            }
3000            Intent intent = new Intent(
3001                    Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null));
3002            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3003            mService.startActivity(intent);
3004        }
3005    }
3006
3007    private void sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value) {
3008        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
3009        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
3010        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, ind_id);
3011        if (ind_value != null)
3012            intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, ind_value);
3013
3014        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
3015    }
3016
3017    private void processAtBind(String at_string, BluetoothDevice device) {
3018        log("processAtBind processAtBind: " + at_string);
3019
3020        // Parse the AT String to find the Indicator Ids that are supported
3021        int ind_id = 0;
3022        int iter = 0;
3023        int iter1 = 0;
3024
3025        while (iter < at_string.length()) {
3026            iter1 = findChar(',', at_string, iter);
3027            String id = at_string.substring(iter, iter1);
3028
3029            try {
3030                ind_id = new Integer(id);
3031            } catch (NumberFormatException e) {
3032                Log.e(TAG, Log.getStackTraceString(new Throwable()));
3033            }
3034
3035            switch (ind_id) {
3036                case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY:
3037                    log("Send Broadcast intent for the"
3038                            + "Enhanced Driver Safety indicator.");
3039                    sendIndicatorIntent(device, ind_id, null);
3040                    break;
3041                case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS:
3042                    log("Send Broadcast intent for the"
3043                            + "Battery Level indicator.");
3044                    sendIndicatorIntent(device, ind_id, null);
3045                    break;
3046                default:
3047                    log("Invalid HF Indicator Received");
3048                    break;
3049            }
3050
3051            iter = iter1 + 1; // move past comma
3052        }
3053    }
3054
3055    private void processAtBiev(int ind_id, int ind_value, BluetoothDevice device) {
3056        log(" Process AT + BIEV Command : " + ind_id + ", " + ind_value);
3057
3058        String ind_value_str = Integer.toString(ind_value);
3059
3060        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
3061        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
3062        sendIndicatorIntent(device, ind_id, ind_value_str);
3063    }
3064
3065    private void onConnectionStateChanged(int state, byte[] address) {
3066        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
3067        event.valueInt = state;
3068        event.device = getDevice(address);
3069        sendMessage(STACK_EVENT, event);
3070    }
3071
3072    private void onAudioStateChanged(int state, byte[] address) {
3073        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
3074        event.valueInt = state;
3075        event.device = getDevice(address);
3076        sendMessage(STACK_EVENT, event);
3077    }
3078
3079    private void onVrStateChanged(int state, byte[] address) {
3080        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
3081        event.valueInt = state;
3082        event.device = getDevice(address);
3083        sendMessage(STACK_EVENT, event);
3084    }
3085
3086    private void onAnswerCall(byte[] address) {
3087        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
3088        event.device = getDevice(address);
3089        sendMessage(STACK_EVENT, event);
3090    }
3091
3092    private void onHangupCall(byte[] address) {
3093        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
3094        event.device = getDevice(address);
3095        sendMessage(STACK_EVENT, event);
3096    }
3097
3098    private void onVolumeChanged(int type, int volume, byte[] address) {
3099        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
3100        event.valueInt = type;
3101        event.valueInt2 = volume;
3102        event.device = getDevice(address);
3103        sendMessage(STACK_EVENT, event);
3104    }
3105
3106    private void onDialCall(String number, byte[] address) {
3107        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
3108        event.valueString = number;
3109        event.device = getDevice(address);
3110        sendMessage(STACK_EVENT, event);
3111    }
3112
3113    private void onSendDtmf(int dtmf, byte[] address) {
3114        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
3115        event.valueInt = dtmf;
3116        event.device = getDevice(address);
3117        sendMessage(STACK_EVENT, event);
3118    }
3119
3120    private void onNoiceReductionEnable(boolean enable, byte[] address) {
3121        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
3122        event.valueInt = enable ? 1 : 0;
3123        event.device = getDevice(address);
3124        sendMessage(STACK_EVENT, event);
3125    }
3126
3127    private void onWBS(int codec, byte[] address) {
3128        StackEvent event = new StackEvent(EVENT_TYPE_WBS);
3129        event.valueInt = codec;
3130        event.device = getDevice(address);
3131        sendMessage(STACK_EVENT, event);
3132    }
3133
3134    private void onAtChld(int chld, byte[] address) {
3135        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
3136        event.valueInt = chld;
3137        event.device = getDevice(address);
3138        sendMessage(STACK_EVENT, event);
3139    }
3140
3141    private void onAtCnum(byte[] address) {
3142        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
3143        event.device = getDevice(address);
3144        sendMessage(STACK_EVENT, event);
3145    }
3146
3147    private void onAtCind(byte[] address) {
3148        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
3149        event.device = getDevice(address);
3150        sendMessage(STACK_EVENT, event);
3151    }
3152
3153    private void onAtCops(byte[] address) {
3154        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
3155        event.device = getDevice(address);
3156        sendMessage(STACK_EVENT, event);
3157    }
3158
3159    private void onAtClcc(byte[] address) {
3160        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
3161        event.device = getDevice(address);
3162        sendMessage(STACK_EVENT, event);
3163    }
3164
3165    private void onUnknownAt(String atString, byte[] address) {
3166        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
3167        event.valueString = atString;
3168        event.device = getDevice(address);
3169        sendMessage(STACK_EVENT, event);
3170    }
3171
3172    private void onKeyPressed(byte[] address) {
3173        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
3174        event.device = getDevice(address);
3175        sendMessage(STACK_EVENT, event);
3176    }
3177
3178    private void onATBind(String atString, byte[] address) {
3179        StackEvent event = new StackEvent(EVENT_TYPE_BIND);
3180        event.valueString = atString;
3181        event.device = getDevice(address);
3182        sendMessage(STACK_EVENT, event);
3183    }
3184
3185    private void onATBiev(int ind_id, int ind_value, byte[] address) {
3186        StackEvent event = new StackEvent(EVENT_TYPE_BIEV);
3187        event.valueInt = ind_id;
3188        event.valueInt2 = ind_value;
3189        event.device = getDevice(address);
3190        sendMessage(STACK_EVENT, event);
3191    }
3192
3193    private void processIntentBatteryChanged(Intent intent) {
3194        int batteryLevel = intent.getIntExtra("level", -1);
3195        int scale = intent.getIntExtra("scale", -1);
3196        if (batteryLevel == -1 || scale == -1 || scale == 0) {
3197            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
3198            return;
3199        }
3200        batteryLevel = batteryLevel * 5 / scale;
3201        mPhoneState.setBatteryCharge(batteryLevel);
3202    }
3203
3204    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
3205        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
3206                deviceState.mBatteryCharge);
3207    }
3208
3209    private void processSendClccResponse(HeadsetClccResponse clcc) {
3210        BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT);
3211        if (device == null) {
3212            return;
3213        }
3214        if (clcc.mIndex == 0) {
3215            removeMessages(CLCC_RSP_TIMEOUT);
3216        }
3217        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
3218                clcc.mNumber, clcc.mType, getByteAddress(device));
3219    }
3220
3221    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
3222        String stringToSend = resultCode.mCommand + ": ";
3223        if (resultCode.mArg != null) {
3224            stringToSend += resultCode.mArg;
3225        }
3226        atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice));
3227    }
3228
3229    private String getCurrentDeviceName(BluetoothDevice device) {
3230        String defaultName = "<unknown>";
3231
3232        if (device == null) {
3233            return defaultName;
3234        }
3235
3236        String deviceName = device.getName();
3237        if (deviceName == null) {
3238            return defaultName;
3239        }
3240        return deviceName;
3241    }
3242
3243    private byte[] getByteAddress(BluetoothDevice device) {
3244        return Utils.getBytesFromAddress(device.getAddress());
3245    }
3246
3247    private BluetoothDevice getDevice(byte[] address) {
3248        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
3249    }
3250
3251    private boolean isInCall() {
3252        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0)
3253                || ((mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)
3254                           && (mPhoneState.getCallState()
3255                                      != HeadsetHalConstants.CALL_STATE_INCOMING)));
3256    }
3257
3258    // Accept incoming SCO only when there is active call, VR activated,
3259    // active VOIP call
3260    private boolean isScoAcceptable() {
3261        return mAudioRouteAllowed && (mVoiceRecognitionStarted || isInCall());
3262    }
3263
3264    boolean isConnected() {
3265        IState currentState = getCurrentState();
3266        return (currentState == mConnected || currentState == mAudioOn);
3267    }
3268
3269    boolean okToConnect(BluetoothDevice device) {
3270        AdapterService adapterService = AdapterService.getAdapterService();
3271        int priority = mService.getPriority(device);
3272        boolean ret = false;
3273        // check if this is an incoming connection in Quiet mode.
3274        if ((adapterService == null)
3275                || ((adapterService.isQuietModeEnabled() == true) && (mTargetDevice == null))) {
3276            ret = false;
3277        }
3278        // check priority and accept or reject the connection. if priority is undefined
3279        // it is likely that our SDP has not completed and peer is initiating the
3280        // connection. Allow this connection, provided the device is bonded
3281        else if ((BluetoothProfile.PRIORITY_OFF < priority)
3282                || ((BluetoothProfile.PRIORITY_UNDEFINED == priority)
3283                           && (device.getBondState() != BluetoothDevice.BOND_NONE))) {
3284            ret = true;
3285        }
3286        return ret;
3287    }
3288
3289    @Override
3290    protected void log(String msg) {
3291        if (DBG) {
3292            super.log(msg);
3293        }
3294    }
3295
3296    public void handleAccessPermissionResult(Intent intent) {
3297        log("handleAccessPermissionResult");
3298        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3299        if (mPhonebook != null) {
3300            if (!mPhonebook.getCheckingAccessPermission()) {
3301                return;
3302            }
3303            int atCommandResult = 0;
3304            int atCommandErrorCode = 0;
3305            // HeadsetBase headset = mHandsfree.getHeadset();
3306            // ASSERT: (headset != null) && headSet.isConnected()
3307            // REASON: mCheckingAccessPermission is true, otherwise resetAtState
3308            // has set mCheckingAccessPermission to false
3309            if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
3310                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
3311                            BluetoothDevice.CONNECTION_ACCESS_NO)
3312                        == BluetoothDevice.CONNECTION_ACCESS_YES) {
3313                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3314                        mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
3315                    }
3316                    atCommandResult = mPhonebook.processCpbrCommand(device);
3317                } else {
3318                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3319                        mCurrentDevice.setPhonebookAccessPermission(
3320                                BluetoothDevice.ACCESS_REJECTED);
3321                    }
3322                }
3323            }
3324            mPhonebook.setCpbrIndex(-1);
3325            mPhonebook.setCheckingAccessPermission(false);
3326
3327            if (atCommandResult >= 0) {
3328                atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device));
3329            } else {
3330                log("handleAccessPermissionResult - RESULT_NONE");
3331            }
3332        } else {
3333            Log.e(TAG, "Phonebook handle null");
3334            if (device != null) {
3335                atResponseCodeNative(
3336                        HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
3337            }
3338        }
3339    }
3340
3341    private static final String SCHEME_TEL = "tel";
3342
3343    // Event types for STACK_EVENT message
3344    final private static int EVENT_TYPE_NONE = 0;
3345    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
3346    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
3347    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
3348    final private static int EVENT_TYPE_ANSWER_CALL = 4;
3349    final private static int EVENT_TYPE_HANGUP_CALL = 5;
3350    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
3351    final private static int EVENT_TYPE_DIAL_CALL = 7;
3352    final private static int EVENT_TYPE_SEND_DTMF = 8;
3353    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
3354    final private static int EVENT_TYPE_AT_CHLD = 10;
3355    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
3356    final private static int EVENT_TYPE_AT_CIND = 12;
3357    final private static int EVENT_TYPE_AT_COPS = 13;
3358    final private static int EVENT_TYPE_AT_CLCC = 14;
3359    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
3360    final private static int EVENT_TYPE_KEY_PRESSED = 16;
3361    final private static int EVENT_TYPE_WBS = 17;
3362    final private static int EVENT_TYPE_BIND = 18;
3363    final private static int EVENT_TYPE_BIEV = 19;
3364
3365    private class StackEvent {
3366        int type = EVENT_TYPE_NONE;
3367        int valueInt = 0;
3368        int valueInt2 = 0;
3369        String valueString = null;
3370        BluetoothDevice device = null;
3371
3372        private StackEvent(int type) {
3373            this.type = type;
3374        }
3375    }
3376
3377    /*package*/ native boolean atResponseCodeNative(
3378            int responseCode, int errorCode, byte[] address);
3379    /*package*/ native boolean atResponseStringNative(String responseString, byte[] address);
3380
3381    private native static void classInitNative();
3382    private native void initializeNative(int max_hf_clients);
3383    private native void cleanupNative();
3384    private native boolean connectHfpNative(byte[] address);
3385    private native boolean disconnectHfpNative(byte[] address);
3386    private native boolean connectAudioNative(byte[] address);
3387    private native boolean disconnectAudioNative(byte[] address);
3388    private native boolean startVoiceRecognitionNative(byte[] address);
3389    private native boolean stopVoiceRecognitionNative(byte[] address);
3390    private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
3391    private native boolean cindResponseNative(int service, int numActive, int numHeld,
3392            int callState, int signal, int roam, int batteryCharge, byte[] address);
3393    private native boolean bindResponseNative(int ind_id, boolean ind_status, byte[] address);
3394    private native boolean notifyDeviceStatusNative(
3395            int networkState, int serviceType, int signal, int batteryCharge);
3396
3397    private native boolean clccResponseNative(int index, int dir, int status, int mode,
3398            boolean mpty, String number, int type, byte[] address);
3399    private native boolean copsResponseNative(String operatorName, byte[] address);
3400
3401    private native boolean phoneStateChangeNative(
3402            int numActive, int numHeld, int callState, String number, int type);
3403    private native boolean configureWBSNative(byte[] address, int condec_config);
3404}
3405