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