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