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