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