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 = true;
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                    // TODO(BT) should I save the state for next broadcast as the prevState?
1086                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1087                    setAudioParameters(device); /*Set proper Audio Paramters.*/
1088                    mAudioManager.setBluetoothScoOn(true);
1089                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1090                                        BluetoothHeadset.STATE_AUDIO_CONNECTING);
1091                    mActiveScoDevice = device;
1092                    transitionTo(mAudioOn);
1093                    break;
1094                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1095                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1096                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1097                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1098                    break;
1099                    // TODO(BT) process other states
1100                default:
1101                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1102                    break;
1103            }
1104        }
1105
1106        private void processSlcConnected() {
1107            if (mPhoneProxy != null) {
1108                try {
1109                    mPhoneProxy.queryPhoneState();
1110                } catch (RemoteException e) {
1111                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1112                }
1113            } else {
1114                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1115            }
1116
1117        }
1118
1119        private void processMultiHFConnected(BluetoothDevice device) {
1120            log("Connect state: processMultiHFConnected");
1121            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1122                log ("mActiveScoDevice is disconnected, setting it to null");
1123                mActiveScoDevice = null;
1124            }
1125            /* Assign the current activedevice again if the disconnected
1126                         device equals to the current active device */
1127            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1128                transitionTo(mConnected);
1129                int deviceSize = mConnectedDevicesList.size();
1130                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1131            } else {
1132                // The disconnected device is not current active device
1133                transitionTo(mConnected);
1134            }
1135            log("processMultiHFConnected , the latest mCurrentDevice is:" +
1136                                     mCurrentDevice);
1137            log("Connect state: processMultiHFConnected ," +
1138                       "fake broadcasting for mCurrentDevice");
1139            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1140                            BluetoothProfile.STATE_DISCONNECTED);
1141        }
1142    }
1143
1144    private class AudioOn extends State {
1145
1146        @Override
1147        public void enter() {
1148            log("Enter AudioOn: " + getCurrentMessage().what + ", size: " +
1149                                  mConnectedDevicesList.size());
1150        }
1151
1152        @Override
1153        public boolean processMessage(Message message) {
1154            log("AudioOn process message: " + message.what + ", size: " +
1155                                  mConnectedDevicesList.size());
1156            if (DBG) {
1157                if (mConnectedDevicesList.size() == 0) {
1158                    log("ERROR: mConnectedDevicesList is empty in AudioOn");
1159                    return NOT_HANDLED;
1160                }
1161            }
1162
1163            boolean retValue = HANDLED;
1164            switch(message.what) {
1165                case CONNECT:
1166                {
1167                    BluetoothDevice device = (BluetoothDevice) message.obj;
1168                    if (device == null) {
1169                        break;
1170                    }
1171
1172                    if (mConnectedDevicesList.contains(device)) {
1173                        break;
1174                    }
1175
1176                    if (max_hf_connections == 1) {
1177                        deferMessage(obtainMessage(DISCONNECT, mCurrentDevice));
1178                        deferMessage(obtainMessage(CONNECT, device));
1179                        if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1180                            Log.d(TAG, "Disconnecting SCO audio for device = " + mCurrentDevice);
1181                        } else {
1182                            Log.e(TAG, "disconnectAudioNative failed");
1183                        }
1184                        break;
1185                    }
1186
1187                    if (mConnectedDevicesList.size() >= max_hf_connections) {
1188                        BluetoothDevice DisconnectConnectedDevice = null;
1189                        IState CurrentAudioState = getCurrentState();
1190                        Log.d(TAG, "Reach to max size, disconnect " +
1191                                           "one of them first");
1192                        DisconnectConnectedDevice = mConnectedDevicesList.get(0);
1193
1194                        if (mActiveScoDevice.equals(DisconnectConnectedDevice)) {
1195                           DisconnectConnectedDevice = mConnectedDevicesList.get(1);
1196                        }
1197
1198                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1199                                   BluetoothProfile.STATE_DISCONNECTED);
1200
1201                        if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
1202                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1203                                           BluetoothProfile.STATE_CONNECTING);
1204                            break;
1205                        } else {
1206                            broadcastConnectionState(DisconnectConnectedDevice,
1207                                       BluetoothProfile.STATE_DISCONNECTING,
1208                                       BluetoothProfile.STATE_CONNECTED);
1209                        }
1210
1211                        synchronized (HeadsetStateMachine.this) {
1212                            mTargetDevice = device;
1213                            mMultiDisconnectDevice = DisconnectConnectedDevice;
1214                            transitionTo(mMultiHFPending);
1215                            DisconnectConnectedDevice = null;
1216                        }
1217                    } else if(mConnectedDevicesList.size() < max_hf_connections) {
1218                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1219                        BluetoothProfile.STATE_DISCONNECTED);
1220                        if (!connectHfpNative(getByteAddress(device))) {
1221                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1222                                BluetoothProfile.STATE_CONNECTING);
1223                            break;
1224                        }
1225                        synchronized (HeadsetStateMachine.this) {
1226                            mTargetDevice = device;
1227                            // Transtion to MultilHFPending state for Multi handsfree connection
1228                            transitionTo(mMultiHFPending);
1229                        }
1230                    }
1231                    Message m = obtainMessage(CONNECT_TIMEOUT);
1232                    m.obj = device;
1233                    sendMessageDelayed(m, 30000);
1234                }
1235                break;
1236                case CONNECT_TIMEOUT:
1237                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1238                                             getByteAddress(mTargetDevice));
1239                break;
1240                case DISCONNECT:
1241                {
1242                    BluetoothDevice device = (BluetoothDevice)message.obj;
1243                    if (!mConnectedDevicesList.contains(device)) {
1244                        break;
1245                    }
1246                    if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1247                        // The disconnected device is active SCO device
1248                        Log.d(TAG, "AudioOn, the disconnected device" +
1249                                            "is active SCO device");
1250                        deferMessage(obtainMessage(DISCONNECT, message.obj));
1251                        // Disconnect BT SCO first
1252                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1253                            log("Disconnecting SCO audio");
1254                        } else {
1255                            // if disconnect BT SCO failed, transition to mConnected state
1256                            transitionTo(mConnected);
1257                        }
1258                    } else {
1259                        /* Do not disconnect BT SCO if the disconnected
1260                           device is not active SCO device */
1261                        Log.d(TAG, "AudioOn, the disconnected device" +
1262                                        "is not active SCO device");
1263                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
1264                                   BluetoothProfile.STATE_CONNECTED);
1265                        // Should be still in AudioOn state
1266                        if (!disconnectHfpNative(getByteAddress(device))) {
1267                            Log.w(TAG, "AudioOn, disconnect device failed");
1268                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1269                                       BluetoothProfile.STATE_DISCONNECTING);
1270                            break;
1271                        }
1272                        /* Transtion to MultiHFPending state for Multi
1273                           handsfree connection */
1274                        if (mConnectedDevicesList.size() > 1) {
1275                            mMultiDisconnectDevice = device;
1276                            transitionTo(mMultiHFPending);
1277                        }
1278                    }
1279                }
1280                break;
1281                case DISCONNECT_AUDIO:
1282                    if (mActiveScoDevice != null) {
1283                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1284                            log("Disconnecting SCO audio for device = " +
1285                                                 mActiveScoDevice);
1286                        } else {
1287                            Log.e(TAG, "disconnectAudioNative failed" +
1288                                      "for device = " + mActiveScoDevice);
1289                        }
1290                    }
1291                    break;
1292                case VOICE_RECOGNITION_START:
1293                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1294                    break;
1295                case VOICE_RECOGNITION_STOP:
1296                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1297                    break;
1298                case INTENT_SCO_VOLUME_CHANGED:
1299                    processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1300                    break;
1301                case CALL_STATE_CHANGED:
1302                    processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false));
1303                    break;
1304                case INTENT_BATTERY_CHANGED:
1305                    processIntentBatteryChanged((Intent) message.obj);
1306                    break;
1307                case DEVICE_STATE_CHANGED:
1308                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1309                    break;
1310                case SEND_CCLC_RESPONSE:
1311                    processSendClccResponse((HeadsetClccResponse) message.obj);
1312                    break;
1313                case CLCC_RSP_TIMEOUT:
1314                {
1315                    BluetoothDevice device = (BluetoothDevice) message.obj;
1316                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1317                }
1318                    break;
1319                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
1320                    processSendVendorSpecificResultCode(
1321                            (HeadsetVendorSpecificResultCode) message.obj);
1322                    break;
1323
1324                case VIRTUAL_CALL_START:
1325                    initiateScoUsingVirtualVoiceCall();
1326                    break;
1327                case VIRTUAL_CALL_STOP:
1328                    terminateScoUsingVirtualVoiceCall();
1329                    break;
1330
1331                case DIALING_OUT_TIMEOUT:
1332                {
1333                    if (mDialingOut) {
1334                        BluetoothDevice device = (BluetoothDevice)message.obj;
1335                        mDialingOut= false;
1336                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1337                                               0, getByteAddress(device));
1338                    }
1339                }
1340                    break;
1341                case START_VR_TIMEOUT:
1342                {
1343                    if (mWaitingForVoiceRecognition) {
1344                        BluetoothDevice device = (BluetoothDevice)message.obj;
1345                        mWaitingForVoiceRecognition = false;
1346                        Log.e(TAG, "Timeout waiting for voice recognition" +
1347                                                     "to start");
1348                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1349                                               0, getByteAddress(device));
1350                    }
1351                }
1352                    break;
1353                case STACK_EVENT:
1354                    StackEvent event = (StackEvent) message.obj;
1355                    if (DBG) {
1356                        log("event type: " + event.type);
1357                    }
1358                    switch (event.type) {
1359                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1360                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1361                            if (device1 != null && device1.equals(event.device)) {
1362                                Log.d(TAG, "remove connect timeout for device = " + device1);
1363                                removeMessages(CONNECT_TIMEOUT);
1364                            }
1365                            processConnectionEvent(event.valueInt, event.device);
1366                            break;
1367                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1368                            processAudioEvent(event.valueInt, event.device);
1369                            break;
1370                        case EVENT_TYPE_VR_STATE_CHANGED:
1371                            processVrEvent(event.valueInt, event.device);
1372                            break;
1373                        case EVENT_TYPE_ANSWER_CALL:
1374                            processAnswerCall(event.device);
1375                            break;
1376                        case EVENT_TYPE_HANGUP_CALL:
1377                            processHangupCall(event.device);
1378                            break;
1379                        case EVENT_TYPE_VOLUME_CHANGED:
1380                            processVolumeEvent(event.valueInt, event.valueInt2,
1381                                                     event.device);
1382                            break;
1383                        case EVENT_TYPE_DIAL_CALL:
1384                            processDialCall(event.valueString, event.device);
1385                            break;
1386                        case EVENT_TYPE_SEND_DTMF:
1387                            processSendDtmf(event.valueInt, event.device);
1388                            break;
1389                        case EVENT_TYPE_NOICE_REDUCTION:
1390                            processNoiceReductionEvent(event.valueInt, event.device);
1391                            break;
1392                        case EVENT_TYPE_AT_CHLD:
1393                            processAtChld(event.valueInt, event.device);
1394                            break;
1395                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1396                            processSubscriberNumberRequest(event.device);
1397                            break;
1398                        case EVENT_TYPE_AT_CIND:
1399                            processAtCind(event.device);
1400                            break;
1401                        case EVENT_TYPE_AT_COPS:
1402                            processAtCops(event.device);
1403                            break;
1404                        case EVENT_TYPE_AT_CLCC:
1405                            processAtClcc(event.device);
1406                            break;
1407                        case EVENT_TYPE_UNKNOWN_AT:
1408                            processUnknownAt(event.valueString, event.device);
1409                            break;
1410                        case EVENT_TYPE_KEY_PRESSED:
1411                            processKeyPressed(event.device);
1412                            break;
1413                        default:
1414                            Log.e(TAG, "Unknown stack event: " + event.type);
1415                            break;
1416                    }
1417                    break;
1418                default:
1419                    return NOT_HANDLED;
1420            }
1421            return retValue;
1422        }
1423
1424        // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
1425        private void processConnectionEvent(int state, BluetoothDevice device) {
1426        Log.d(TAG, "processConnectionEvent state = " + state + ", device = " +
1427                                                   device);
1428            switch (state) {
1429                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1430                    if (mConnectedDevicesList.contains(device)) {
1431                        if (mActiveScoDevice != null
1432                            && mActiveScoDevice.equals(device)&& mAudioState
1433                            != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1434                            processAudioEvent(
1435                                HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
1436                        }
1437
1438                        synchronized (HeadsetStateMachine.this) {
1439                            mConnectedDevicesList.remove(device);
1440                            mHeadsetAudioParam.remove(device);
1441                            mHeadsetBrsf.remove(device);
1442                            Log.d(TAG, "device " + device.getAddress() +
1443                                           " is removed in AudioOn state");
1444                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1445                                                     BluetoothProfile.STATE_CONNECTED);
1446                            processWBSEvent(0, device); /* disable WBS audio parameters */
1447                            if (mConnectedDevicesList.size() == 0) {
1448                                transitionTo(mDisconnected);
1449                            }
1450                            else {
1451                                processMultiHFConnected(device);
1452                            }
1453                        }
1454                    } else {
1455                        Log.e(TAG, "Disconnected from unknown device: " + device);
1456                    }
1457                    break;
1458               case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1459                    processSlcConnected();
1460                    break;
1461                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1462                    if (mConnectedDevicesList.contains(device)) {
1463                        mIncomingDevice = null;
1464                        mTargetDevice = null;
1465                        break;
1466                    }
1467                    Log.w(TAG, "HFP to be Connected in AudioOn state");
1468                    if (okToConnect(device) && (mConnectedDevicesList.size()
1469                                                      < max_hf_connections) ) {
1470                        Log.i(TAG,"Incoming Hf accepted");
1471                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1472                                          BluetoothProfile.STATE_DISCONNECTED);
1473                        synchronized (HeadsetStateMachine.this) {
1474                            if (!mConnectedDevicesList.contains(device)) {
1475                                mCurrentDevice = device;
1476                                mConnectedDevicesList.add(device);
1477                                Log.d(TAG, "device " + device.getAddress() +
1478                                              " is added in AudioOn state");
1479                            }
1480                        }
1481                        configAudioParameters(device);
1482                     } else {
1483                         // reject the connection and stay in Connected state itself
1484                         Log.i(TAG,"Incoming Hf rejected. priority="
1485                                      + mService.getPriority(device) +
1486                                       " bondState=" + device.getBondState());
1487                         disconnectHfpNative(getByteAddress(device));
1488                         // the other profile connection should be initiated
1489                         AdapterService adapterService = AdapterService.getAdapterService();
1490                         if (adapterService != null) {
1491                             adapterService.connectOtherProfile(device,
1492                                             AdapterService.PROFILE_CONN_REJECTED);
1493                         }
1494                    }
1495                    break;
1496                default:
1497                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1498                    break;
1499            }
1500        }
1501
1502        // in AudioOn state
1503        private void processAudioEvent(int state, BluetoothDevice device) {
1504            if (!mConnectedDevicesList.contains(device)) {
1505                Log.e(TAG, "Audio changed on disconnected device: " + device);
1506                return;
1507            }
1508
1509            switch (state) {
1510                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1511                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1512                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1513                        mAudioManager.setBluetoothScoOn(false);
1514                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1515                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
1516                    }
1517                    transitionTo(mConnected);
1518                    break;
1519                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1520                    // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
1521                    //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
1522                    //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
1523                    break;
1524                default:
1525                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1526                    break;
1527            }
1528        }
1529
1530        private void processSlcConnected() {
1531            if (mPhoneProxy != null) {
1532                try {
1533                    mPhoneProxy.queryPhoneState();
1534                } catch (RemoteException e) {
1535                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1536                }
1537            } else {
1538                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1539            }
1540         }
1541
1542        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1543            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1544            if (mPhoneState.getSpeakerVolume() != volumeValue) {
1545                mPhoneState.setSpeakerVolume(volumeValue);
1546                setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK,
1547                                        volumeValue, getByteAddress(device));
1548            }
1549        }
1550
1551        private void processMultiHFConnected(BluetoothDevice device) {
1552            log("AudioOn state: processMultiHFConnected");
1553            /* Assign the current activedevice again if the disconnected
1554                          device equals to the current active device */
1555            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1556                int deviceSize = mConnectedDevicesList.size();
1557                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1558            }
1559            if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED)
1560                transitionTo(mConnected);
1561
1562            log("processMultiHFConnected , the latest mCurrentDevice is:"
1563                                      + mCurrentDevice);
1564            log("AudioOn state: processMultiHFConnected ," +
1565                       "fake broadcasting for mCurrentDevice");
1566            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1567                            BluetoothProfile.STATE_DISCONNECTED);
1568        }
1569    }
1570
1571    /* Add MultiHFPending state when atleast 1 HS is connected
1572            and disconnect/connect new HS */
1573    private class MultiHFPending extends State {
1574        @Override
1575        public void enter() {
1576            log("Enter MultiHFPending: " + getCurrentMessage().what +
1577                         ", size: " + mConnectedDevicesList.size());
1578        }
1579
1580        @Override
1581        public boolean processMessage(Message message) {
1582            log("MultiHFPending process message: " + message.what +
1583                         ", size: " + mConnectedDevicesList.size());
1584
1585            boolean retValue = HANDLED;
1586            switch(message.what) {
1587                case CONNECT:
1588                    deferMessage(message);
1589                    break;
1590
1591                case CONNECT_AUDIO:
1592                    if (mCurrentDevice != null) {
1593                        connectAudioNative(getByteAddress(mCurrentDevice));
1594                    }
1595                    break;
1596                case CONNECT_TIMEOUT:
1597                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1598                                             getByteAddress(mTargetDevice));
1599                    break;
1600
1601                case DISCONNECT_AUDIO:
1602                    if (mActiveScoDevice != null) {
1603                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1604                            Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for " +
1605                                                 mActiveScoDevice);
1606                        } else {
1607                            Log.e(TAG, "disconnectAudioNative failed" +
1608                                      "for device = " + mActiveScoDevice);
1609                        }
1610                    }
1611                    break;
1612                case DISCONNECT:
1613                    BluetoothDevice device = (BluetoothDevice) message.obj;
1614                    if (mConnectedDevicesList.contains(device) &&
1615                        mTargetDevice != null && mTargetDevice.equals(device)) {
1616                        // cancel connection to the mTargetDevice
1617                        broadcastConnectionState(device,
1618                                       BluetoothProfile.STATE_DISCONNECTED,
1619                                       BluetoothProfile.STATE_CONNECTING);
1620                        synchronized (HeadsetStateMachine.this) {
1621                            mTargetDevice = null;
1622                        }
1623                    } else {
1624                        deferMessage(message);
1625                    }
1626                    break;
1627                case VOICE_RECOGNITION_START:
1628                    device = (BluetoothDevice) message.obj;
1629                    if (mConnectedDevicesList.contains(device)) {
1630                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1631                    }
1632                    break;
1633                case VOICE_RECOGNITION_STOP:
1634                    device = (BluetoothDevice) message.obj;
1635                    if (mConnectedDevicesList.contains(device)) {
1636                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1637                    }
1638                    break;
1639                case INTENT_BATTERY_CHANGED:
1640                    processIntentBatteryChanged((Intent) message.obj);
1641                    break;
1642                case CALL_STATE_CHANGED:
1643                    processCallState((HeadsetCallState) message.obj,
1644                                      ((message.arg1 == 1)?true:false));
1645                    break;
1646                case DEVICE_STATE_CHANGED:
1647                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1648                    break;
1649                case SEND_CCLC_RESPONSE:
1650                    processSendClccResponse((HeadsetClccResponse) message.obj);
1651                    break;
1652                case CLCC_RSP_TIMEOUT:
1653                {
1654                    device = (BluetoothDevice) message.obj;
1655                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1656                }
1657                    break;
1658                case DIALING_OUT_TIMEOUT:
1659                    if (mDialingOut) {
1660                        device = (BluetoothDevice) message.obj;
1661                        mDialingOut= false;
1662                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1663                                             0, getByteAddress(device));
1664                    }
1665                    break;
1666                case VIRTUAL_CALL_START:
1667                    device = (BluetoothDevice) message.obj;
1668                    if(mConnectedDevicesList.contains(device)) {
1669                        initiateScoUsingVirtualVoiceCall();
1670                    }
1671                    break;
1672                case VIRTUAL_CALL_STOP:
1673                    device = (BluetoothDevice) message.obj;
1674                    if (mConnectedDevicesList.contains(device)) {
1675                        terminateScoUsingVirtualVoiceCall();
1676                    }
1677                    break;
1678                case START_VR_TIMEOUT:
1679                    if (mWaitingForVoiceRecognition) {
1680                        device = (BluetoothDevice) message.obj;
1681                        mWaitingForVoiceRecognition = false;
1682                        Log.e(TAG, "Timeout waiting for voice" +
1683                                             "recognition to start");
1684                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1685                                               0, getByteAddress(device));
1686                    }
1687                    break;
1688                case STACK_EVENT:
1689                    StackEvent event = (StackEvent) message.obj;
1690                    if (DBG) {
1691                        log("event type: " + event.type);
1692                    }
1693                    switch (event.type) {
1694                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1695                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1696                            if (device1 != null && device1.equals(event.device)) {
1697                                Log.d(TAG, "remove connect timeout for device = " + device1);
1698                                removeMessages(CONNECT_TIMEOUT);
1699                            }
1700                            processConnectionEvent(event.valueInt, event.device);
1701                            break;
1702                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1703                            processAudioEvent(event.valueInt, event.device);
1704                            break;
1705                        case EVENT_TYPE_VR_STATE_CHANGED:
1706                            processVrEvent(event.valueInt,event.device);
1707                            break;
1708                        case EVENT_TYPE_ANSWER_CALL:
1709                            //TODO(BT) could answer call happen on Connected state?
1710                            processAnswerCall(event.device);
1711                            break;
1712                        case EVENT_TYPE_HANGUP_CALL:
1713                            // TODO(BT) could hangup call happen on Connected state?
1714                            processHangupCall(event.device);
1715                            break;
1716                        case EVENT_TYPE_VOLUME_CHANGED:
1717                            processVolumeEvent(event.valueInt, event.valueInt2,
1718                                                    event.device);
1719                            break;
1720                        case EVENT_TYPE_DIAL_CALL:
1721                            processDialCall(event.valueString, event.device);
1722                            break;
1723                        case EVENT_TYPE_SEND_DTMF:
1724                            processSendDtmf(event.valueInt, event.device);
1725                            break;
1726                        case EVENT_TYPE_NOICE_REDUCTION:
1727                            processNoiceReductionEvent(event.valueInt, event.device);
1728                            break;
1729                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1730                            processSubscriberNumberRequest(event.device);
1731                            break;
1732                        case EVENT_TYPE_AT_CIND:
1733                            processAtCind(event.device);
1734                            break;
1735                        case EVENT_TYPE_AT_CHLD:
1736                            processAtChld(event.valueInt, event.device);
1737                            break;
1738                        case EVENT_TYPE_AT_COPS:
1739                            processAtCops(event.device);
1740                            break;
1741                        case EVENT_TYPE_AT_CLCC:
1742                            processAtClcc(event.device);
1743                            break;
1744                        case EVENT_TYPE_UNKNOWN_AT:
1745                            processUnknownAt(event.valueString,event.device);
1746                            break;
1747                        case EVENT_TYPE_KEY_PRESSED:
1748                            processKeyPressed(event.device);
1749                            break;
1750                        default:
1751                            Log.e(TAG, "Unexpected event: " + event.type);
1752                            break;
1753                    }
1754                    break;
1755                default:
1756                    return NOT_HANDLED;
1757            }
1758            return retValue;
1759        }
1760
1761        // in MultiHFPending state
1762        private void processConnectionEvent(int state, BluetoothDevice device) {
1763            Log.d(TAG, "processConnectionEvent state = " + state +
1764                                     ", device = " + device);
1765            switch (state) {
1766                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1767                    if (mConnectedDevicesList.contains(device)) {
1768                        if (mMultiDisconnectDevice != null &&
1769                                mMultiDisconnectDevice.equals(device)) {
1770                            mMultiDisconnectDevice = null;
1771
1772                          synchronized (HeadsetStateMachine.this) {
1773                              mConnectedDevicesList.remove(device);
1774                              mHeadsetAudioParam.remove(device);
1775                              mHeadsetBrsf.remove(device);
1776                              Log.d(TAG, "device " + device.getAddress() +
1777                                      " is removed in MultiHFPending state");
1778                              broadcastConnectionState(device,
1779                                        BluetoothProfile.STATE_DISCONNECTED,
1780                                        BluetoothProfile.STATE_DISCONNECTING);
1781                          }
1782
1783                          if (mTargetDevice != null) {
1784                              if (!connectHfpNative(getByteAddress(mTargetDevice))) {
1785
1786                                broadcastConnectionState(mTargetDevice,
1787                                          BluetoothProfile.STATE_DISCONNECTED,
1788                                          BluetoothProfile.STATE_CONNECTING);
1789                                  synchronized (HeadsetStateMachine.this) {
1790                                      mTargetDevice = null;
1791                                      if (mConnectedDevicesList.size() == 0) {
1792                                          // Should be not in this state since it has at least
1793                                          // one HF connected in MultiHFPending state
1794                                          Log.d(TAG, "Should be not in this state, error handling");
1795                                          transitionTo(mDisconnected);
1796                                      }
1797                                      else {
1798                                          processMultiHFConnected(device);
1799                                      }
1800                                  }
1801                              }
1802                          } else {
1803                              synchronized (HeadsetStateMachine.this) {
1804                                  mIncomingDevice = null;
1805                                  if (mConnectedDevicesList.size() == 0) {
1806                                      transitionTo(mDisconnected);
1807                                  }
1808                                  else {
1809                                      processMultiHFConnected(device);
1810                                  }
1811                              }
1812                           }
1813                        } else {
1814                            /* Another HF disconnected when one HF is connecting */
1815                            synchronized (HeadsetStateMachine.this) {
1816                              mConnectedDevicesList.remove(device);
1817                              mHeadsetAudioParam.remove(device);
1818                              mHeadsetBrsf.remove(device);
1819                              Log.d(TAG, "device " + device.getAddress() +
1820                                           " is removed in MultiHFPending state");
1821                            }
1822                            broadcastConnectionState(device,
1823                                BluetoothProfile.STATE_DISCONNECTED,
1824                                BluetoothProfile.STATE_CONNECTED);
1825                        }
1826                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1827
1828                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1829                                                 BluetoothProfile.STATE_CONNECTING);
1830                        synchronized (HeadsetStateMachine.this) {
1831                            mTargetDevice = null;
1832                            if (mConnectedDevicesList.size() == 0) {
1833                                transitionTo(mDisconnected);
1834                            }
1835                            else
1836                            {
1837                               if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1838                                   transitionTo(mAudioOn);
1839                               else transitionTo(mConnected);
1840                            }
1841                        }
1842                    } else {
1843                        Log.e(TAG, "Unknown device Disconnected: " + device);
1844                    }
1845                    break;
1846            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1847                /* Outgoing disconnection for device failed */
1848                if (mConnectedDevicesList.contains(device)) {
1849
1850                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1851                                             BluetoothProfile.STATE_DISCONNECTING);
1852                    if (mTargetDevice != null) {
1853                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1854                                                 BluetoothProfile.STATE_CONNECTING);
1855                    }
1856                    synchronized (HeadsetStateMachine.this) {
1857                        mTargetDevice = null;
1858                        if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1859                            transitionTo(mAudioOn);
1860                        else transitionTo(mConnected);
1861                    }
1862                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1863
1864                    synchronized (HeadsetStateMachine.this) {
1865                            mCurrentDevice = device;
1866                            mConnectedDevicesList.add(device);
1867                            Log.d(TAG, "device " + device.getAddress() +
1868                                      " is added in MultiHFPending state");
1869                            mTargetDevice = null;
1870                            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1871                                transitionTo(mAudioOn);
1872                            else transitionTo(mConnected);
1873                    }
1874
1875                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1876                                             BluetoothProfile.STATE_CONNECTING);
1877                    configAudioParameters(device);
1878                } else {
1879                    Log.w(TAG, "Some other incoming HF connected" +
1880                                          "in Multi Pending state");
1881                    if (okToConnect(device) &&
1882                            (mConnectedDevicesList.size() < max_hf_connections)) {
1883                        Log.i(TAG,"Incoming Hf accepted");
1884                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1885                                         BluetoothProfile.STATE_DISCONNECTED);
1886                        synchronized (HeadsetStateMachine.this) {
1887                            if (!mConnectedDevicesList.contains(device)) {
1888                                mCurrentDevice = device;
1889                                mConnectedDevicesList.add(device);
1890                                Log.d(TAG, "device " + device.getAddress() +
1891                                            " is added in MultiHFPending state");
1892                            }
1893                        }
1894                        configAudioParameters(device);
1895                    } else {
1896                        // reject the connection and stay in Pending state itself
1897                        Log.i(TAG,"Incoming Hf rejected. priority=" +
1898                                          mService.getPriority(device) +
1899                                  " bondState=" + device.getBondState());
1900                        disconnectHfpNative(getByteAddress(device));
1901                        // the other profile connection should be initiated
1902                        AdapterService adapterService = AdapterService.getAdapterService();
1903                        if (adapterService != null) {
1904                            adapterService.connectOtherProfile(device,
1905                                          AdapterService.PROFILE_CONN_REJECTED);
1906                        }
1907                    }
1908                }
1909                break;
1910            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
1911                if (mConnectedDevicesList.contains(device)) {
1912                    Log.e(TAG, "current device tries to connect back");
1913                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1914                    if (DBG) {
1915                        log("Stack and target device are connecting");
1916                    }
1917                }
1918                else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1919                    Log.e(TAG, "Another connecting event on" +
1920                                              "the incoming device");
1921                }
1922                break;
1923            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
1924                if (mConnectedDevicesList.contains(device)) {
1925                    if (DBG) {
1926                        log("stack is disconnecting mCurrentDevice");
1927                    }
1928                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1929                    Log.e(TAG, "TargetDevice is getting disconnected");
1930                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1931                    Log.e(TAG, "IncomingDevice is getting disconnected");
1932                } else {
1933                    Log.e(TAG, "Disconnecting unknow device: " + device);
1934                }
1935                break;
1936            default:
1937                Log.e(TAG, "Incorrect state: " + state);
1938                break;
1939            }
1940        }
1941
1942        private void processAudioEvent(int state, BluetoothDevice device) {
1943            if (!mConnectedDevicesList.contains(device)) {
1944                Log.e(TAG, "Audio changed on disconnected device: " + device);
1945                return;
1946            }
1947
1948            switch (state) {
1949                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1950                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1951                    setAudioParameters(device); /* Set proper Audio Parameters. */
1952                    mAudioManager.setBluetoothScoOn(true);
1953                    mActiveScoDevice = device;
1954                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1955                            BluetoothHeadset.STATE_AUDIO_CONNECTING);
1956                    /* The state should be still in MultiHFPending state when
1957                       audio connected since other device is still connecting/
1958                       disconnecting */
1959                    break;
1960                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1961                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1962                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1963                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1964                    break;
1965                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1966                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1967                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1968                        mAudioManager.setBluetoothScoOn(false);
1969                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1970                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
1971                    }
1972                    /* The state should be still in MultiHFPending state when audio
1973                       disconnected since other device is still connecting/
1974                       disconnecting */
1975                    break;
1976
1977                default:
1978                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1979                    break;
1980            }
1981        }
1982
1983        private void processMultiHFConnected(BluetoothDevice device) {
1984            log("MultiHFPending state: processMultiHFConnected");
1985            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1986                log ("mActiveScoDevice is disconnected, setting it to null");
1987                mActiveScoDevice = null;
1988            }
1989            /* Assign the current activedevice again if the disconnected
1990               device equals to the current active device */
1991            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1992                int deviceSize = mConnectedDevicesList.size();
1993                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1994            }
1995            // The disconnected device is not current active device
1996            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1997                transitionTo(mAudioOn);
1998            else transitionTo(mConnected);
1999            log("processMultiHFConnected , the latest mCurrentDevice is:"
2000                                            + mCurrentDevice);
2001            log("MultiHFPending state: processMultiHFConnected ," +
2002                         "fake broadcasting for mCurrentDevice");
2003            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
2004                            BluetoothProfile.STATE_DISCONNECTED);
2005        }
2006
2007    }
2008
2009
2010    private ServiceConnection mConnection = new ServiceConnection() {
2011        public void onServiceConnected(ComponentName className, IBinder service) {
2012            if (DBG) Log.d(TAG, "Proxy object connected");
2013            mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
2014        }
2015
2016        public void onServiceDisconnected(ComponentName className) {
2017            if (DBG) Log.d(TAG, "Proxy object disconnected");
2018            mPhoneProxy = null;
2019        }
2020    };
2021
2022    // HFP Connection state of the device could be changed by the state machine
2023    // in separate thread while this method is executing.
2024    int getConnectionState(BluetoothDevice device) {
2025        if (getCurrentState() == mDisconnected) {
2026            if (DBG) Log.d(TAG, "currentState is Disconnected");
2027            return BluetoothProfile.STATE_DISCONNECTED;
2028        }
2029
2030        synchronized (this) {
2031            IState currentState = getCurrentState();
2032            if (DBG) Log.d(TAG, "currentState = " + currentState);
2033            if (currentState == mPending) {
2034                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2035                    return BluetoothProfile.STATE_CONNECTING;
2036                }
2037                if (mConnectedDevicesList.contains(device)) {
2038                    return BluetoothProfile.STATE_DISCONNECTING;
2039                }
2040                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2041                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2042                }
2043                return BluetoothProfile.STATE_DISCONNECTED;
2044            }
2045
2046            if (currentState == mMultiHFPending) {
2047                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2048                    return BluetoothProfile.STATE_CONNECTING;
2049                }
2050                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2051                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2052                }
2053                if (mConnectedDevicesList.contains(device)) {
2054                    if ((mMultiDisconnectDevice != null) &&
2055                            (!mMultiDisconnectDevice.equals(device))) {
2056                        // The device is still connected
2057                        return BluetoothProfile.STATE_CONNECTED;
2058                    }
2059                    return BluetoothProfile.STATE_DISCONNECTING;
2060                }
2061                return BluetoothProfile.STATE_DISCONNECTED;
2062            }
2063
2064            if (currentState == mConnected || currentState == mAudioOn) {
2065                if (mConnectedDevicesList.contains(device)) {
2066                    return BluetoothProfile.STATE_CONNECTED;
2067                }
2068                return BluetoothProfile.STATE_DISCONNECTED;
2069            } else {
2070                Log.e(TAG, "Bad currentState: " + currentState);
2071                return BluetoothProfile.STATE_DISCONNECTED;
2072            }
2073        }
2074    }
2075
2076    List<BluetoothDevice> getConnectedDevices() {
2077        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2078        synchronized(this) {
2079            for (int i = 0; i < mConnectedDevicesList.size(); i++)
2080                devices.add(mConnectedDevicesList.get(i));
2081            }
2082
2083        return devices;
2084    }
2085
2086    boolean isAudioOn() {
2087        return (getCurrentState() == mAudioOn);
2088    }
2089
2090    boolean isAudioConnected(BluetoothDevice device) {
2091        synchronized(this) {
2092
2093            /*  Additional check for audio state included for the case when PhoneApp queries
2094            Bluetooth Audio state, before we receive the close event from the stack for the
2095            sco disconnect issued in AudioOn state. This was causing a mismatch in the
2096            Incall screen UI. */
2097
2098            if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
2099                && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
2100            {
2101                return true;
2102            }
2103        }
2104        return false;
2105    }
2106
2107    int getAudioState(BluetoothDevice device) {
2108        synchronized(this) {
2109            if (mConnectedDevicesList.size() == 0) {
2110                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
2111            }
2112        }
2113        return mAudioState;
2114    }
2115
2116    private void processVrEvent(int state, BluetoothDevice device) {
2117
2118        if(device == null) {
2119            Log.w(TAG, "processVrEvent device is null");
2120            return;
2121        }
2122        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " +
2123            mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition +
2124            " isInCall: " + isInCall());
2125        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2126            if (!isVirtualCallInProgress() &&
2127                !isInCall())
2128            {
2129                try {
2130                    mService.startActivity(sVoiceCommandIntent);
2131                } catch (ActivityNotFoundException e) {
2132                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2133                                        0, getByteAddress(device));
2134                    return;
2135                }
2136                expectVoiceRecognition(device);
2137            }
2138        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
2139            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
2140            {
2141                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2142                                         0, getByteAddress(device));
2143                mVoiceRecognitionStarted = false;
2144                mWaitingForVoiceRecognition = false;
2145                if (!isInCall() && (mActiveScoDevice != null)) {
2146                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2147                    mAudioManager.setParameters("A2dpSuspended=false");
2148                }
2149            }
2150            else
2151            {
2152                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2153                                        0, getByteAddress(device));
2154            }
2155        } else {
2156            Log.e(TAG, "Bad Voice Recognition state: " + state);
2157        }
2158    }
2159
2160    private void processLocalVrEvent(int state)
2161    {
2162        BluetoothDevice device = null;
2163        if (state == HeadsetHalConstants.VR_STATE_STARTED)
2164        {
2165            boolean needAudio = true;
2166            if (mVoiceRecognitionStarted || isInCall())
2167            {
2168                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() +
2169                    " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
2170                return;
2171            }
2172            mVoiceRecognitionStarted = true;
2173
2174            if (mWaitingForVoiceRecognition)
2175            {
2176                device = getDeviceForMessage(START_VR_TIMEOUT);
2177                if (device == null)
2178                    return;
2179
2180                Log.d(TAG, "Voice recognition started successfully");
2181                mWaitingForVoiceRecognition = false;
2182                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2183                                        0, getByteAddress(device));
2184                removeMessages(START_VR_TIMEOUT);
2185            }
2186            else
2187            {
2188                Log.d(TAG, "Voice recognition started locally");
2189                needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice));
2190                if (mCurrentDevice != null)
2191                    device = mCurrentDevice;
2192            }
2193
2194            if (needAudio && !isAudioOn())
2195            {
2196                Log.d(TAG, "Initiating audio connection for Voice Recognition");
2197                // At this stage, we need to be sure that AVDTP is not streaming. This is needed
2198                // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
2199                // streaming state while a SCO connection is established.
2200                // This is needed for VoiceDial scenario alone and not for
2201                // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
2202                // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
2203                // Whereas for VoiceDial we want to activate the SCO connection but we are still
2204                // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
2205                mAudioManager.setParameters("A2dpSuspended=true");
2206                connectAudioNative(getByteAddress(device));
2207            }
2208
2209            if (mStartVoiceRecognitionWakeLock.isHeld()) {
2210                mStartVoiceRecognitionWakeLock.release();
2211            }
2212        }
2213        else
2214        {
2215            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted +
2216                " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
2217            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
2218            {
2219                mVoiceRecognitionStarted = false;
2220                mWaitingForVoiceRecognition = false;
2221
2222                if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice))
2223                                && !isInCall() && mActiveScoDevice != null) {
2224                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2225                    mAudioManager.setParameters("A2dpSuspended=false");
2226                }
2227            }
2228        }
2229    }
2230
2231    private synchronized void expectVoiceRecognition(BluetoothDevice device) {
2232        mWaitingForVoiceRecognition = true;
2233        Message m = obtainMessage(START_VR_TIMEOUT);
2234        m.obj = getMatchingDevice(device);
2235        sendMessageDelayed(m, START_VR_TIMEOUT_VALUE);
2236
2237        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
2238            mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
2239        }
2240    }
2241
2242    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2243        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
2244        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
2245        int connectionState;
2246        synchronized (this) {
2247            for (BluetoothDevice device : bondedDevices) {
2248                ParcelUuid[] featureUuids = device.getUuids();
2249                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
2250                    continue;
2251                }
2252                connectionState = getConnectionState(device);
2253                for(int i = 0; i < states.length; i++) {
2254                    if (connectionState == states[i]) {
2255                        deviceList.add(device);
2256                    }
2257                }
2258            }
2259        }
2260        return deviceList;
2261    }
2262
2263    private BluetoothDevice getDeviceForMessage(int what)
2264    {
2265        if (what == CONNECT_TIMEOUT) {
2266            log("getDeviceForMessage: returning mTargetDevice for what=" + what);
2267            return mTargetDevice;
2268        }
2269        if (mConnectedDevicesList.size() == 0) {
2270            log("getDeviceForMessage: No connected device. what=" + what);
2271            return null;
2272        }
2273        for (BluetoothDevice device : mConnectedDevicesList)
2274        {
2275            if (getHandler().hasMessages(what, device))
2276            {
2277                log("getDeviceForMessage: returning " + device);
2278                return device;
2279            }
2280        }
2281        log("getDeviceForMessage: No matching device for " + what + ". Returning null");
2282        return null;
2283    }
2284
2285    private BluetoothDevice getMatchingDevice(BluetoothDevice device)
2286    {
2287        for (BluetoothDevice matchingDevice : mConnectedDevicesList)
2288        {
2289            if (matchingDevice.equals(device))
2290            {
2291                return matchingDevice;
2292            }
2293        }
2294        return null;
2295    }
2296
2297    // This method does not check for error conditon (newState == prevState)
2298    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
2299        log("Connection state " + device + ": " + prevState + "->" + newState);
2300        if(prevState == BluetoothProfile.STATE_CONNECTED) {
2301            // Headset is disconnecting, stop Virtual call if active.
2302            terminateScoUsingVirtualVoiceCall();
2303        }
2304
2305        /* Notifying the connection state change of the profile before sending the intent for
2306           connection state change, as it was causing a race condition, with the UI not being
2307           updated with the correct connection state. */
2308        mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,
2309                                                     newState, prevState);
2310        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
2311        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2312        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2313        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2314        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
2315                HeadsetService.BLUETOOTH_PERM);
2316    }
2317
2318    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
2319        if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2320            // When SCO gets disconnected during call transfer, Virtual call
2321            //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
2322            terminateScoUsingVirtualVoiceCall();
2323        }
2324        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
2325        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2326        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2327        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2328        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
2329                HeadsetService.BLUETOOTH_PERM);
2330        log("Audio state " + device + ": " + prevState + "->" + newState);
2331    }
2332
2333    /*
2334     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
2335     */
2336    private void broadcastVendorSpecificEventIntent(String command,
2337                                                    int companyId,
2338                                                    int commandType,
2339                                                    Object[] arguments,
2340                                                    BluetoothDevice device) {
2341        log("broadcastVendorSpecificEventIntent(" + command + ")");
2342        Intent intent =
2343                new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
2344        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
2345        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
2346                        commandType);
2347        // assert: all elements of args are Serializable
2348        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
2349        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2350
2351        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
2352            + "." + Integer.toString(companyId));
2353
2354        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
2355                HeadsetService.BLUETOOTH_PERM);
2356    }
2357
2358    private void configAudioParameters(BluetoothDevice device)
2359    {
2360        // Reset NREC on connect event. Headset will override later
2361        HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>();
2362        AudioParamConfig.put("NREC", 1);
2363        mHeadsetAudioParam.put(device, AudioParamConfig);
2364        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" +
2365                                    HEADSET_NREC + "=on");
2366        Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = " +
2367                      AudioParamConfig.get("NREC"));
2368    }
2369
2370    private void setAudioParameters(BluetoothDevice device)
2371    {
2372        // 1. update nrec value
2373        // 2. update headset name
2374        HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device);
2375        int mNrec = AudioParam.get("NREC");
2376
2377        if (mNrec == 1) {
2378            Log.d(TAG, "Set NREC: 1 for device:" + device);
2379            mAudioManager.setParameters(HEADSET_NREC + "=on");
2380        } else {
2381            Log.d(TAG, "Set NREC: 0 for device:" + device);
2382            mAudioManager.setParameters(HEADSET_NREC + "=off");
2383        }
2384        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device));
2385    }
2386
2387    private String parseUnknownAt(String atString)
2388    {
2389        StringBuilder atCommand = new StringBuilder(atString.length());
2390        String result = null;
2391
2392        for (int i = 0; i < atString.length(); i++) {
2393            char c = atString.charAt(i);
2394            if (c == '"') {
2395                int j = atString.indexOf('"', i + 1 );  // search for closing "
2396                if (j == -1) {  // unmatched ", insert one.
2397                    atCommand.append(atString.substring(i, atString.length()));
2398                    atCommand.append('"');
2399                    break;
2400                }
2401                atCommand.append(atString.substring(i, j + 1));
2402                i = j;
2403            } else if (c != ' ') {
2404                atCommand.append(Character.toUpperCase(c));
2405            }
2406        }
2407        result = atCommand.toString();
2408        return result;
2409    }
2410
2411    private int getAtCommandType(String atCommand)
2412    {
2413        int commandType = mPhonebook.TYPE_UNKNOWN;
2414        String atString = null;
2415        atCommand = atCommand.trim();
2416        if (atCommand.length() > 5)
2417        {
2418            atString = atCommand.substring(5);
2419            if (atString.startsWith("?"))     // Read
2420                commandType = mPhonebook.TYPE_READ;
2421            else if (atString.startsWith("=?"))   // Test
2422                commandType = mPhonebook.TYPE_TEST;
2423            else if (atString.startsWith("="))   // Set
2424                commandType = mPhonebook.TYPE_SET;
2425            else
2426                commandType = mPhonebook.TYPE_UNKNOWN;
2427        }
2428        return commandType;
2429    }
2430
2431    /* Method to check if Virtual Call in Progress */
2432    private boolean isVirtualCallInProgress() {
2433        return mVirtualCallStarted;
2434    }
2435
2436    void setVirtualCallInProgress(boolean state) {
2437        mVirtualCallStarted = state;
2438    }
2439
2440    /* NOTE: Currently the VirtualCall API does not support handling of
2441    call transfers. If it is initiated from the handsfree device,
2442    HeadsetStateMachine will end the virtual call by calling
2443    terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
2444    synchronized boolean initiateScoUsingVirtualVoiceCall() {
2445        if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
2446        // 1. Check if the SCO state is idle
2447        if (isInCall() || mVoiceRecognitionStarted) {
2448            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
2449            return false;
2450        }
2451
2452        // 2. Send virtual phone state changed to initialize SCO
2453        processCallState(new HeadsetCallState(0, 0,
2454            HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
2455        processCallState(new HeadsetCallState(0, 0,
2456            HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
2457        processCallState(new HeadsetCallState(1, 0,
2458            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2459        setVirtualCallInProgress(true);
2460        // Done
2461        if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
2462        return true;
2463    }
2464
2465    synchronized boolean terminateScoUsingVirtualVoiceCall() {
2466        if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
2467
2468        if (!isVirtualCallInProgress()) {
2469            Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+
2470                "No present call to terminate");
2471            return false;
2472        }
2473
2474        // 2. Send virtual phone state changed to close SCO
2475        processCallState(new HeadsetCallState(0, 0,
2476            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2477        setVirtualCallInProgress(false);
2478        // Done
2479        if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
2480        return true;
2481    }
2482
2483    private void processAnswerCall(BluetoothDevice device) {
2484        if(device == null) {
2485            Log.w(TAG, "processAnswerCall device is null");
2486            return;
2487        }
2488
2489        if (mPhoneProxy != null) {
2490            try {
2491                mPhoneProxy.answerCall();
2492            } catch (RemoteException e) {
2493                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2494            }
2495        } else {
2496            Log.e(TAG, "Handsfree phone proxy null for answering call");
2497        }
2498    }
2499
2500    private void processHangupCall(BluetoothDevice device) {
2501        if(device == null) {
2502            Log.w(TAG, "processHangupCall device is null");
2503            return;
2504        }
2505        // Close the virtual call if active. Virtual call should be
2506        // terminated for CHUP callback event
2507        if (isVirtualCallInProgress()) {
2508            terminateScoUsingVirtualVoiceCall();
2509        } else {
2510            if (mPhoneProxy != null) {
2511                try {
2512                    mPhoneProxy.hangupCall();
2513                } catch (RemoteException e) {
2514                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2515                }
2516            } else {
2517                Log.e(TAG, "Handsfree phone proxy null for hanging up call");
2518            }
2519        }
2520    }
2521
2522    private void processDialCall(String number, BluetoothDevice device) {
2523        if(device == null) {
2524            Log.w(TAG, "processDialCall device is null");
2525            return;
2526        }
2527
2528        String dialNumber;
2529        if ((number == null) || (number.length() == 0)) {
2530            dialNumber = mPhonebook.getLastDialledNumber();
2531            if (dialNumber == null) {
2532                if (DBG) log("processDialCall, last dial number null");
2533                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2534                                       getByteAddress(device));
2535                return;
2536            }
2537        } else if (number.charAt(0) == '>') {
2538            // Yuck - memory dialling requested.
2539            // Just dial last number for now
2540            if (number.startsWith(">9999")) {   // for PTS test
2541                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2542                                       getByteAddress(device));
2543                return;
2544            }
2545            if (DBG) log("processDialCall, memory dial do last dial for now");
2546            dialNumber = mPhonebook.getLastDialledNumber();
2547            if (dialNumber == null) {
2548                if (DBG) log("processDialCall, last dial number null");
2549                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2550                                       getByteAddress(device));
2551                return;
2552            }
2553        } else {
2554            // Remove trailing ';'
2555            if (number.charAt(number.length() - 1) == ';') {
2556                number = number.substring(0, number.length() - 1);
2557            }
2558
2559            dialNumber = PhoneNumberUtils.convertPreDial(number);
2560        }
2561        // Check for virtual call to terminate before sending Call Intent
2562        terminateScoUsingVirtualVoiceCall();
2563
2564        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
2565                                   Uri.fromParts(SCHEME_TEL, dialNumber, null));
2566        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2567        mService.startActivity(intent);
2568        // TODO(BT) continue send OK reults code after call starts
2569        //          hold wait lock, start a timer, set wait call flag
2570        //          Get call started indication from bluetooth phone
2571        mDialingOut = true;
2572        Message m = obtainMessage(DIALING_OUT_TIMEOUT);
2573        m.obj = getMatchingDevice(device);
2574        sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE);
2575    }
2576
2577    private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) {
2578        if(device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) {
2579            Log.w(TAG, "ignore processVolumeEvent");
2580            return;
2581        }
2582
2583        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
2584            mPhoneState.setSpeakerVolume(volume);
2585            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
2586            mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
2587        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
2588            mPhoneState.setMicVolume(volume);
2589        } else {
2590            Log.e(TAG, "Bad voluem type: " + volumeType);
2591        }
2592    }
2593
2594    private void processSendDtmf(int dtmf, BluetoothDevice device) {
2595        if(device == null) {
2596            Log.w(TAG, "processSendDtmf device is null");
2597            return;
2598        }
2599
2600        if (mPhoneProxy != null) {
2601            try {
2602                mPhoneProxy.sendDtmf(dtmf);
2603            } catch (RemoteException e) {
2604                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2605            }
2606        } else {
2607            Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
2608        }
2609    }
2610
2611    private void processCallState(HeadsetCallState callState) {
2612        processCallState(callState, false);
2613    }
2614
2615    private void processCallState(HeadsetCallState callState,
2616        boolean isVirtualCall) {
2617        mPhoneState.setNumActiveCall(callState.mNumActive);
2618        mPhoneState.setNumHeldCall(callState.mNumHeld);
2619        mPhoneState.setCallState(callState.mCallState);
2620        if (mDialingOut) {
2621            if (callState.mCallState ==
2622                HeadsetHalConstants.CALL_STATE_DIALING) {
2623                BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT);
2624                if (device == null) {
2625                    return;
2626                }
2627                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2628                                                       0, getByteAddress(device));
2629                removeMessages(DIALING_OUT_TIMEOUT);
2630            } else if (callState.mCallState ==
2631                HeadsetHalConstants.CALL_STATE_ACTIVE || callState.mCallState
2632                == HeadsetHalConstants.CALL_STATE_IDLE) {
2633                mDialingOut = false;
2634            }
2635        }
2636
2637        /* Set ActiveScoDevice to null when call ends */
2638        if ((mActiveScoDevice != null) && !isInCall() &&
2639                callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE)
2640            mActiveScoDevice = null;
2641
2642        log("mNumActive: " + callState.mNumActive + " mNumHeld: " +
2643            callState.mNumHeld +" mCallState: " + callState.mCallState);
2644        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
2645        if(!isVirtualCall) {
2646            /* Not a Virtual call request. End the virtual call, if running,
2647            before sending phoneStateChangeNative to BTIF */
2648            terminateScoUsingVirtualVoiceCall();
2649        }
2650        if (getCurrentState() != mDisconnected) {
2651            phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2652                callState.mCallState, callState.mNumber, callState.mType);
2653        }
2654    }
2655
2656    // 1 enable noice reduction
2657    // 0 disable noice reduction
2658    private void processNoiceReductionEvent(int enable, BluetoothDevice device) {
2659        HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device);
2660        if (enable == 1)
2661            AudioParamNrec.put("NREC", 1);
2662        else
2663            AudioParamNrec.put("NREC", 0);
2664        Log.d(TAG, "NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC"));
2665    }
2666
2667    // 2 - WBS on
2668    // 1 - NBS on
2669    private void processWBSEvent(int enable, BluetoothDevice device) {
2670        if (enable == 2) {
2671            Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " +
2672                        device.getName() + " - " + device.getAddress());
2673            mAudioManager.setParameters(HEADSET_WBS + "=on");
2674        } else {
2675            Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " +
2676                        device.getName() + " - " + device.getAddress());
2677            mAudioManager.setParameters(HEADSET_WBS + "=off");
2678        }
2679    }
2680
2681    private void processAtChld(int chld, BluetoothDevice device) {
2682        if(device == null) {
2683            Log.w(TAG, "processAtChld device is null");
2684            return;
2685        }
2686
2687        if (mPhoneProxy != null) {
2688            try {
2689                if (mPhoneProxy.processChld(chld)) {
2690                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2691                                               0, getByteAddress(device));
2692                } else {
2693                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2694                                               0, getByteAddress(device));
2695                }
2696            } catch (RemoteException e) {
2697                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2698                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2699                                               0, getByteAddress(device));
2700            }
2701        } else {
2702            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
2703            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2704                                               0, getByteAddress(device));
2705        }
2706    }
2707
2708    private void processSubscriberNumberRequest(BluetoothDevice device) {
2709        if(device == null) {
2710            Log.w(TAG, "processSubscriberNumberRequest device is null");
2711            return;
2712        }
2713
2714        if (mPhoneProxy != null) {
2715            try {
2716                String number = mPhoneProxy.getSubscriberNumber();
2717                if (number != null) {
2718                    atResponseStringNative("+CNUM: ,\"" + number + "\"," +
2719                                                PhoneNumberUtils.toaFromString(number) +
2720                                                ",,4", getByteAddress(device));
2721                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2722                                                0, getByteAddress(device));
2723                } else {
2724                    Log.e(TAG, "getSubscriberNumber returns null");
2725                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2726                                                0, getByteAddress(device));
2727                }
2728            } catch (RemoteException e) {
2729                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2730                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2731                                                 0, getByteAddress(device));
2732            }
2733        } else {
2734            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
2735        }
2736    }
2737
2738    private void processAtCind(BluetoothDevice device) {
2739        int call, call_setup;
2740
2741        if(device == null) {
2742            Log.w(TAG, "processAtCind device is null");
2743            return;
2744        }
2745
2746        /* Handsfree carkits expect that +CIND is properly responded to
2747         Hence we ensure that a proper response is sent
2748         for the virtual call too.*/
2749        if (isVirtualCallInProgress()) {
2750            call = 1;
2751            call_setup = 0;
2752        } else {
2753            // regular phone call
2754            call = mPhoneState.getNumActiveCall();
2755            call_setup = mPhoneState.getNumHeldCall();
2756        }
2757
2758        cindResponseNative(mPhoneState.getService(), call,
2759                           call_setup, mPhoneState.getCallState(),
2760                           mPhoneState.getSignal(), mPhoneState.getRoam(),
2761                           mPhoneState.getBatteryCharge(), getByteAddress(device));
2762    }
2763
2764    private void processAtCops(BluetoothDevice device) {
2765        if(device == null) {
2766            Log.w(TAG, "processAtCops device is null");
2767            return;
2768        }
2769
2770        if (mPhoneProxy != null) {
2771            try {
2772                String operatorName = mPhoneProxy.getNetworkOperator();
2773                if (operatorName == null) {
2774                    operatorName = "";
2775                }
2776                copsResponseNative(operatorName, getByteAddress(device));
2777            } catch (RemoteException e) {
2778                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2779                copsResponseNative("", getByteAddress(device));
2780            }
2781        } else {
2782            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
2783            copsResponseNative("", getByteAddress(device));
2784        }
2785    }
2786
2787    private void processAtClcc(BluetoothDevice device) {
2788        if(device == null) {
2789            Log.w(TAG, "processAtClcc device is null");
2790            return;
2791        }
2792
2793        if (mPhoneProxy != null) {
2794            try {
2795                if(isVirtualCallInProgress()) {
2796                    String phoneNumber = "";
2797                    int type = PhoneNumberUtils.TOA_Unknown;
2798                    try {
2799                        phoneNumber = mPhoneProxy.getSubscriberNumber();
2800                        type = PhoneNumberUtils.toaFromString(phoneNumber);
2801                    } catch (RemoteException ee) {
2802                        Log.e(TAG, "Unable to retrieve phone number"+
2803                            "using IBluetoothHeadsetPhone proxy");
2804                        phoneNumber = "";
2805                    }
2806                    clccResponseNative(1, 0, 0, 0, false, phoneNumber, type,
2807                                                       getByteAddress(device));
2808                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2809                }
2810                else if (!mPhoneProxy.listCurrentCalls()) {
2811                    clccResponseNative(0, 0, 0, 0, false, "", 0,
2812                                                       getByteAddress(device));
2813                }
2814                else
2815                {
2816                    Log.d(TAG, "Starting CLCC response timeout for device: "
2817                                                                     + device);
2818                    Message m = obtainMessage(CLCC_RSP_TIMEOUT);
2819                    m.obj = getMatchingDevice(device);
2820                    sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE);
2821                }
2822            } catch (RemoteException e) {
2823                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2824                clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2825            }
2826        } else {
2827            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
2828            clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2829        }
2830    }
2831
2832    private void processAtCscs(String atString, int type, BluetoothDevice device) {
2833        log("processAtCscs - atString = "+ atString);
2834        if(mPhonebook != null) {
2835            mPhonebook.handleCscsCommand(atString, type, device);
2836        }
2837        else {
2838            Log.e(TAG, "Phonebook handle null for At+CSCS");
2839            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2840        }
2841    }
2842
2843    private void processAtCpbs(String atString, int type, BluetoothDevice device) {
2844        log("processAtCpbs - atString = "+ atString);
2845        if(mPhonebook != null) {
2846            mPhonebook.handleCpbsCommand(atString, type, device);
2847        }
2848        else {
2849            Log.e(TAG, "Phonebook handle null for At+CPBS");
2850            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2851        }
2852    }
2853
2854    private void processAtCpbr(String atString, int type, BluetoothDevice device) {
2855        log("processAtCpbr - atString = "+ atString);
2856        if(mPhonebook != null) {
2857            mPhonebook.handleCpbrCommand(atString, type, device);
2858        }
2859        else {
2860            Log.e(TAG, "Phonebook handle null for At+CPBR");
2861            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2862        }
2863    }
2864
2865    /**
2866     * Find a character ch, ignoring quoted sections.
2867     * Return input.length() if not found.
2868     */
2869    static private int findChar(char ch, String input, int fromIndex) {
2870        for (int i = fromIndex; i < input.length(); i++) {
2871            char c = input.charAt(i);
2872            if (c == '"') {
2873                i = input.indexOf('"', i + 1);
2874                if (i == -1) {
2875                    return input.length();
2876                }
2877            } else if (c == ch) {
2878                return i;
2879            }
2880        }
2881        return input.length();
2882    }
2883
2884    /**
2885     * Break an argument string into individual arguments (comma delimited).
2886     * Integer arguments are turned into Integer objects. Otherwise a String
2887     * object is used.
2888     */
2889    static private Object[] generateArgs(String input) {
2890        int i = 0;
2891        int j;
2892        ArrayList<Object> out = new ArrayList<Object>();
2893        while (i <= input.length()) {
2894            j = findChar(',', input, i);
2895
2896            String arg = input.substring(i, j);
2897            try {
2898                out.add(new Integer(arg));
2899            } catch (NumberFormatException e) {
2900                out.add(arg);
2901            }
2902
2903            i = j + 1; // move past comma
2904        }
2905        return out.toArray();
2906    }
2907
2908    /**
2909     * @return {@code true} if the given string is a valid vendor-specific AT command.
2910     */
2911    private boolean processVendorSpecificAt(String atString) {
2912        log("processVendorSpecificAt - atString = " + atString);
2913
2914        // Currently we accept only SET type commands.
2915        int indexOfEqual = atString.indexOf("=");
2916        if (indexOfEqual == -1) {
2917            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2918            return false;
2919        }
2920
2921        String command = atString.substring(0, indexOfEqual);
2922        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
2923        if (companyId == null) {
2924            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
2925            return false;
2926        }
2927
2928        String arg = atString.substring(indexOfEqual + 1);
2929        if (arg.startsWith("?")) {
2930            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2931            return false;
2932        }
2933
2934        Object[] args = generateArgs(arg);
2935        broadcastVendorSpecificEventIntent(command,
2936                                           companyId,
2937                                           BluetoothHeadset.AT_CMD_TYPE_SET,
2938                                           args,
2939                                           mCurrentDevice);
2940        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice));
2941        return true;
2942    }
2943
2944    private void processUnknownAt(String atString, BluetoothDevice device) {
2945        if(device == null) {
2946            Log.w(TAG, "processUnknownAt device is null");
2947            return;
2948        }
2949
2950        // TODO (BT)
2951        log("processUnknownAt - atString = "+ atString);
2952        String atCommand = parseUnknownAt(atString);
2953        int commandType = getAtCommandType(atCommand);
2954        if (atCommand.startsWith("+CSCS"))
2955            processAtCscs(atCommand.substring(5), commandType, device);
2956        else if (atCommand.startsWith("+CPBS"))
2957            processAtCpbs(atCommand.substring(5), commandType, device);
2958        else if (atCommand.startsWith("+CPBR"))
2959            processAtCpbr(atCommand.substring(5), commandType, device);
2960        else if (!processVendorSpecificAt(atCommand))
2961            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2962    }
2963
2964    private void processKeyPressed(BluetoothDevice device) {
2965        if(device == null) {
2966            Log.w(TAG, "processKeyPressed device is null");
2967            return;
2968        }
2969
2970        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
2971            if (mPhoneProxy != null) {
2972                try {
2973                    mPhoneProxy.answerCall();
2974                } catch (RemoteException e) {
2975                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2976                }
2977            } else {
2978                Log.e(TAG, "Handsfree phone proxy null for answering call");
2979            }
2980        } else if (mPhoneState.getNumActiveCall() > 0) {
2981            if (!isAudioOn())
2982            {
2983                connectAudioNative(getByteAddress(mCurrentDevice));
2984            }
2985            else
2986            {
2987                if (mPhoneProxy != null) {
2988                    try {
2989                        mPhoneProxy.hangupCall();
2990                    } catch (RemoteException e) {
2991                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
2992                    }
2993                } else {
2994                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
2995                }
2996            }
2997        } else {
2998            String dialNumber = mPhonebook.getLastDialledNumber();
2999            if (dialNumber == null) {
3000                if (DBG) log("processKeyPressed, last dial number null");
3001                return;
3002            }
3003            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
3004                                       Uri.fromParts(SCHEME_TEL, dialNumber, null));
3005            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3006            mService.startActivity(intent);
3007        }
3008    }
3009
3010    private void onConnectionStateChanged(int state, byte[] address) {
3011        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
3012        event.valueInt = state;
3013        event.device = getDevice(address);
3014        sendMessage(STACK_EVENT, event);
3015    }
3016
3017    private void onAudioStateChanged(int state, byte[] address) {
3018        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
3019        event.valueInt = state;
3020        event.device = getDevice(address);
3021        sendMessage(STACK_EVENT, event);
3022    }
3023
3024    private void onVrStateChanged(int state, byte[] address) {
3025        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
3026        event.valueInt = state;
3027        event.device = getDevice(address);
3028        sendMessage(STACK_EVENT, event);
3029    }
3030
3031    private void onAnswerCall(byte[] address) {
3032        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
3033        event.device = getDevice(address);
3034        sendMessage(STACK_EVENT, event);
3035    }
3036
3037    private void onHangupCall(byte[] address) {
3038        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
3039        event.device = getDevice(address);
3040        sendMessage(STACK_EVENT, event);
3041    }
3042
3043    private void onVolumeChanged(int type, int volume, byte[] address) {
3044        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
3045        event.valueInt = type;
3046        event.valueInt2 = volume;
3047        event.device = getDevice(address);
3048        sendMessage(STACK_EVENT, event);
3049    }
3050
3051    private void onDialCall(String number, byte[] address) {
3052        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
3053        event.valueString = number;
3054        event.device = getDevice(address);
3055        sendMessage(STACK_EVENT, event);
3056    }
3057
3058    private void onSendDtmf(int dtmf, byte[] address) {
3059        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
3060        event.valueInt = dtmf;
3061        event.device = getDevice(address);
3062        sendMessage(STACK_EVENT, event);
3063    }
3064
3065    private void onNoiceReductionEnable(boolean enable,  byte[] address) {
3066        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
3067        event.valueInt = enable ? 1 : 0;
3068        event.device = getDevice(address);
3069        sendMessage(STACK_EVENT, event);
3070    }
3071
3072    private void onWBS(int codec, byte[] address) {
3073        StackEvent event = new StackEvent(EVENT_TYPE_WBS);
3074        event.valueInt = codec;
3075        event.device = getDevice(address);
3076        sendMessage(STACK_EVENT, event);
3077    }
3078
3079    private void onAtChld(int chld, byte[] address) {
3080        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
3081        event.valueInt = chld;
3082        event.device = getDevice(address);
3083        sendMessage(STACK_EVENT, event);
3084    }
3085
3086    private void onAtCnum(byte[] address) {
3087        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
3088        event.device = getDevice(address);
3089        sendMessage(STACK_EVENT, event);
3090    }
3091
3092    private void onAtCind(byte[] address) {
3093        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
3094        event.device = getDevice(address);
3095        sendMessage(STACK_EVENT, event);
3096    }
3097
3098    private void onAtCops(byte[] address) {
3099        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
3100        event.device = getDevice(address);
3101        sendMessage(STACK_EVENT, event);
3102    }
3103
3104    private void onAtClcc(byte[] address) {
3105        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
3106        event.device = getDevice(address);
3107        sendMessage(STACK_EVENT, event);
3108    }
3109
3110    private void onUnknownAt(String atString, byte[] address) {
3111        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
3112        event.valueString = atString;
3113        event.device = getDevice(address);
3114        sendMessage(STACK_EVENT, event);
3115    }
3116
3117    private void onKeyPressed(byte[] address) {
3118        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
3119        event.device = getDevice(address);
3120        sendMessage(STACK_EVENT, event);
3121    }
3122
3123    private void processIntentBatteryChanged(Intent intent) {
3124        int batteryLevel = intent.getIntExtra("level", -1);
3125        int scale = intent.getIntExtra("scale", -1);
3126        if (batteryLevel == -1 || scale == -1 || scale == 0) {
3127            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
3128            return;
3129        }
3130        batteryLevel = batteryLevel * 5 / scale;
3131        mPhoneState.setBatteryCharge(batteryLevel);
3132    }
3133
3134    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
3135        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
3136                                 deviceState.mBatteryCharge);
3137    }
3138
3139    private void processSendClccResponse(HeadsetClccResponse clcc) {
3140        BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT);
3141        if (device == null) {
3142            return;
3143        }
3144        if (clcc.mIndex == 0) {
3145            removeMessages(CLCC_RSP_TIMEOUT);
3146        }
3147        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
3148                           clcc.mNumber, clcc.mType, getByteAddress(device));
3149    }
3150
3151    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
3152        String stringToSend = resultCode.mCommand + ": ";
3153        if (resultCode.mArg != null) {
3154            stringToSend += resultCode.mArg;
3155        }
3156        atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice));
3157    }
3158
3159    private String getCurrentDeviceName(BluetoothDevice device) {
3160        String defaultName = "<unknown>";
3161
3162        if(device == null) {
3163            return defaultName;
3164        }
3165
3166        String deviceName = device.getName();
3167        if (deviceName == null) {
3168            return defaultName;
3169        }
3170        return deviceName;
3171    }
3172
3173    private byte[] getByteAddress(BluetoothDevice device) {
3174        return Utils.getBytesFromAddress(device.getAddress());
3175    }
3176
3177    private BluetoothDevice getDevice(byte[] address) {
3178        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
3179    }
3180
3181    private boolean isInCall() {
3182        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
3183                (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
3184    }
3185
3186    boolean isConnected() {
3187        IState currentState = getCurrentState();
3188        return (currentState == mConnected || currentState == mAudioOn);
3189    }
3190
3191    boolean okToConnect(BluetoothDevice device) {
3192        AdapterService adapterService = AdapterService.getAdapterService();
3193        int priority = mService.getPriority(device);
3194        boolean ret = false;
3195        //check if this is an incoming connection in Quiet mode.
3196        if((adapterService == null) ||
3197           ((adapterService.isQuietModeEnabled() == true) &&
3198           (mTargetDevice == null))){
3199            ret = false;
3200        }
3201        // check priority and accept or reject the connection. if priority is undefined
3202        // it is likely that our SDP has not completed and peer is initiating the
3203        // connection. Allow this connection, provided the device is bonded
3204        else if((BluetoothProfile.PRIORITY_OFF < priority) ||
3205                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
3206                (device.getBondState() != BluetoothDevice.BOND_NONE))){
3207            ret= true;
3208        }
3209        return ret;
3210    }
3211
3212    @Override
3213    protected void log(String msg) {
3214        if (DBG) {
3215            super.log(msg);
3216        }
3217    }
3218
3219    public void handleAccessPermissionResult(Intent intent) {
3220        log("handleAccessPermissionResult");
3221        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3222        if (mPhonebook != null) {
3223            if (!mPhonebook.getCheckingAccessPermission()) {
3224                return;
3225            }
3226            int atCommandResult = 0;
3227            int atCommandErrorCode = 0;
3228            //HeadsetBase headset = mHandsfree.getHeadset();
3229            // ASSERT: (headset != null) && headSet.isConnected()
3230            // REASON: mCheckingAccessPermission is true, otherwise resetAtState
3231            // has set mCheckingAccessPermission to false
3232            if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
3233                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
3234                                       BluetoothDevice.CONNECTION_ACCESS_NO)
3235                        == BluetoothDevice.CONNECTION_ACCESS_YES) {
3236                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3237                        mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
3238                    }
3239                    atCommandResult = mPhonebook.processCpbrCommand(device);
3240                } else {
3241                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3242                        mCurrentDevice.setPhonebookAccessPermission(
3243                                BluetoothDevice.ACCESS_REJECTED);
3244                    }
3245                }
3246            }
3247            mPhonebook.setCpbrIndex(-1);
3248            mPhonebook.setCheckingAccessPermission(false);
3249
3250            if (atCommandResult >= 0) {
3251                atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device));
3252            } else {
3253                log("handleAccessPermissionResult - RESULT_NONE");
3254            }
3255        } else {
3256            Log.e(TAG, "Phonebook handle null");
3257            if (device != null) {
3258                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
3259                                     getByteAddress(device));
3260            }
3261        }
3262    }
3263
3264    private static final String SCHEME_TEL = "tel";
3265
3266    // Event types for STACK_EVENT message
3267    final private static int EVENT_TYPE_NONE = 0;
3268    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
3269    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
3270    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
3271    final private static int EVENT_TYPE_ANSWER_CALL = 4;
3272    final private static int EVENT_TYPE_HANGUP_CALL = 5;
3273    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
3274    final private static int EVENT_TYPE_DIAL_CALL = 7;
3275    final private static int EVENT_TYPE_SEND_DTMF = 8;
3276    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
3277    final private static int EVENT_TYPE_AT_CHLD = 10;
3278    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
3279    final private static int EVENT_TYPE_AT_CIND = 12;
3280    final private static int EVENT_TYPE_AT_COPS = 13;
3281    final private static int EVENT_TYPE_AT_CLCC = 14;
3282    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
3283    final private static int EVENT_TYPE_KEY_PRESSED = 16;
3284    final private static int EVENT_TYPE_WBS = 17;
3285
3286    private class StackEvent {
3287        int type = EVENT_TYPE_NONE;
3288        int valueInt = 0;
3289        int valueInt2 = 0;
3290        String valueString = null;
3291        BluetoothDevice device = null;
3292
3293        private StackEvent(int type) {
3294            this.type = type;
3295        }
3296    }
3297
3298    /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode,
3299                                                                          byte[] address);
3300    /*package*/ native boolean atResponseStringNative(String responseString, byte[] address);
3301
3302    private native static void classInitNative();
3303    private native void initializeNative(int max_hf_clients);
3304    private native void cleanupNative();
3305    private native boolean connectHfpNative(byte[] address);
3306    private native boolean disconnectHfpNative(byte[] address);
3307    private native boolean connectAudioNative(byte[] address);
3308    private native boolean disconnectAudioNative(byte[] address);
3309    private native boolean startVoiceRecognitionNative(byte[] address);
3310    private native boolean stopVoiceRecognitionNative(byte[] address);
3311    private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
3312    private native boolean cindResponseNative(int service, int numActive, int numHeld,
3313                                              int callState, int signal, int roam,
3314                                              int batteryCharge, byte[] address);
3315    private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
3316                                                    int batteryCharge);
3317
3318    private native boolean clccResponseNative(int index, int dir, int status, int mode,
3319                                              boolean mpty, String number, int type,
3320                                                                           byte[] address);
3321    private native boolean copsResponseNative(String operatorName, byte[] address);
3322
3323    private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
3324                                                  String number, int type);
3325    private native boolean configureWBSNative(byte[] address,int condec_config);
3326}
3327