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