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        if(!isVirtualCall) {
1521            /* Not a Virtual call request. End the virtual call, if running,
1522            before sending phoneStateChangeNative to BTIF */
1523            terminateScoUsingVirtualVoiceCall();
1524        }
1525        if (getCurrentState() != mDisconnected) {
1526            phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
1527                callState.mCallState, callState.mNumber, callState.mType);
1528        }
1529    }
1530
1531    // enable 1 enable noice reduction
1532    //        0 disable noice reduction
1533    private void processNoiceReductionEvent(int enable) {
1534        if (enable == 1) {
1535            mAudioManager.setParameters(HEADSET_NREC + "=on");
1536        } else {
1537            mAudioManager.setParameters(HEADSET_NREC + "=off");
1538        }
1539    }
1540
1541    private void processAtChld(int chld) {
1542        if (mPhoneProxy != null) {
1543            try {
1544                if (mPhoneProxy.processChld(chld)) {
1545                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1546                } else {
1547                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1548                }
1549            } catch (RemoteException e) {
1550                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1551                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1552            }
1553        } else {
1554            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
1555            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1556        }
1557    }
1558
1559    private void processSubscriberNumberRequest() {
1560        if (mPhoneProxy != null) {
1561            try {
1562                String number = mPhoneProxy.getSubscriberNumber();
1563                if (number != null) {
1564                    atResponseStringNative("+CNUM: ,\"" + number + "\"," +
1565                                           PhoneNumberUtils.toaFromString(number) + ",,4");
1566                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1567                }
1568            } catch (RemoteException e) {
1569                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1570                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1571            }
1572        } else {
1573            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
1574        }
1575    }
1576
1577    private void processAtCind() {
1578        int call, call_setup;
1579
1580        /* Handsfree carkits expect that +CIND is properly responded to
1581         Hence we ensure that a proper response is sent
1582         for the virtual call too.*/
1583        if (isVirtualCallInProgress()) {
1584            call = 1;
1585            call_setup = 0;
1586        } else {
1587            // regular phone call
1588            call = mPhoneState.getNumActiveCall();
1589            call_setup = mPhoneState.getNumHeldCall();
1590        }
1591
1592        cindResponseNative(mPhoneState.getService(), call,
1593                           call_setup, mPhoneState.getCallState(),
1594                           mPhoneState.getSignal(), mPhoneState.getRoam(),
1595                           mPhoneState.getBatteryCharge());
1596    }
1597
1598    private void processAtCops() {
1599        if (mPhoneProxy != null) {
1600            try {
1601                String operatorName = mPhoneProxy.getNetworkOperator();
1602                if (operatorName == null) {
1603                    operatorName = "";
1604                }
1605                copsResponseNative(operatorName);
1606            } catch (RemoteException e) {
1607                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1608                copsResponseNative("");
1609            }
1610        } else {
1611            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
1612            copsResponseNative("");
1613        }
1614    }
1615
1616    private void processAtClcc() {
1617        if (mPhoneProxy != null) {
1618            try {
1619                if(isVirtualCallInProgress()) {
1620                    String phoneNumber = "";
1621                    int type = PhoneNumberUtils.TOA_Unknown;
1622                    try {
1623                        phoneNumber = mPhoneProxy.getSubscriberNumber();
1624                        type = PhoneNumberUtils.toaFromString(phoneNumber);
1625                    } catch (RemoteException ee) {
1626                        Log.e(TAG, "Unable to retrieve phone number"+
1627                            "using IBluetoothHeadsetPhone proxy");
1628                        phoneNumber = "";
1629                    }
1630                    clccResponseNative(1, 0, 0, 0, false, phoneNumber, type);
1631                }
1632                else if (!mPhoneProxy.listCurrentCalls()) {
1633                    clccResponseNative(0, 0, 0, 0, false, "", 0);
1634                }
1635            } catch (RemoteException e) {
1636                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1637                clccResponseNative(0, 0, 0, 0, false, "", 0);
1638            }
1639        } else {
1640            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
1641            clccResponseNative(0, 0, 0, 0, false, "", 0);
1642        }
1643    }
1644
1645    private void processAtCscs(String atString, int type) {
1646        log("processAtCscs - atString = "+ atString);
1647        if(mPhonebook != null) {
1648            mPhonebook.handleCscsCommand(atString, type);
1649        }
1650        else {
1651            Log.e(TAG, "Phonebook handle null for At+CSCS");
1652            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1653        }
1654    }
1655
1656    private void processAtCpbs(String atString, int type) {
1657        log("processAtCpbs - atString = "+ atString);
1658        if(mPhonebook != null) {
1659            mPhonebook.handleCpbsCommand(atString, type);
1660        }
1661        else {
1662            Log.e(TAG, "Phonebook handle null for At+CPBS");
1663            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1664        }
1665    }
1666
1667    private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) {
1668        log("processAtCpbr - atString = "+ atString);
1669        if(mPhonebook != null) {
1670            mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice);
1671        }
1672        else {
1673            Log.e(TAG, "Phonebook handle null for At+CPBR");
1674            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1675        }
1676    }
1677
1678    /**
1679     * Find a character ch, ignoring quoted sections.
1680     * Return input.length() if not found.
1681     */
1682    static private int findChar(char ch, String input, int fromIndex) {
1683        for (int i = fromIndex; i < input.length(); i++) {
1684            char c = input.charAt(i);
1685            if (c == '"') {
1686                i = input.indexOf('"', i + 1);
1687                if (i == -1) {
1688                    return input.length();
1689                }
1690            } else if (c == ch) {
1691                return i;
1692            }
1693        }
1694        return input.length();
1695    }
1696
1697    /**
1698     * Break an argument string into individual arguments (comma delimited).
1699     * Integer arguments are turned into Integer objects. Otherwise a String
1700     * object is used.
1701     */
1702    static private Object[] generateArgs(String input) {
1703        int i = 0;
1704        int j;
1705        ArrayList<Object> out = new ArrayList<Object>();
1706        while (i <= input.length()) {
1707            j = findChar(',', input, i);
1708
1709            String arg = input.substring(i, j);
1710            try {
1711                out.add(new Integer(arg));
1712            } catch (NumberFormatException e) {
1713                out.add(arg);
1714            }
1715
1716            i = j + 1; // move past comma
1717        }
1718        return out.toArray();
1719    }
1720
1721    /**
1722     * @return {@code true} if the given string is a valid vendor-specific AT command.
1723     */
1724    private boolean processVendorSpecificAt(String atString) {
1725        log("processVendorSpecificAt - atString = " + atString);
1726
1727        // Currently we accept only SET type commands.
1728        int indexOfEqual = atString.indexOf("=");
1729        if (indexOfEqual == -1) {
1730            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
1731            return false;
1732        }
1733
1734        String command = atString.substring(0, indexOfEqual);
1735        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
1736        if (companyId == null) {
1737            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
1738            return false;
1739        }
1740
1741        String arg = atString.substring(indexOfEqual + 1);
1742        if (arg.startsWith("?")) {
1743            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
1744            return false;
1745        }
1746
1747        Object[] args = generateArgs(arg);
1748        broadcastVendorSpecificEventIntent(command,
1749                                           companyId,
1750                                           BluetoothHeadset.AT_CMD_TYPE_SET,
1751                                           args,
1752                                           mCurrentDevice);
1753        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
1754        return true;
1755    }
1756
1757    private void processUnknownAt(String atString) {
1758        // TODO (BT)
1759        log("processUnknownAt - atString = "+ atString);
1760        String atCommand = parseUnknownAt(atString);
1761        int commandType = getAtCommandType(atCommand);
1762        if (atCommand.startsWith("+CSCS"))
1763            processAtCscs(atCommand.substring(5), commandType);
1764        else if (atCommand.startsWith("+CPBS"))
1765            processAtCpbs(atCommand.substring(5), commandType);
1766        else if (atCommand.startsWith("+CPBR"))
1767            processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice);
1768        else if (!processVendorSpecificAt(atCommand))
1769            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1770    }
1771
1772    private void processKeyPressed() {
1773        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
1774            if (mPhoneProxy != null) {
1775                try {
1776                    mPhoneProxy.answerCall();
1777                } catch (RemoteException e) {
1778                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1779                }
1780            } else {
1781                Log.e(TAG, "Handsfree phone proxy null for answering call");
1782            }
1783        } else if (mPhoneState.getNumActiveCall() > 0) {
1784            if (!isAudioOn())
1785            {
1786                connectAudioNative(getByteAddress(mCurrentDevice));
1787            }
1788            else
1789            {
1790                if (mPhoneProxy != null) {
1791                    try {
1792                        mPhoneProxy.hangupCall();
1793                    } catch (RemoteException e) {
1794                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
1795                    }
1796                } else {
1797                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
1798                }
1799            }
1800        } else {
1801            String dialNumber = mPhonebook.getLastDialledNumber();
1802            if (dialNumber == null) {
1803                if (DBG) log("processKeyPressed, last dial number null");
1804                return;
1805            }
1806            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1807                                       Uri.fromParts(SCHEME_TEL, dialNumber, null));
1808            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1809            mService.startActivity(intent);
1810        }
1811    }
1812
1813    private void onConnectionStateChanged(int state, byte[] address) {
1814        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
1815        event.valueInt = state;
1816        event.device = getDevice(address);
1817        sendMessage(STACK_EVENT, event);
1818    }
1819
1820    private void onAudioStateChanged(int state, byte[] address) {
1821        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
1822        event.valueInt = state;
1823        event.device = getDevice(address);
1824        sendMessage(STACK_EVENT, event);
1825    }
1826
1827    private void onVrStateChanged(int state) {
1828        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
1829        event.valueInt = state;
1830        sendMessage(STACK_EVENT, event);
1831    }
1832
1833    private void onAnswerCall() {
1834        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
1835        sendMessage(STACK_EVENT, event);
1836    }
1837
1838    private void onHangupCall() {
1839        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
1840        sendMessage(STACK_EVENT, event);
1841    }
1842
1843    private void onVolumeChanged(int type, int volume) {
1844        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
1845        event.valueInt = type;
1846        event.valueInt2 = volume;
1847        sendMessage(STACK_EVENT, event);
1848    }
1849
1850    private void onDialCall(String number) {
1851        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
1852        event.valueString = number;
1853        sendMessage(STACK_EVENT, event);
1854    }
1855
1856    private void onSendDtmf(int dtmf) {
1857        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
1858        event.valueInt = dtmf;
1859        sendMessage(STACK_EVENT, event);
1860    }
1861
1862    private void onNoiceReductionEnable(boolean enable) {
1863        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
1864        event.valueInt = enable ? 1 : 0;
1865        sendMessage(STACK_EVENT, event);
1866    }
1867
1868    private void onAtChld(int chld) {
1869        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
1870        event.valueInt = chld;
1871        sendMessage(STACK_EVENT, event);
1872    }
1873
1874    private void onAtCnum() {
1875        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
1876        sendMessage(STACK_EVENT, event);
1877    }
1878
1879    private void onAtCind() {
1880        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
1881        sendMessage(STACK_EVENT, event);
1882    }
1883
1884    private void onAtCops() {
1885        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
1886        sendMessage(STACK_EVENT, event);
1887    }
1888
1889    private void onAtClcc() {
1890        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
1891        sendMessage(STACK_EVENT, event);
1892    }
1893
1894    private void onUnknownAt(String atString) {
1895        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
1896        event.valueString = atString;
1897        sendMessage(STACK_EVENT, event);
1898    }
1899
1900    private void onKeyPressed() {
1901        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
1902        sendMessage(STACK_EVENT, event);
1903    }
1904
1905    private void processIntentBatteryChanged(Intent intent) {
1906        int batteryLevel = intent.getIntExtra("level", -1);
1907        int scale = intent.getIntExtra("scale", -1);
1908        if (batteryLevel == -1 || scale == -1 || scale == 0) {
1909            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
1910            return;
1911        }
1912        batteryLevel = batteryLevel * 5 / scale;
1913        mPhoneState.setBatteryCharge(batteryLevel);
1914    }
1915
1916    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
1917        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
1918                                 deviceState.mBatteryCharge);
1919    }
1920
1921    private void processSendClccResponse(HeadsetClccResponse clcc) {
1922        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
1923                           clcc.mNumber, clcc.mType);
1924    }
1925
1926    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
1927        String stringToSend = resultCode.mCommand + ": ";
1928        if (resultCode.mArg != null) {
1929            stringToSend += resultCode.mArg;
1930        }
1931        atResponseStringNative(stringToSend);
1932    }
1933
1934    private String getCurrentDeviceName() {
1935        String defaultName = "<unknown>";
1936        if (mCurrentDevice == null) {
1937            return defaultName;
1938        }
1939        String deviceName = mCurrentDevice.getName();
1940        if (deviceName == null) {
1941            return defaultName;
1942        }
1943        return deviceName;
1944    }
1945
1946    private byte[] getByteAddress(BluetoothDevice device) {
1947        return Utils.getBytesFromAddress(device.getAddress());
1948    }
1949
1950    private BluetoothDevice getDevice(byte[] address) {
1951        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
1952    }
1953
1954    private boolean isInCall() {
1955        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
1956                (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
1957    }
1958
1959    boolean isConnected() {
1960        IState currentState = getCurrentState();
1961        return (currentState == mConnected || currentState == mAudioOn);
1962    }
1963
1964    boolean okToConnect(BluetoothDevice device) {
1965        AdapterService adapterService = AdapterService.getAdapterService();
1966        int priority = mService.getPriority(device);
1967        boolean ret = false;
1968        //check if this is an incoming connection in Quiet mode.
1969        if((adapterService == null) ||
1970           ((adapterService.isQuietModeEnabled() == true) &&
1971           (mTargetDevice == null))){
1972            ret = false;
1973        }
1974        // check priority and accept or reject the connection. if priority is undefined
1975        // it is likely that our SDP has not completed and peer is initiating the
1976        // connection. Allow this connection, provided the device is bonded
1977        else if((BluetoothProfile.PRIORITY_OFF < priority) ||
1978                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
1979                (device.getBondState() != BluetoothDevice.BOND_NONE))){
1980            ret= true;
1981        }
1982        return ret;
1983    }
1984
1985    @Override
1986    protected void log(String msg) {
1987        if (DBG) {
1988            super.log(msg);
1989        }
1990    }
1991
1992    public void handleAccessPermissionResult(Intent intent) {
1993        log("handleAccessPermissionResult");
1994        if(mPhonebook != null) {
1995            if (!mPhonebook.getCheckingAccessPermission()) {
1996                return;
1997            }
1998            int atCommandResult = 0;
1999            int atCommandErrorCode = 0;
2000            //HeadsetBase headset = mHandsfree.getHeadset();
2001            // ASSERT: (headset != null) && headSet.isConnected()
2002            // REASON: mCheckingAccessPermission is true, otherwise resetAtState
2003            // has set mCheckingAccessPermission to false
2004            if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
2005                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
2006                    BluetoothDevice.CONNECTION_ACCESS_NO) ==
2007                    BluetoothDevice.CONNECTION_ACCESS_YES) {
2008                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
2009                        mCurrentDevice.setTrust(true);
2010                    }
2011                    atCommandResult = mPhonebook.processCpbrCommand();
2012                }
2013            }
2014            mPhonebook.setCpbrIndex(-1);
2015            mPhonebook.setCheckingAccessPermission(false);
2016
2017            if (atCommandResult >= 0) {
2018                atResponseCodeNative(atCommandResult, atCommandErrorCode);
2019            }
2020            else
2021                log("handleAccessPermissionResult - RESULT_NONE");
2022        }
2023        else {
2024            Log.e(TAG, "Phonebook handle null");
2025            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2026        }
2027    }
2028
2029    private static final String SCHEME_TEL = "tel";
2030
2031    // Event types for STACK_EVENT message
2032    final private static int EVENT_TYPE_NONE = 0;
2033    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
2034    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
2035    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
2036    final private static int EVENT_TYPE_ANSWER_CALL = 4;
2037    final private static int EVENT_TYPE_HANGUP_CALL = 5;
2038    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
2039    final private static int EVENT_TYPE_DIAL_CALL = 7;
2040    final private static int EVENT_TYPE_SEND_DTMF = 8;
2041    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
2042    final private static int EVENT_TYPE_AT_CHLD = 10;
2043    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
2044    final private static int EVENT_TYPE_AT_CIND = 12;
2045    final private static int EVENT_TYPE_AT_COPS = 13;
2046    final private static int EVENT_TYPE_AT_CLCC = 14;
2047    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
2048    final private static int EVENT_TYPE_KEY_PRESSED = 16;
2049
2050    private class StackEvent {
2051        int type = EVENT_TYPE_NONE;
2052        int valueInt = 0;
2053        int valueInt2 = 0;
2054        String valueString = null;
2055        BluetoothDevice device = null;
2056
2057        private StackEvent(int type) {
2058            this.type = type;
2059        }
2060    }
2061
2062    /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode);
2063    /*package*/ native boolean atResponseStringNative(String responseString);
2064
2065    private native static void classInitNative();
2066    private native void initializeNative();
2067    private native void cleanupNative();
2068    private native boolean connectHfpNative(byte[] address);
2069    private native boolean disconnectHfpNative(byte[] address);
2070    private native boolean connectAudioNative(byte[] address);
2071    private native boolean disconnectAudioNative(byte[] address);
2072    private native boolean startVoiceRecognitionNative();
2073    private native boolean stopVoiceRecognitionNative();
2074    private native boolean setVolumeNative(int volumeType, int volume);
2075    private native boolean cindResponseNative(int service, int numActive, int numHeld,
2076                                              int callState, int signal, int roam,
2077                                              int batteryCharge);
2078    private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
2079                                                    int batteryCharge);
2080
2081    private native boolean clccResponseNative(int index, int dir, int status, int mode,
2082                                              boolean mpty, String number, int type);
2083    private native boolean copsResponseNative(String operatorName);
2084
2085    private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
2086                                                  String number, int type);
2087}
2088