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