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