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