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