HeadsetStateMachine.java revision 10260dc85133bbfbc51c53f06ba8992e00d9b67a
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * Bluetooth Handset StateMachine
19 *                      (Disconnected)
20 *                           |    ^
21 *                   CONNECT |    | DISCONNECTED
22 *                           V    |
23 *                         (Pending)
24 *                           |    ^
25 *                 CONNECTED |    | CONNECT
26 *                           V    |
27 *                        (Connected)
28 *                           |    ^
29 *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
30 *                           V    |
31 *                         (AudioOn)
32 */
33package com.android.bluetooth.hfp;
34
35import android.bluetooth.BluetoothAdapter;
36import android.bluetooth.BluetoothAssignedNumbers;
37import android.bluetooth.BluetoothDevice;
38import android.bluetooth.BluetoothHeadset;
39import android.bluetooth.BluetoothProfile;
40import android.bluetooth.BluetoothUuid;
41import android.bluetooth.IBluetooth;
42import android.bluetooth.IBluetoothHeadsetPhone;
43import android.content.ComponentName;
44import android.content.Context;
45import android.content.Intent;
46import android.content.ServiceConnection;
47import android.content.ActivityNotFoundException;
48import android.media.AudioManager;
49import android.net.Uri;
50import android.os.IBinder;
51import android.os.Message;
52import android.os.ParcelUuid;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.os.PowerManager;
56import android.os.PowerManager.WakeLock;
57import android.telephony.PhoneNumberUtils;
58import android.util.Log;
59import com.android.bluetooth.Utils;
60import com.android.bluetooth.btservice.AdapterService;
61import com.android.internal.util.IState;
62import com.android.internal.util.State;
63import com.android.internal.util.StateMachine;
64import java.util.ArrayList;
65import java.util.HashMap;
66import java.util.List;
67import java.util.Map;
68import java.util.Set;
69
70final class HeadsetStateMachine extends StateMachine {
71    private static final String TAG = "HeadsetStateMachine";
72    private static final boolean DBG = false;
73    //For Debugging only
74    private static int sRefCount=0;
75
76    private static final String HEADSET_NAME = "bt_headset_name";
77    private static final String HEADSET_NREC = "bt_headset_nrec";
78
79    static final int CONNECT = 1;
80    static final int DISCONNECT = 2;
81    static final int CONNECT_AUDIO = 3;
82    static final int DISCONNECT_AUDIO = 4;
83    static final int VOICE_RECOGNITION_START = 5;
84    static final int VOICE_RECOGNITION_STOP = 6;
85
86    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
87    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
88    static final int INTENT_SCO_VOLUME_CHANGED = 7;
89    static final int SET_MIC_VOLUME = 8;
90    static final int CALL_STATE_CHANGED = 9;
91    static final int INTENT_BATTERY_CHANGED = 10;
92    static final int DEVICE_STATE_CHANGED = 11;
93    static final int SEND_CCLC_RESPONSE = 12;
94    static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
95
96    static final int VIRTUAL_CALL_START = 14;
97    static final int VIRTUAL_CALL_STOP = 15;
98
99    private static final int STACK_EVENT = 101;
100    private static final int DIALING_OUT_TIMEOUT = 102;
101    private static final int START_VR_TIMEOUT = 103;
102
103    private static final int CONNECT_TIMEOUT = 201;
104
105    private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
106    private static final int START_VR_TIMEOUT_VALUE = 5000;
107
108    // Keys are AT commands, and values are the company IDs.
109    private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
110
111    private static final ParcelUuid[] HEADSET_UUIDS = {
112        BluetoothUuid.HSP,
113        BluetoothUuid.Handsfree,
114    };
115
116    private Disconnected mDisconnected;
117    private Pending mPending;
118    private Connected mConnected;
119    private AudioOn mAudioOn;
120
121    private HeadsetService mService;
122    private PowerManager mPowerManager;
123    private boolean mVirtualCallStarted = false;
124    private boolean mVoiceRecognitionStarted = false;
125    private boolean mWaitingForVoiceRecognition = false;
126    private WakeLock mStartVoiceRecognitionWakeLock;  // held while waiting for voice recognition
127
128    private boolean mDialingOut = false;
129    private AudioManager mAudioManager;
130    private AtPhonebook mPhonebook;
131
132    private static Intent sVoiceCommandIntent;
133
134    private HeadsetPhoneState mPhoneState;
135    private int mAudioState;
136    private BluetoothAdapter mAdapter;
137    private IBluetoothHeadsetPhone mPhoneProxy;
138    private boolean mNativeAvailable;
139
140    // mCurrentDevice is the device connected before the state changes
141    // mTargetDevice is the device to be connected
142    // mIncomingDevice is the device connecting to us, valid only in Pending state
143    //                when mIncomingDevice is not null, both mCurrentDevice
144    //                  and mTargetDevice are null
145    //                when either mCurrentDevice or mTargetDevice is not null,
146    //                  mIncomingDevice is null
147    // Stable states
148    //   No connection, Disconnected state
149    //                  both mCurrentDevice and mTargetDevice are null
150    //   Connected, Connected state
151    //              mCurrentDevice is not null, mTargetDevice is null
152    // Interim states
153    //   Connecting to a device, Pending
154    //                           mCurrentDevice is null, mTargetDevice is not null
155    //   Disconnecting device, Connecting to new device
156    //     Pending
157    //     Both mCurrentDevice and mTargetDevice are not null
158    //   Disconnecting device Pending
159    //                        mCurrentDevice is not null, mTargetDevice is null
160    //   Incoming connections Pending
161    //                        Both mCurrentDevice and mTargetDevice are null
162    private BluetoothDevice mCurrentDevice = null;
163    private BluetoothDevice mTargetDevice = null;
164    private BluetoothDevice mIncomingDevice = null;
165
166    static {
167        classInitNative();
168
169        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>();
170        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS);
171        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE);
172    }
173
174    private HeadsetStateMachine(HeadsetService context) {
175        super(TAG);
176        mService = context;
177        mVoiceRecognitionStarted = false;
178        mWaitingForVoiceRecognition = false;
179
180        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
181        mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
182                                                       TAG + ":VoiceRecognition");
183        mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
184
185        mDialingOut = false;
186        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
187        mPhonebook = new AtPhonebook(mService, this);
188        mPhoneState = new HeadsetPhoneState(context, this);
189        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
190        mAdapter = BluetoothAdapter.getDefaultAdapter();
191        Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
192        intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
193        if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
194            Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
195        }
196
197        initializeNative();
198        mNativeAvailable=true;
199
200        mDisconnected = new Disconnected();
201        mPending = new Pending();
202        mConnected = new Connected();
203        mAudioOn = new AudioOn();
204
205        if (sVoiceCommandIntent == null) {
206            sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
207            sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
208        }
209
210        addState(mDisconnected);
211        addState(mPending);
212        addState(mConnected);
213        addState(mAudioOn);
214
215        setInitialState(mDisconnected);
216    }
217
218    static HeadsetStateMachine make(HeadsetService context) {
219        Log.d(TAG, "make");
220        HeadsetStateMachine hssm = new HeadsetStateMachine(context);
221        hssm.start();
222        return hssm;
223    }
224
225
226    public void doQuit() {
227        quitNow();
228    }
229
230    public void cleanup() {
231        if (mPhoneProxy != null) {
232            if (DBG) Log.d(TAG,"Unbinding service...");
233            synchronized (mConnection) {
234                try {
235                    mPhoneProxy = null;
236                    mService.unbindService(mConnection);
237                } catch (Exception re) {
238                    Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re);
239                }
240            }
241        }
242        if (mPhoneState != null) {
243            mPhoneState.listenForPhoneState(false);
244            mPhoneState.cleanup();
245        }
246        if (mPhonebook != null) {
247            mPhonebook.cleanup();
248        }
249        if (mNativeAvailable) {
250            cleanupNative();
251            mNativeAvailable = false;
252        }
253    }
254
255    private class Disconnected extends State {
256        @Override
257        public void enter() {
258            log("Enter Disconnected: " + getCurrentMessage().what);
259            mPhonebook.resetAtState();
260            mPhoneState.listenForPhoneState(false);
261        }
262
263        @Override
264        public boolean processMessage(Message message) {
265            log("Disconnected process message: " + message.what);
266            if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
267                Log.e(TAG, "ERROR: current, target, or mIncomingDevice not null in Disconnected");
268                return NOT_HANDLED;
269            }
270
271            boolean retValue = HANDLED;
272            switch(message.what) {
273                case CONNECT:
274                    BluetoothDevice device = (BluetoothDevice) message.obj;
275                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
276                                   BluetoothProfile.STATE_DISCONNECTED);
277
278                    if (!connectHfpNative(getByteAddress(device)) ) {
279                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
280                                       BluetoothProfile.STATE_CONNECTING);
281                        break;
282                    }
283
284                    synchronized (HeadsetStateMachine.this) {
285                        mTargetDevice = device;
286                        transitionTo(mPending);
287                    }
288                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
289                    //          sends back events consistently
290                    sendMessageDelayed(CONNECT_TIMEOUT, 30000);
291                    break;
292                case DISCONNECT:
293                    // ignore
294                    break;
295                case INTENT_BATTERY_CHANGED:
296                    processIntentBatteryChanged((Intent) message.obj);
297                    break;
298                case CALL_STATE_CHANGED:
299                    processCallState((HeadsetCallState) message.obj,
300                        ((message.arg1 == 1)?true:false));
301                    break;
302                case STACK_EVENT:
303                    StackEvent event = (StackEvent) message.obj;
304                    if (DBG) {
305                        log("event type: " + event.type);
306                    }
307                    switch (event.type) {
308                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
309                            processConnectionEvent(event.valueInt, event.device);
310                            break;
311                        default:
312                            Log.e(TAG, "Unexpected stack event: " + event.type);
313                            break;
314                    }
315                    break;
316                default:
317                    return NOT_HANDLED;
318            }
319            return retValue;
320        }
321
322        @Override
323        public void exit() {
324            log("Exit Disconnected: " + getCurrentMessage().what);
325        }
326
327        // in Disconnected state
328        private void processConnectionEvent(int state, BluetoothDevice device) {
329            switch (state) {
330            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
331                Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
332                break;
333            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
334                if (okToConnect(device)){
335                    Log.i(TAG,"Incoming Hf accepted");
336                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
337                                             BluetoothProfile.STATE_DISCONNECTED);
338                    synchronized (HeadsetStateMachine.this) {
339                        mIncomingDevice = device;
340                        transitionTo(mPending);
341                    }
342                } else {
343                    Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device)+
344                              " bondState=" + device.getBondState());
345                    //reject the connection and stay in Disconnected state itself
346                    disconnectHfpNative(getByteAddress(device));
347                    // the other profile connection should be initiated
348                    AdapterService adapterService = AdapterService.getAdapterService();
349                    if ( adapterService != null) {
350                        adapterService.connectOtherProfile(device,
351                                                           AdapterService.PROFILE_CONN_REJECTED);
352                    }
353                }
354                break;
355            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
356                Log.w(TAG, "HFP Connected from Disconnected state");
357                if (okToConnect(device)){
358                    Log.i(TAG,"Incoming Hf accepted");
359                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
360                                             BluetoothProfile.STATE_DISCONNECTED);
361                    synchronized (HeadsetStateMachine.this) {
362                        mCurrentDevice = device;
363                        transitionTo(mConnected);
364                    }
365                    configAudioParameters();
366                } else {
367                    //reject the connection and stay in Disconnected state itself
368                    Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device) +
369                              " bondState=" + device.getBondState());
370                    disconnectHfpNative(getByteAddress(device));
371                    // the other profile connection should be initiated
372                    AdapterService adapterService = AdapterService.getAdapterService();
373                    if ( adapterService != null) {
374                        adapterService.connectOtherProfile(device,
375                                                           AdapterService.PROFILE_CONN_REJECTED);
376                    }
377                }
378                break;
379            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
380                Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
381                break;
382            default:
383                Log.e(TAG, "Incorrect state: " + state);
384                break;
385            }
386        }
387    }
388
389    private class Pending extends State {
390        @Override
391        public void enter() {
392            log("Enter Pending: " + getCurrentMessage().what);
393        }
394
395        @Override
396        public boolean processMessage(Message message) {
397            log("Pending process message: " + message.what);
398
399            boolean retValue = HANDLED;
400            switch(message.what) {
401                case CONNECT:
402                case CONNECT_AUDIO:
403                    deferMessage(message);
404                    break;
405                case CONNECT_TIMEOUT:
406                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
407                                             getByteAddress(mTargetDevice));
408                    break;
409                case DISCONNECT:
410                    BluetoothDevice device = (BluetoothDevice) message.obj;
411                    if (mCurrentDevice != null && mTargetDevice != null &&
412                        mTargetDevice.equals(device) ) {
413                        // cancel connection to the mTargetDevice
414                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
415                                       BluetoothProfile.STATE_CONNECTING);
416                        synchronized (HeadsetStateMachine.this) {
417                            mTargetDevice = null;
418                        }
419                    } else {
420                        deferMessage(message);
421                    }
422                    break;
423                case INTENT_BATTERY_CHANGED:
424                    processIntentBatteryChanged((Intent) message.obj);
425                    break;
426                case CALL_STATE_CHANGED:
427                    processCallState((HeadsetCallState) message.obj,
428                        ((message.arg1 == 1)?true:false));
429                    break;
430                case STACK_EVENT:
431                    StackEvent event = (StackEvent) message.obj;
432                    if (DBG) {
433                        log("event type: " + event.type);
434                    }
435                    switch (event.type) {
436                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
437                            removeMessages(CONNECT_TIMEOUT);
438                            processConnectionEvent(event.valueInt, event.device);
439                            break;
440                        default:
441                            Log.e(TAG, "Unexpected event: " + event.type);
442                            break;
443                    }
444                    break;
445                default:
446                    return NOT_HANDLED;
447            }
448            return retValue;
449        }
450
451        // in Pending state
452        private void processConnectionEvent(int state, BluetoothDevice device) {
453            switch (state) {
454                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
455                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
456                        broadcastConnectionState(mCurrentDevice,
457                                                 BluetoothProfile.STATE_DISCONNECTED,
458                                                 BluetoothProfile.STATE_DISCONNECTING);
459                        synchronized (HeadsetStateMachine.this) {
460                            mCurrentDevice = null;
461                        }
462
463                        if (mTargetDevice != null) {
464                            if (!connectHfpNative(getByteAddress(mTargetDevice))) {
465                                broadcastConnectionState(mTargetDevice,
466                                                         BluetoothProfile.STATE_DISCONNECTED,
467                                                         BluetoothProfile.STATE_CONNECTING);
468                                synchronized (HeadsetStateMachine.this) {
469                                    mTargetDevice = null;
470                                    transitionTo(mDisconnected);
471                                }
472                            }
473                        } else {
474                            synchronized (HeadsetStateMachine.this) {
475                                mIncomingDevice = null;
476                                transitionTo(mDisconnected);
477                            }
478                        }
479                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
480                        // outgoing connection failed
481                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
482                                                 BluetoothProfile.STATE_CONNECTING);
483                        synchronized (HeadsetStateMachine.this) {
484                            mTargetDevice = null;
485                            transitionTo(mDisconnected);
486                        }
487                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
488                        broadcastConnectionState(mIncomingDevice,
489                                                 BluetoothProfile.STATE_DISCONNECTED,
490                                                 BluetoothProfile.STATE_CONNECTING);
491                        synchronized (HeadsetStateMachine.this) {
492                            mIncomingDevice = null;
493                            transitionTo(mDisconnected);
494                        }
495                    } else {
496                        Log.e(TAG, "Unknown device Disconnected: " + device);
497                    }
498                    break;
499            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
500                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
501                    // disconnection failed
502                    broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
503                                             BluetoothProfile.STATE_DISCONNECTING);
504                    if (mTargetDevice != null) {
505                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
506                                                 BluetoothProfile.STATE_CONNECTING);
507                    }
508                    synchronized (HeadsetStateMachine.this) {
509                        mTargetDevice = null;
510                        transitionTo(mConnected);
511                    }
512                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
513                    broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
514                                             BluetoothProfile.STATE_CONNECTING);
515                    synchronized (HeadsetStateMachine.this) {
516                        mCurrentDevice = mTargetDevice;
517                        mTargetDevice = null;
518                        transitionTo(mConnected);
519                    }
520                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
521                    broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
522                                             BluetoothProfile.STATE_CONNECTING);
523                    synchronized (HeadsetStateMachine.this) {
524                        mCurrentDevice = mIncomingDevice;
525                        mIncomingDevice = null;
526                        transitionTo(mConnected);
527                    }
528                } else {
529                    Log.e(TAG, "Unknown device Connected: " + device);
530                    // something is wrong here, but sync our state with stack
531                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
532                                             BluetoothProfile.STATE_DISCONNECTED);
533                    synchronized (HeadsetStateMachine.this) {
534                        mCurrentDevice = device;
535                        mTargetDevice = null;
536                        mIncomingDevice = null;
537                        transitionTo(mConnected);
538                    }
539                }
540                configAudioParameters();
541                break;
542            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
543                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
544                    log("current device tries to connect back");
545                    // TODO(BT) ignore or reject
546                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
547                    // The stack is connecting to target device or
548                    // there is an incoming connection from the target device at the same time
549                    // we already broadcasted the intent, doing nothing here
550                    if (DBG) {
551                        log("Stack and target device are connecting");
552                    }
553                }
554                else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
555                    Log.e(TAG, "Another connecting event on the incoming device");
556                } else {
557                    // We get an incoming connecting request while Pending
558                    // TODO(BT) is stack handing this case? let's ignore it for now
559                    log("Incoming connection while pending, ignore");
560                }
561                break;
562            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
563                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
564                    // we already broadcasted the intent, doing nothing here
565                    if (DBG) {
566                        log("stack is disconnecting mCurrentDevice");
567                    }
568                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
569                    Log.e(TAG, "TargetDevice is getting disconnected");
570                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
571                    Log.e(TAG, "IncomingDevice is getting disconnected");
572                } else {
573                    Log.e(TAG, "Disconnecting unknow device: " + device);
574                }
575                break;
576            default:
577                Log.e(TAG, "Incorrect state: " + state);
578                break;
579            }
580        }
581
582    }
583
584    private class Connected extends State {
585        @Override
586        public void enter() {
587            log("Enter Connected: " + getCurrentMessage().what);
588        }
589
590        @Override
591        public boolean processMessage(Message message) {
592            log("Connected process message: " + message.what);
593            if (DBG) {
594                if (mCurrentDevice == null) {
595                    log("ERROR: mCurrentDevice is null in Connected");
596                    return NOT_HANDLED;
597                }
598            }
599
600            boolean retValue = HANDLED;
601            switch(message.what) {
602                case CONNECT:
603                {
604                    BluetoothDevice device = (BluetoothDevice) message.obj;
605                    if (mCurrentDevice.equals(device)) {
606                        break;
607                    }
608
609                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
610                                   BluetoothProfile.STATE_DISCONNECTED);
611                    if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) {
612                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
613                                       BluetoothProfile.STATE_CONNECTING);
614                        break;
615                    }
616
617                    synchronized (HeadsetStateMachine.this) {
618                        mTargetDevice = device;
619                        transitionTo(mPending);
620                    }
621                }
622                    break;
623                case DISCONNECT:
624                {
625                    BluetoothDevice device = (BluetoothDevice) message.obj;
626                    if (!mCurrentDevice.equals(device)) {
627                        break;
628                    }
629                    broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
630                                   BluetoothProfile.STATE_CONNECTED);
631                    if (!disconnectHfpNative(getByteAddress(device))) {
632                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
633                                       BluetoothProfile.STATE_DISCONNECTED);
634                        break;
635                    }
636                    transitionTo(mPending);
637                }
638                    break;
639                case CONNECT_AUDIO:
640                    // TODO(BT) when failure, broadcast audio connecting to disconnected intent
641                    //          check if device matches mCurrentDevice
642                    connectAudioNative(getByteAddress(mCurrentDevice));
643                    break;
644                case VOICE_RECOGNITION_START:
645                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
646                    break;
647                case VOICE_RECOGNITION_STOP:
648                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
649                    break;
650                case CALL_STATE_CHANGED:
651                    processCallState((HeadsetCallState) message.obj, ((message.arg1==1)?true:false));
652                    break;
653                case INTENT_BATTERY_CHANGED:
654                    processIntentBatteryChanged((Intent) message.obj);
655                    break;
656                case DEVICE_STATE_CHANGED:
657                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
658                    break;
659                case SEND_CCLC_RESPONSE:
660                    processSendClccResponse((HeadsetClccResponse) message.obj);
661                    break;
662                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
663                    processSendVendorSpecificResultCode(
664                            (HeadsetVendorSpecificResultCode) message.obj);
665                    break;
666                case DIALING_OUT_TIMEOUT:
667                    if (mDialingOut) {
668                        mDialingOut= false;
669                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
670                    }
671                    break;
672                case VIRTUAL_CALL_START:
673                    initiateScoUsingVirtualVoiceCall();
674                    break;
675                case VIRTUAL_CALL_STOP:
676                    terminateScoUsingVirtualVoiceCall();
677                    break;
678                case START_VR_TIMEOUT:
679                    if (mWaitingForVoiceRecognition) {
680                        mWaitingForVoiceRecognition = false;
681                        Log.e(TAG, "Timeout waiting for voice recognition to start");
682                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
683                    }
684                    break;
685                case STACK_EVENT:
686                    StackEvent event = (StackEvent) message.obj;
687                    if (DBG) {
688                        log("event type: " + event.type);
689                    }
690                    switch (event.type) {
691                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
692                            processConnectionEvent(event.valueInt, event.device);
693                            break;
694                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
695                            processAudioEvent(event.valueInt, event.device);
696                            break;
697                        case EVENT_TYPE_VR_STATE_CHANGED:
698                            processVrEvent(event.valueInt);
699                            break;
700                        case EVENT_TYPE_ANSWER_CALL:
701                            // TODO(BT) could answer call happen on Connected state?
702                            processAnswerCall();
703                            break;
704                        case EVENT_TYPE_HANGUP_CALL:
705                            // TODO(BT) could hangup call happen on Connected state?
706                            processHangupCall();
707                            break;
708                        case EVENT_TYPE_VOLUME_CHANGED:
709                            processVolumeEvent(event.valueInt, event.valueInt2);
710                            break;
711                        case EVENT_TYPE_DIAL_CALL:
712                            processDialCall(event.valueString);
713                            break;
714                        case EVENT_TYPE_SEND_DTMF:
715                            processSendDtmf(event.valueInt);
716                            break;
717                        case EVENT_TYPE_NOICE_REDUCTION:
718                            processNoiceReductionEvent(event.valueInt);
719                            break;
720                        case EVENT_TYPE_AT_CHLD:
721                            processAtChld(event.valueInt);
722                            break;
723                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
724                            processSubscriberNumberRequest();
725                            break;
726                        case EVENT_TYPE_AT_CIND:
727                            processAtCind();
728                            break;
729                        case EVENT_TYPE_AT_COPS:
730                            processAtCops();
731                            break;
732                        case EVENT_TYPE_AT_CLCC:
733                            processAtClcc();
734                            break;
735                        case EVENT_TYPE_UNKNOWN_AT:
736                            processUnknownAt(event.valueString);
737                            break;
738                        case EVENT_TYPE_KEY_PRESSED:
739                            processKeyPressed();
740                            break;
741                        default:
742                            Log.e(TAG, "Unknown stack event: " + event.type);
743                            break;
744                    }
745                    break;
746                default:
747                    return NOT_HANDLED;
748            }
749            return retValue;
750        }
751
752        // in Connected state
753        private void processConnectionEvent(int state, BluetoothDevice device) {
754            switch (state) {
755                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
756                    if (mCurrentDevice.equals(device)) {
757                        broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
758                                                 BluetoothProfile.STATE_CONNECTED);
759                        synchronized (HeadsetStateMachine.this) {
760                            mCurrentDevice = null;
761                            transitionTo(mDisconnected);
762                        }
763                    } else {
764                        Log.e(TAG, "Disconnected from unknown device: " + device);
765                    }
766                    break;
767                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
768                    processSlcConnected();
769                    break;
770              default:
771                  Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
772                  break;
773            }
774        }
775
776        // in Connected state
777        private void processAudioEvent(int state, BluetoothDevice device) {
778            if (!mCurrentDevice.equals(device)) {
779                Log.e(TAG, "Audio changed on disconnected device: " + device);
780                return;
781            }
782
783            switch (state) {
784                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
785                    // TODO(BT) should I save the state for next broadcast as the prevState?
786                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
787                    mAudioManager.setBluetoothScoOn(true);
788                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
789                                        BluetoothHeadset.STATE_AUDIO_CONNECTING);
790                    transitionTo(mAudioOn);
791                    break;
792                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
793                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
794                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
795                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
796                    break;
797                    // TODO(BT) process other states
798                default:
799                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
800                    break;
801            }
802        }
803
804        private void processSlcConnected() {
805            if (mPhoneProxy != null) {
806                try {
807                    // start phone state listener here, instead of on disconnected exit()
808                    // On BT off, exitting SM sends a SM exit() call which incorrectly forces
809                    // a listenForPhoneState(true).
810                    // Additionally, no indicator updates should be sent prior to SLC setup
811                    mPhoneState.listenForPhoneState(true);
812                    mPhoneProxy.queryPhoneState();
813                } catch (RemoteException e) {
814                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
815                }
816            } else {
817                Log.e(TAG, "Handsfree phone proxy null for query phone state");
818            }
819
820        }
821    }
822
823    private class AudioOn extends State {
824
825        @Override
826        public void enter() {
827            log("Enter AudioOn: " + getCurrentMessage().what);
828        }
829
830        @Override
831        public boolean processMessage(Message message) {
832            log("AudioOn process message: " + message.what);
833            if (DBG) {
834                if (mCurrentDevice == null) {
835                    log("ERROR: mCurrentDevice is null in AudioOn");
836                    return NOT_HANDLED;
837                }
838            }
839
840            boolean retValue = HANDLED;
841            switch(message.what) {
842                case DISCONNECT:
843                {
844                    BluetoothDevice device = (BluetoothDevice) message.obj;
845                    if (!mCurrentDevice.equals(device)) {
846                        break;
847                    }
848                    deferMessage(obtainMessage(DISCONNECT, message.obj));
849                }
850                // fall through
851                case DISCONNECT_AUDIO:
852                    if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
853                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
854                        mAudioManager.setBluetoothScoOn(false);
855                        broadcastAudioState(mCurrentDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
856                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
857                    }
858                    break;
859                case VOICE_RECOGNITION_START:
860                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
861                    break;
862                case VOICE_RECOGNITION_STOP:
863                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
864                    break;
865                case INTENT_SCO_VOLUME_CHANGED:
866                    processIntentScoVolume((Intent) message.obj);
867                    break;
868                case CALL_STATE_CHANGED:
869                    processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false));
870                    break;
871                case INTENT_BATTERY_CHANGED:
872                    processIntentBatteryChanged((Intent) message.obj);
873                    break;
874                case DEVICE_STATE_CHANGED:
875                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
876                    break;
877                case SEND_CCLC_RESPONSE:
878                    processSendClccResponse((HeadsetClccResponse) message.obj);
879                    break;
880                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
881                    processSendVendorSpecificResultCode(
882                            (HeadsetVendorSpecificResultCode) message.obj);
883                    break;
884
885                case VIRTUAL_CALL_START:
886                    initiateScoUsingVirtualVoiceCall();
887                    break;
888                case VIRTUAL_CALL_STOP:
889                    terminateScoUsingVirtualVoiceCall();
890                    break;
891
892                case DIALING_OUT_TIMEOUT:
893                    if (mDialingOut) {
894                        mDialingOut= false;
895                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
896                    }
897                    break;
898                case START_VR_TIMEOUT:
899                    if (mWaitingForVoiceRecognition) {
900                        mWaitingForVoiceRecognition = false;
901                        Log.e(TAG, "Timeout waiting for voice recognition to start");
902                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
903                    }
904                    break;
905                case STACK_EVENT:
906                    StackEvent event = (StackEvent) message.obj;
907                    if (DBG) {
908                        log("event type: " + event.type);
909                    }
910                    switch (event.type) {
911                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
912                            processConnectionEvent(event.valueInt, event.device);
913                            break;
914                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
915                            processAudioEvent(event.valueInt, event.device);
916                            break;
917                        case EVENT_TYPE_VR_STATE_CHANGED:
918                            processVrEvent(event.valueInt);
919                            break;
920                        case EVENT_TYPE_ANSWER_CALL:
921                            processAnswerCall();
922                            break;
923                        case EVENT_TYPE_HANGUP_CALL:
924                            processHangupCall();
925                            break;
926                        case EVENT_TYPE_VOLUME_CHANGED:
927                            processVolumeEvent(event.valueInt, event.valueInt2);
928                            break;
929                        case EVENT_TYPE_DIAL_CALL:
930                            processDialCall(event.valueString);
931                            break;
932                        case EVENT_TYPE_SEND_DTMF:
933                            processSendDtmf(event.valueInt);
934                            break;
935                        case EVENT_TYPE_NOICE_REDUCTION:
936                            processNoiceReductionEvent(event.valueInt);
937                            break;
938                        case EVENT_TYPE_AT_CHLD:
939                            processAtChld(event.valueInt);
940                            break;
941                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
942                            processSubscriberNumberRequest();
943                            break;
944                        case EVENT_TYPE_AT_CIND:
945                            processAtCind();
946                            break;
947                        case EVENT_TYPE_AT_COPS:
948                            processAtCops();
949                            break;
950                        case EVENT_TYPE_AT_CLCC:
951                            processAtClcc();
952                            break;
953                        case EVENT_TYPE_UNKNOWN_AT:
954                            processUnknownAt(event.valueString);
955                            break;
956                        case EVENT_TYPE_KEY_PRESSED:
957                            processKeyPressed();
958                            break;
959                        default:
960                            Log.e(TAG, "Unknown stack event: " + event.type);
961                            break;
962                    }
963                    break;
964                default:
965                    return NOT_HANDLED;
966            }
967            return retValue;
968        }
969
970        // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
971        private void processConnectionEvent(int state, BluetoothDevice device) {
972            switch (state) {
973                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
974                    if (mCurrentDevice.equals(device)) {
975                        processAudioEvent (HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
976                        broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
977                                                 BluetoothProfile.STATE_CONNECTED);
978                        synchronized (HeadsetStateMachine.this) {
979                            mCurrentDevice = null;
980                            transitionTo(mDisconnected);
981                        }
982                    } else {
983                        Log.e(TAG, "Disconnected from unknown device: " + device);
984                    }
985                    break;
986              default:
987                  Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
988                  break;
989            }
990        }
991
992        // in AudioOn state
993        private void processAudioEvent(int state, BluetoothDevice device) {
994            if (!mCurrentDevice.equals(device)) {
995                Log.e(TAG, "Audio changed on disconnected device: " + device);
996                return;
997            }
998
999            switch (state) {
1000                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1001                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1002                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1003                        mAudioManager.setBluetoothScoOn(false);
1004                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1005                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
1006                    }
1007                    transitionTo(mConnected);
1008                    break;
1009                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1010                    // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
1011                    //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
1012                    //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
1013                    break;
1014                default:
1015                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1016                    break;
1017            }
1018        }
1019
1020        private void processIntentScoVolume(Intent intent) {
1021            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1022            if (mPhoneState.getSpeakerVolume() != volumeValue) {
1023                mPhoneState.setSpeakerVolume(volumeValue);
1024                setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue);
1025            }
1026        }
1027    }
1028
1029    private ServiceConnection mConnection = new ServiceConnection() {
1030        public void onServiceConnected(ComponentName className, IBinder service) {
1031            if (DBG) Log.d(TAG, "Proxy object connected");
1032            mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
1033        }
1034
1035        public void onServiceDisconnected(ComponentName className) {
1036            if (DBG) Log.d(TAG, "Proxy object disconnected");
1037            mPhoneProxy = null;
1038        }
1039    };
1040
1041    // HFP Connection state of the device could be changed by the state machine
1042    // in separate thread while this method is executing.
1043    int getConnectionState(BluetoothDevice device) {
1044        if (getCurrentState() == mDisconnected) {
1045            return BluetoothProfile.STATE_DISCONNECTED;
1046        }
1047
1048        synchronized (this) {
1049            IState currentState = getCurrentState();
1050            if (currentState == mPending) {
1051                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
1052                    return BluetoothProfile.STATE_CONNECTING;
1053                }
1054                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
1055                    return BluetoothProfile.STATE_DISCONNECTING;
1056                }
1057                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
1058                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
1059                }
1060                return BluetoothProfile.STATE_DISCONNECTED;
1061            }
1062
1063            if (currentState == mConnected || currentState == mAudioOn) {
1064                if (mCurrentDevice.equals(device)) {
1065                    return BluetoothProfile.STATE_CONNECTED;
1066                }
1067                return BluetoothProfile.STATE_DISCONNECTED;
1068            } else {
1069                Log.e(TAG, "Bad currentState: " + currentState);
1070                return BluetoothProfile.STATE_DISCONNECTED;
1071            }
1072        }
1073    }
1074
1075    List<BluetoothDevice> getConnectedDevices() {
1076        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
1077        synchronized(this) {
1078            if (isConnected()) {
1079                devices.add(mCurrentDevice);
1080            }
1081        }
1082        return devices;
1083    }
1084
1085    boolean isAudioOn() {
1086        return (getCurrentState() == mAudioOn);
1087    }
1088
1089    boolean isAudioConnected(BluetoothDevice device) {
1090        synchronized(this) {
1091
1092            /*  Additional check for audio state included for the case when PhoneApp queries
1093            Bluetooth Audio state, before we receive the close event from the stack for the
1094            sco disconnect issued in AudioOn state. This was causing a mismatch in the
1095            Incall screen UI. */
1096
1097            if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
1098                && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
1099            {
1100                return true;
1101            }
1102        }
1103        return false;
1104    }
1105
1106    int getAudioState(BluetoothDevice device) {
1107        synchronized(this) {
1108            if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
1109                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1110            }
1111        }
1112        return mAudioState;
1113    }
1114
1115    private void processVrEvent(int state) {
1116        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " +
1117            mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition +
1118            " isInCall: " + isInCall());
1119        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
1120            if (!isVirtualCallInProgress() &&
1121                !isInCall())
1122            {
1123                try {
1124                    mService.startActivity(sVoiceCommandIntent);
1125                } catch (ActivityNotFoundException e) {
1126                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1127                    return;
1128                }
1129                expectVoiceRecognition();
1130            }
1131        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
1132            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
1133            {
1134                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1135                mVoiceRecognitionStarted = false;
1136                mWaitingForVoiceRecognition = false;
1137                if (!isInCall()) {
1138                    disconnectAudioNative(getByteAddress(mCurrentDevice));
1139                    mAudioManager.setParameters("A2dpSuspended=false");
1140                }
1141            }
1142            else
1143            {
1144                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1145            }
1146        } else {
1147            Log.e(TAG, "Bad Voice Recognition state: " + state);
1148        }
1149    }
1150
1151    private void processLocalVrEvent(int state)
1152    {
1153        if (state == HeadsetHalConstants.VR_STATE_STARTED)
1154        {
1155            boolean needAudio = true;
1156            if (mVoiceRecognitionStarted || isInCall())
1157            {
1158                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() +
1159                    " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
1160                return;
1161            }
1162            mVoiceRecognitionStarted = true;
1163
1164            if (mWaitingForVoiceRecognition)
1165            {
1166                Log.d(TAG, "Voice recognition started successfully");
1167                mWaitingForVoiceRecognition = false;
1168                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1169                removeMessages(START_VR_TIMEOUT);
1170            }
1171            else
1172            {
1173                Log.d(TAG, "Voice recognition started locally");
1174                needAudio = startVoiceRecognitionNative();
1175            }
1176
1177            if (needAudio && !isAudioOn())
1178            {
1179                Log.d(TAG, "Initiating audio connection for Voice Recognition");
1180                // At this stage, we need to be sure that AVDTP is not streaming. This is needed
1181                // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
1182                // streaming state while a SCO connection is established.
1183                // This is needed for VoiceDial scenario alone and not for
1184                // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
1185                // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
1186                // Whereas for VoiceDial we want to activate the SCO connection but we are still
1187                // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
1188                mAudioManager.setParameters("A2dpSuspended=true");
1189                connectAudioNative(getByteAddress(mCurrentDevice));
1190            }
1191
1192            if (mStartVoiceRecognitionWakeLock.isHeld()) {
1193                mStartVoiceRecognitionWakeLock.release();
1194            }
1195        }
1196        else
1197        {
1198            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted +
1199                " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
1200            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
1201            {
1202                mVoiceRecognitionStarted = false;
1203                mWaitingForVoiceRecognition = false;
1204
1205                if (stopVoiceRecognitionNative() && !isInCall()) {
1206                    disconnectAudioNative(getByteAddress(mCurrentDevice));
1207                    mAudioManager.setParameters("A2dpSuspended=false");
1208                }
1209            }
1210        }
1211    }
1212
1213    private synchronized void expectVoiceRecognition() {
1214        mWaitingForVoiceRecognition = true;
1215        sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE);
1216        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
1217            mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
1218        }
1219    }
1220
1221    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1222        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
1223        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
1224        int connectionState;
1225        synchronized (this) {
1226            for (BluetoothDevice device : bondedDevices) {
1227                ParcelUuid[] featureUuids = device.getUuids();
1228                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
1229                    continue;
1230                }
1231                connectionState = getConnectionState(device);
1232                for(int i = 0; i < states.length; i++) {
1233                    if (connectionState == states[i]) {
1234                        deviceList.add(device);
1235                    }
1236                }
1237            }
1238        }
1239        return deviceList;
1240    }
1241
1242    // This method does not check for error conditon (newState == prevState)
1243    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
1244        log("Connection state " + device + ": " + prevState + "->" + newState);
1245        if(prevState == BluetoothProfile.STATE_CONNECTED) {
1246            // Headset is disconnecting, stop Virtual call if active.
1247            terminateScoUsingVirtualVoiceCall();
1248        }
1249
1250        /* Notifying the connection state change of the profile before sending the intent for
1251           connection state change, as it was causing a race condition, with the UI not being
1252           updated with the correct connection state. */
1253        mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,
1254                                                     newState, prevState);
1255        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
1256        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1257        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1258        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1259        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
1260    }
1261
1262    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
1263        if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1264            // When SCO gets disconnected during call transfer, Virtual call
1265            //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
1266            terminateScoUsingVirtualVoiceCall();
1267        }
1268        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
1269        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1270        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1271        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1272        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
1273        log("Audio state " + device + ": " + prevState + "->" + newState);
1274    }
1275
1276    /*
1277     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
1278     */
1279    private void broadcastVendorSpecificEventIntent(String command,
1280                                                    int companyId,
1281                                                    int commandType,
1282                                                    Object[] arguments,
1283                                                    BluetoothDevice device) {
1284        log("broadcastVendorSpecificEventIntent(" + command + ")");
1285        Intent intent =
1286                new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
1287        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
1288        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
1289                        commandType);
1290        // assert: all elements of args are Serializable
1291        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
1292        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1293
1294        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
1295            + "." + Integer.toString(companyId));
1296
1297        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
1298    }
1299
1300    private void configAudioParameters()
1301    {
1302        // Reset NREC on connect event. Headset will override later
1303        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" +
1304                                    HEADSET_NREC + "=on");
1305    }
1306
1307    private String parseUnknownAt(String atString)
1308    {
1309        StringBuilder atCommand = new StringBuilder(atString.length());
1310        String result = null;
1311
1312        for (int i = 0; i < atString.length(); i++) {
1313            char c = atString.charAt(i);
1314            if (c == '"') {
1315                int j = atString.indexOf('"', i + 1 );  // search for closing "
1316                if (j == -1) {  // unmatched ", insert one.
1317                    atCommand.append(atString.substring(i, atString.length()));
1318                    atCommand.append('"');
1319                    break;
1320                }
1321                atCommand.append(atString.substring(i, j + 1));
1322                i = j;
1323            } else if (c != ' ') {
1324                atCommand.append(Character.toUpperCase(c));
1325            }
1326        }
1327        result = atCommand.toString();
1328        return result;
1329    }
1330
1331    private int getAtCommandType(String atCommand)
1332    {
1333        int commandType = mPhonebook.TYPE_UNKNOWN;
1334        String atString = null;
1335        atCommand = atCommand.trim();
1336        if (atCommand.length() > 5)
1337        {
1338            atString = atCommand.substring(5);
1339            if (atString.startsWith("?"))     // Read
1340                commandType = mPhonebook.TYPE_READ;
1341            else if (atString.startsWith("=?"))   // Test
1342                commandType = mPhonebook.TYPE_TEST;
1343            else if (atString.startsWith("="))   // Set
1344                commandType = mPhonebook.TYPE_SET;
1345            else
1346                commandType = mPhonebook.TYPE_UNKNOWN;
1347        }
1348        return commandType;
1349    }
1350
1351    /* Method to check if Virtual Call in Progress */
1352    private boolean isVirtualCallInProgress() {
1353        return mVirtualCallStarted;
1354    }
1355
1356    void setVirtualCallInProgress(boolean state) {
1357        mVirtualCallStarted = state;
1358    }
1359
1360    /* NOTE: Currently the VirtualCall API does not support handling of
1361    call transfers. If it is initiated from the handsfree device,
1362    HeadsetStateMachine will end the virtual call by calling
1363    terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
1364    synchronized boolean initiateScoUsingVirtualVoiceCall() {
1365        if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
1366        // 1. Check if the SCO state is idle
1367        if (isInCall() || mVoiceRecognitionStarted) {
1368            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
1369            return false;
1370        }
1371
1372        // 2. Send virtual phone state changed to initialize SCO
1373        processCallState(new HeadsetCallState(0, 0,
1374            HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
1375        processCallState(new HeadsetCallState(0, 0,
1376            HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
1377        processCallState(new HeadsetCallState(1, 0,
1378            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
1379        setVirtualCallInProgress(true);
1380        // Done
1381        if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
1382        return true;
1383    }
1384
1385    synchronized boolean terminateScoUsingVirtualVoiceCall() {
1386        if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
1387
1388        if (!isVirtualCallInProgress()) {
1389            Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+
1390                "No present call to terminate");
1391            return false;
1392        }
1393
1394        // 2. Send virtual phone state changed to close SCO
1395        processCallState(new HeadsetCallState(0, 0,
1396            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
1397        setVirtualCallInProgress(false);
1398        // Done
1399        if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
1400        return true;
1401    }
1402
1403    private void processAnswerCall() {
1404        if (mPhoneProxy != null) {
1405            try {
1406                mPhoneProxy.answerCall();
1407            } catch (RemoteException e) {
1408                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1409            }
1410        } else {
1411            Log.e(TAG, "Handsfree phone proxy null for answering call");
1412        }
1413    }
1414
1415    private void processHangupCall() {
1416        // Close the virtual call if active. Virtual call should be
1417        // terminated for CHUP callback event
1418        if (isVirtualCallInProgress()) {
1419            terminateScoUsingVirtualVoiceCall();
1420        } else {
1421            if (mPhoneProxy != null) {
1422                try {
1423                    mPhoneProxy.hangupCall();
1424                } catch (RemoteException e) {
1425                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1426                }
1427            } else {
1428                Log.e(TAG, "Handsfree phone proxy null for hanging up call");
1429            }
1430        }
1431    }
1432
1433    private void processDialCall(String number) {
1434        String dialNumber;
1435        if ((number == null) || (number.length() == 0)) {
1436            dialNumber = mPhonebook.getLastDialledNumber();
1437            if (dialNumber == null) {
1438                if (DBG) log("processDialCall, last dial number null");
1439                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1440                return;
1441            }
1442        } else if (number.charAt(0) == '>') {
1443            // Yuck - memory dialling requested.
1444            // Just dial last number for now
1445            if (number.startsWith(">9999")) {   // for PTS test
1446                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1447                return;
1448            }
1449            if (DBG) log("processDialCall, memory dial do last dial for now");
1450            dialNumber = mPhonebook.getLastDialledNumber();
1451            if (dialNumber == null) {
1452                if (DBG) log("processDialCall, last dial number null");
1453                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1454                return;
1455            }
1456        } else {
1457            // Remove trailing ';'
1458            if (number.charAt(number.length() - 1) == ';') {
1459                number = number.substring(0, number.length() - 1);
1460            }
1461
1462            dialNumber = PhoneNumberUtils.convertPreDial(number);
1463        }
1464        // Check for virtual call to terminate before sending Call Intent
1465        terminateScoUsingVirtualVoiceCall();
1466
1467        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1468                                   Uri.fromParts(SCHEME_TEL, dialNumber, null));
1469        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1470        mService.startActivity(intent);
1471        // TODO(BT) continue send OK reults code after call starts
1472        //          hold wait lock, start a timer, set wait call flag
1473        //          Get call started indication from bluetooth phone
1474        mDialingOut = true;
1475        sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE);
1476    }
1477
1478    private void processVolumeEvent(int volumeType, int volume) {
1479        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
1480            mPhoneState.setSpeakerVolume(volume);
1481            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
1482            mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
1483        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
1484            mPhoneState.setMicVolume(volume);
1485        } else {
1486            Log.e(TAG, "Bad voluem type: " + volumeType);
1487        }
1488    }
1489
1490    private void processSendDtmf(int dtmf) {
1491        if (mPhoneProxy != null) {
1492            try {
1493                mPhoneProxy.sendDtmf(dtmf);
1494            } catch (RemoteException e) {
1495                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1496            }
1497        } else {
1498            Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
1499        }
1500    }
1501
1502    private void processCallState(HeadsetCallState callState) {
1503        processCallState(callState, false);
1504    }
1505
1506    private void processCallState(HeadsetCallState callState,
1507        boolean isVirtualCall) {
1508        mPhoneState.setNumActiveCall(callState.mNumActive);
1509        mPhoneState.setNumHeldCall(callState.mNumHeld);
1510        mPhoneState.setCallState(callState.mCallState);
1511        if (mDialingOut && callState.mCallState ==
1512            HeadsetHalConstants.CALL_STATE_DIALING) {
1513                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1514                removeMessages(DIALING_OUT_TIMEOUT);
1515                mDialingOut = false;
1516        }
1517        log("mNumActive: " + callState.mNumActive + " mNumHeld: " +
1518            callState.mNumHeld +" mCallState: " + callState.mCallState);
1519        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
1520
1521        if (isVirtualCall) {
1522            // virtual call state update
1523            if (getCurrentState() != mDisconnected) {
1524                phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
1525                    callState.mCallState, callState.mNumber, callState.mType);
1526            }
1527        } else {
1528            // circuit-switch voice call update
1529            // stop virtual voice call if there is a CSV call ongoing
1530            if (callState.mNumActive > 0 || callState.mNumHeld > 0
1531                    || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) {
1532                terminateScoUsingVirtualVoiceCall();
1533            }
1534            // at this step: if there is virtual call ongoing, it means there is no CSV call
1535            // let virtual call continue and skip phone state update
1536            if (!isVirtualCallInProgress()) {
1537                if (getCurrentState() != mDisconnected) {
1538                    phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
1539                        callState.mCallState, callState.mNumber, callState.mType);
1540                }
1541            }
1542        }
1543    }
1544
1545    // enable 1 enable noice reduction
1546    //        0 disable noice reduction
1547    private void processNoiceReductionEvent(int enable) {
1548        if (enable == 1) {
1549            mAudioManager.setParameters(HEADSET_NREC + "=on");
1550        } else {
1551            mAudioManager.setParameters(HEADSET_NREC + "=off");
1552        }
1553    }
1554
1555    private void processAtChld(int chld) {
1556        if (mPhoneProxy != null) {
1557            try {
1558                if (mPhoneProxy.processChld(chld)) {
1559                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1560                } else {
1561                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1562                }
1563            } catch (RemoteException e) {
1564                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1565                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1566            }
1567        } else {
1568            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
1569            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1570        }
1571    }
1572
1573    private void processSubscriberNumberRequest() {
1574        if (mPhoneProxy != null) {
1575            try {
1576                String number = mPhoneProxy.getSubscriberNumber();
1577                if (number != null) {
1578                    atResponseStringNative("+CNUM: ,\"" + number + "\"," +
1579                                           PhoneNumberUtils.toaFromString(number) + ",,4");
1580                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1581                }
1582            } catch (RemoteException e) {
1583                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1584                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1585            }
1586        } else {
1587            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
1588        }
1589    }
1590
1591    private void processAtCind() {
1592        int call, call_setup;
1593
1594        /* Handsfree carkits expect that +CIND is properly responded to
1595         Hence we ensure that a proper response is sent
1596         for the virtual call too.*/
1597        if (isVirtualCallInProgress()) {
1598            call = 1;
1599            call_setup = 0;
1600        } else {
1601            // regular phone call
1602            call = mPhoneState.getNumActiveCall();
1603            call_setup = mPhoneState.getNumHeldCall();
1604        }
1605
1606        cindResponseNative(mPhoneState.getService(), call,
1607                           call_setup, mPhoneState.getCallState(),
1608                           mPhoneState.getSignal(), mPhoneState.getRoam(),
1609                           mPhoneState.getBatteryCharge());
1610    }
1611
1612    private void processAtCops() {
1613        if (mPhoneProxy != null) {
1614            try {
1615                String operatorName = mPhoneProxy.getNetworkOperator();
1616                if (operatorName == null) {
1617                    operatorName = "";
1618                }
1619                copsResponseNative(operatorName);
1620            } catch (RemoteException e) {
1621                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1622                copsResponseNative("");
1623            }
1624        } else {
1625            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
1626            copsResponseNative("");
1627        }
1628    }
1629
1630    private void processAtClcc() {
1631        if (mPhoneProxy != null) {
1632            try {
1633                if(isVirtualCallInProgress()) {
1634                    String phoneNumber = "";
1635                    int type = PhoneNumberUtils.TOA_Unknown;
1636                    try {
1637                        phoneNumber = mPhoneProxy.getSubscriberNumber();
1638                        type = PhoneNumberUtils.toaFromString(phoneNumber);
1639                    } catch (RemoteException ee) {
1640                        Log.e(TAG, "Unable to retrieve phone number"+
1641                            "using IBluetoothHeadsetPhone proxy");
1642                        phoneNumber = "";
1643                    }
1644                    clccResponseNative(1, 0, 0, 0, false, phoneNumber, type);
1645                    clccResponseNative(0, 0, 0, 0, false, "", 0);
1646                }
1647                else if (!mPhoneProxy.listCurrentCalls()) {
1648                    clccResponseNative(0, 0, 0, 0, false, "", 0);
1649                }
1650            } catch (RemoteException e) {
1651                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1652                clccResponseNative(0, 0, 0, 0, false, "", 0);
1653            }
1654        } else {
1655            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
1656            clccResponseNative(0, 0, 0, 0, false, "", 0);
1657        }
1658    }
1659
1660    private void processAtCscs(String atString, int type) {
1661        log("processAtCscs - atString = "+ atString);
1662        if(mPhonebook != null) {
1663            mPhonebook.handleCscsCommand(atString, type);
1664        }
1665        else {
1666            Log.e(TAG, "Phonebook handle null for At+CSCS");
1667            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1668        }
1669    }
1670
1671    private void processAtCpbs(String atString, int type) {
1672        log("processAtCpbs - atString = "+ atString);
1673        if(mPhonebook != null) {
1674            mPhonebook.handleCpbsCommand(atString, type);
1675        }
1676        else {
1677            Log.e(TAG, "Phonebook handle null for At+CPBS");
1678            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1679        }
1680    }
1681
1682    private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) {
1683        log("processAtCpbr - atString = "+ atString);
1684        if(mPhonebook != null) {
1685            mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice);
1686        }
1687        else {
1688            Log.e(TAG, "Phonebook handle null for At+CPBR");
1689            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1690        }
1691    }
1692
1693    /**
1694     * Find a character ch, ignoring quoted sections.
1695     * Return input.length() if not found.
1696     */
1697    static private int findChar(char ch, String input, int fromIndex) {
1698        for (int i = fromIndex; i < input.length(); i++) {
1699            char c = input.charAt(i);
1700            if (c == '"') {
1701                i = input.indexOf('"', i + 1);
1702                if (i == -1) {
1703                    return input.length();
1704                }
1705            } else if (c == ch) {
1706                return i;
1707            }
1708        }
1709        return input.length();
1710    }
1711
1712    /**
1713     * Break an argument string into individual arguments (comma delimited).
1714     * Integer arguments are turned into Integer objects. Otherwise a String
1715     * object is used.
1716     */
1717    static private Object[] generateArgs(String input) {
1718        int i = 0;
1719        int j;
1720        ArrayList<Object> out = new ArrayList<Object>();
1721        while (i <= input.length()) {
1722            j = findChar(',', input, i);
1723
1724            String arg = input.substring(i, j);
1725            try {
1726                out.add(new Integer(arg));
1727            } catch (NumberFormatException e) {
1728                out.add(arg);
1729            }
1730
1731            i = j + 1; // move past comma
1732        }
1733        return out.toArray();
1734    }
1735
1736    /**
1737     * @return {@code true} if the given string is a valid vendor-specific AT command.
1738     */
1739    private boolean processVendorSpecificAt(String atString) {
1740        log("processVendorSpecificAt - atString = " + atString);
1741
1742        // Currently we accept only SET type commands.
1743        int indexOfEqual = atString.indexOf("=");
1744        if (indexOfEqual == -1) {
1745            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
1746            return false;
1747        }
1748
1749        String command = atString.substring(0, indexOfEqual);
1750        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
1751        if (companyId == null) {
1752            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
1753            return false;
1754        }
1755
1756        String arg = atString.substring(indexOfEqual + 1);
1757        if (arg.startsWith("?")) {
1758            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
1759            return false;
1760        }
1761
1762        Object[] args = generateArgs(arg);
1763        broadcastVendorSpecificEventIntent(command,
1764                                           companyId,
1765                                           BluetoothHeadset.AT_CMD_TYPE_SET,
1766                                           args,
1767                                           mCurrentDevice);
1768        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1769        return true;
1770    }
1771
1772    private void processUnknownAt(String atString) {
1773        // TODO (BT)
1774        log("processUnknownAt - atString = "+ atString);
1775        String atCommand = parseUnknownAt(atString);
1776        int commandType = getAtCommandType(atCommand);
1777        if (atCommand.startsWith("+CSCS"))
1778            processAtCscs(atCommand.substring(5), commandType);
1779        else if (atCommand.startsWith("+CPBS"))
1780            processAtCpbs(atCommand.substring(5), commandType);
1781        else if (atCommand.startsWith("+CPBR"))
1782            processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice);
1783        else if (!processVendorSpecificAt(atCommand))
1784            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1785    }
1786
1787    private void processKeyPressed() {
1788        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
1789            if (mPhoneProxy != null) {
1790                try {
1791                    mPhoneProxy.answerCall();
1792                } catch (RemoteException e) {
1793                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1794                }
1795            } else {
1796                Log.e(TAG, "Handsfree phone proxy null for answering call");
1797            }
1798        } else if (mPhoneState.getNumActiveCall() > 0) {
1799            if (!isAudioOn())
1800            {
1801                connectAudioNative(getByteAddress(mCurrentDevice));
1802            }
1803            else
1804            {
1805                if (mPhoneProxy != null) {
1806                    try {
1807                        mPhoneProxy.hangupCall();
1808                    } catch (RemoteException e) {
1809                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
1810                    }
1811                } else {
1812                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
1813                }
1814            }
1815        } else {
1816            String dialNumber = mPhonebook.getLastDialledNumber();
1817            if (dialNumber == null) {
1818                if (DBG) log("processKeyPressed, last dial number null");
1819                return;
1820            }
1821            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1822                                       Uri.fromParts(SCHEME_TEL, dialNumber, null));
1823            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1824            mService.startActivity(intent);
1825        }
1826    }
1827
1828    private void onConnectionStateChanged(int state, byte[] address) {
1829        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
1830        event.valueInt = state;
1831        event.device = getDevice(address);
1832        sendMessage(STACK_EVENT, event);
1833    }
1834
1835    private void onAudioStateChanged(int state, byte[] address) {
1836        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
1837        event.valueInt = state;
1838        event.device = getDevice(address);
1839        sendMessage(STACK_EVENT, event);
1840    }
1841
1842    private void onVrStateChanged(int state) {
1843        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
1844        event.valueInt = state;
1845        sendMessage(STACK_EVENT, event);
1846    }
1847
1848    private void onAnswerCall() {
1849        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
1850        sendMessage(STACK_EVENT, event);
1851    }
1852
1853    private void onHangupCall() {
1854        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
1855        sendMessage(STACK_EVENT, event);
1856    }
1857
1858    private void onVolumeChanged(int type, int volume) {
1859        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
1860        event.valueInt = type;
1861        event.valueInt2 = volume;
1862        sendMessage(STACK_EVENT, event);
1863    }
1864
1865    private void onDialCall(String number) {
1866        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
1867        event.valueString = number;
1868        sendMessage(STACK_EVENT, event);
1869    }
1870
1871    private void onSendDtmf(int dtmf) {
1872        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
1873        event.valueInt = dtmf;
1874        sendMessage(STACK_EVENT, event);
1875    }
1876
1877    private void onNoiceReductionEnable(boolean enable) {
1878        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
1879        event.valueInt = enable ? 1 : 0;
1880        sendMessage(STACK_EVENT, event);
1881    }
1882
1883    private void onAtChld(int chld) {
1884        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
1885        event.valueInt = chld;
1886        sendMessage(STACK_EVENT, event);
1887    }
1888
1889    private void onAtCnum() {
1890        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
1891        sendMessage(STACK_EVENT, event);
1892    }
1893
1894    private void onAtCind() {
1895        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
1896        sendMessage(STACK_EVENT, event);
1897    }
1898
1899    private void onAtCops() {
1900        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
1901        sendMessage(STACK_EVENT, event);
1902    }
1903
1904    private void onAtClcc() {
1905        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
1906        sendMessage(STACK_EVENT, event);
1907    }
1908
1909    private void onUnknownAt(String atString) {
1910        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
1911        event.valueString = atString;
1912        sendMessage(STACK_EVENT, event);
1913    }
1914
1915    private void onKeyPressed() {
1916        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
1917        sendMessage(STACK_EVENT, event);
1918    }
1919
1920    private void processIntentBatteryChanged(Intent intent) {
1921        int batteryLevel = intent.getIntExtra("level", -1);
1922        int scale = intent.getIntExtra("scale", -1);
1923        if (batteryLevel == -1 || scale == -1 || scale == 0) {
1924            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
1925            return;
1926        }
1927        batteryLevel = batteryLevel * 5 / scale;
1928        mPhoneState.setBatteryCharge(batteryLevel);
1929    }
1930
1931    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
1932        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
1933                                 deviceState.mBatteryCharge);
1934    }
1935
1936    private void processSendClccResponse(HeadsetClccResponse clcc) {
1937        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
1938                           clcc.mNumber, clcc.mType);
1939    }
1940
1941    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
1942        String stringToSend = resultCode.mCommand + ": ";
1943        if (resultCode.mArg != null) {
1944            stringToSend += resultCode.mArg;
1945        }
1946        atResponseStringNative(stringToSend);
1947    }
1948
1949    private String getCurrentDeviceName() {
1950        String defaultName = "<unknown>";
1951        if (mCurrentDevice == null) {
1952            return defaultName;
1953        }
1954        String deviceName = mCurrentDevice.getName();
1955        if (deviceName == null) {
1956            return defaultName;
1957        }
1958        return deviceName;
1959    }
1960
1961    private byte[] getByteAddress(BluetoothDevice device) {
1962        return Utils.getBytesFromAddress(device.getAddress());
1963    }
1964
1965    private BluetoothDevice getDevice(byte[] address) {
1966        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
1967    }
1968
1969    private boolean isInCall() {
1970        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
1971                (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
1972    }
1973
1974    boolean isConnected() {
1975        IState currentState = getCurrentState();
1976        return (currentState == mConnected || currentState == mAudioOn);
1977    }
1978
1979    boolean okToConnect(BluetoothDevice device) {
1980        AdapterService adapterService = AdapterService.getAdapterService();
1981        int priority = mService.getPriority(device);
1982        boolean ret = false;
1983        //check if this is an incoming connection in Quiet mode.
1984        if((adapterService == null) ||
1985           ((adapterService.isQuietModeEnabled() == true) &&
1986           (mTargetDevice == null))){
1987            ret = false;
1988        }
1989        // check priority and accept or reject the connection. if priority is undefined
1990        // it is likely that our SDP has not completed and peer is initiating the
1991        // connection. Allow this connection, provided the device is bonded
1992        else if((BluetoothProfile.PRIORITY_OFF < priority) ||
1993                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
1994                (device.getBondState() != BluetoothDevice.BOND_NONE))){
1995            ret= true;
1996        }
1997        return ret;
1998    }
1999
2000    @Override
2001    protected void log(String msg) {
2002        if (DBG) {
2003            super.log(msg);
2004        }
2005    }
2006
2007    public void handleAccessPermissionResult(Intent intent) {
2008        log("handleAccessPermissionResult");
2009        if(mPhonebook != null) {
2010            if (!mPhonebook.getCheckingAccessPermission()) {
2011                return;
2012            }
2013            int atCommandResult = 0;
2014            int atCommandErrorCode = 0;
2015            //HeadsetBase headset = mHandsfree.getHeadset();
2016            // ASSERT: (headset != null) && headSet.isConnected()
2017            // REASON: mCheckingAccessPermission is true, otherwise resetAtState
2018            // has set mCheckingAccessPermission to false
2019            if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
2020                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
2021                    BluetoothDevice.CONNECTION_ACCESS_NO) ==
2022                    BluetoothDevice.CONNECTION_ACCESS_YES) {
2023                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
2024                        mCurrentDevice.setTrust(true);
2025                    }
2026                    atCommandResult = mPhonebook.processCpbrCommand();
2027                }
2028            }
2029            mPhonebook.setCpbrIndex(-1);
2030            mPhonebook.setCheckingAccessPermission(false);
2031
2032            if (atCommandResult >= 0) {
2033                atResponseCodeNative(atCommandResult, atCommandErrorCode);
2034            }
2035            else
2036                log("handleAccessPermissionResult - RESULT_NONE");
2037        }
2038        else {
2039            Log.e(TAG, "Phonebook handle null");
2040            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2041        }
2042    }
2043
2044    private static final String SCHEME_TEL = "tel";
2045
2046    // Event types for STACK_EVENT message
2047    final private static int EVENT_TYPE_NONE = 0;
2048    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
2049    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
2050    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
2051    final private static int EVENT_TYPE_ANSWER_CALL = 4;
2052    final private static int EVENT_TYPE_HANGUP_CALL = 5;
2053    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
2054    final private static int EVENT_TYPE_DIAL_CALL = 7;
2055    final private static int EVENT_TYPE_SEND_DTMF = 8;
2056    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
2057    final private static int EVENT_TYPE_AT_CHLD = 10;
2058    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
2059    final private static int EVENT_TYPE_AT_CIND = 12;
2060    final private static int EVENT_TYPE_AT_COPS = 13;
2061    final private static int EVENT_TYPE_AT_CLCC = 14;
2062    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
2063    final private static int EVENT_TYPE_KEY_PRESSED = 16;
2064
2065    private class StackEvent {
2066        int type = EVENT_TYPE_NONE;
2067        int valueInt = 0;
2068        int valueInt2 = 0;
2069        String valueString = null;
2070        BluetoothDevice device = null;
2071
2072        private StackEvent(int type) {
2073            this.type = type;
2074        }
2075    }
2076
2077    /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode);
2078    /*package*/ native boolean atResponseStringNative(String responseString);
2079
2080    private native static void classInitNative();
2081    private native void initializeNative();
2082    private native void cleanupNative();
2083    private native boolean connectHfpNative(byte[] address);
2084    private native boolean disconnectHfpNative(byte[] address);
2085    private native boolean connectAudioNative(byte[] address);
2086    private native boolean disconnectAudioNative(byte[] address);
2087    private native boolean startVoiceRecognitionNative();
2088    private native boolean stopVoiceRecognitionNative();
2089    private native boolean setVolumeNative(int volumeType, int volume);
2090    private native boolean cindResponseNative(int service, int numActive, int numHeld,
2091                                              int callState, int signal, int roam,
2092                                              int batteryCharge);
2093    private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
2094                                                    int batteryCharge);
2095
2096    private native boolean clccResponseNative(int index, int dir, int status, int mode,
2097                                              boolean mpty, String number, int type);
2098    private native boolean copsResponseNative(String operatorName);
2099
2100    private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
2101                                                  String number, int type);
2102}
2103