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