HeadsetStateMachine.java revision 6654f5c903de510a70f9e72cd5ad7837b615d93f
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5/**
6 * Bluetooth Handset StateMachine
7 *                      (Disconnected)
8 *                           |    ^
9 *                   CONNECT |    | DISCONNECTED
10 *                           V    |
11 *                         (Pending)
12 *                           |    ^
13 *                 CONNECTED |    | CONNECT
14 *                           V    |
15 *                        (Connected)
16 *                           |    ^
17 *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
18 *                           V    |
19 *                         (AudioOn)
20 */
21package com.android.bluetooth.hfp;
22
23import android.bluetooth.BluetoothAdapter;
24import android.bluetooth.BluetoothDevice;
25import android.bluetooth.BluetoothHeadset;
26import android.bluetooth.BluetoothProfile;
27import android.bluetooth.BluetoothUuid;
28import android.bluetooth.IBluetooth;
29import android.bluetooth.IBluetoothHeadsetPhone;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.ServiceConnection;
34import android.content.ActivityNotFoundException;
35import android.media.AudioManager;
36import android.net.Uri;
37import android.os.IBinder;
38import android.os.Message;
39import android.os.ParcelUuid;
40import android.os.RemoteException;
41import android.os.ServiceManager;
42import android.os.PowerManager;
43import android.os.PowerManager.WakeLock;
44import android.telephony.PhoneNumberUtils;
45import android.util.Log;
46import com.android.bluetooth.Utils;
47import com.android.internal.util.IState;
48import com.android.internal.util.State;
49import com.android.internal.util.StateMachine;
50import java.util.ArrayList;
51import java.util.List;
52import java.util.Set;
53
54final class HeadsetStateMachine extends StateMachine {
55    private static final String TAG = "HeadsetStateMachine";
56    private static final boolean DBG = true;
57
58    static final int CONNECT = 1;
59    static final int DISCONNECT = 2;
60    static final int CONNECT_AUDIO = 3;
61    static final int DISCONNECT_AUDIO = 4;
62    static final int VOICE_RECOGNITION_START = 5;
63    static final int VOICE_RECOGNITION_STOP = 6;
64
65    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
66    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
67    static final int INTENT_SCO_VOLUME_CHANGED = 7;
68    static final int SET_MIC_VOLUME = 8;
69    static final int CALL_STATE_CHANGED = 9;
70    static final int INTENT_BATTERY_CHANGED = 10;
71    static final int DEVICE_STATE_CHANGED = 11;
72    static final int ROAM_CHANGED = 12;
73    static final int SEND_CCLC_RESPONSE = 13;
74
75    private static final int STACK_EVENT = 101;
76    private static final int DIALING_OUT_TIMEOUT = 102;
77    private static final int START_VR_TIMEOUT = 103;
78
79    private static final int CONNECT_TIMEOUT = 201;
80
81    private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
82    private static final int START_VR_TIMEOUT_VALUE = 5000;
83
84    private static final ParcelUuid[] HEADSET_UUIDS = {
85        BluetoothUuid.HSP,
86        BluetoothUuid.Handsfree,
87    };
88
89    private Disconnected mDisconnected;
90    private Pending mPending;
91    private Connected mConnected;
92    private AudioOn mAudioOn;
93
94    private Context mContext;
95
96    private PowerManager mPowerManager;
97
98    private boolean mVoiceRecognitionStarted = false;
99    private boolean mWaitingForVoiceRecognition = false;
100    private WakeLock mStartVoiceRecognitionWakeLock;  // held while waiting for voice recognition
101
102    private boolean mDialingOut = false;
103    private AudioManager mAudioManager;
104    private AtPhonebook mPhonebook;
105
106    private static Intent sVoiceCommandIntent;
107
108    private HeadsetPhoneState mPhoneState;
109    private int mAudioState;
110    private BluetoothAdapter mAdapter;
111    private IBluetooth mAdapterService;
112    private IBluetoothHeadsetPhone mPhoneProxy;
113
114    // mCurrentDevice is the device connected before the state changes
115    // mTargetDevice is the device to be connected
116    // mIncomingDevice is the device connecting to us, valid only in Pending state
117    //                when mIncomingDevice is not null, both mCurrentDevice
118    //                  and mTargetDevice are null
119    //                when either mCurrentDevice or mTargetDevice is not null,
120    //                  mIncomingDevice is null
121    // Stable states
122    //   No connection, Disconnected state
123    //                  both mCurrentDevice and mTargetDevice are null
124    //   Connected, Connected state
125    //              mCurrentDevice is not null, mTargetDevice is null
126    // Interim states
127    //   Connecting to a device, Pending
128    //                           mCurrentDevice is null, mTargetDevice is not null
129    //   Disconnecting device, Connecting to new device
130    //     Pending
131    //     Both mCurrentDevice and mTargetDevice are not null
132    //   Disconnecting device Pending
133    //                        mCurrentDevice is not null, mTargetDevice is null
134    //   Incoming connections Pending
135    //                        Both mCurrentDevice and mTargetDevice are null
136    private BluetoothDevice mCurrentDevice = null;
137    private BluetoothDevice mTargetDevice = null;
138    private BluetoothDevice mIncomingDevice = null;
139
140    static {
141        classInitNative();
142    }
143
144    HeadsetStateMachine(Context context) {
145        super(TAG);
146
147        mContext = context;
148        mVoiceRecognitionStarted = false;
149        mWaitingForVoiceRecognition = false;
150
151        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
152        mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
153                                                       TAG + ":VoiceRecognition");
154        mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
155
156        mDialingOut = false;
157        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
158        mPhonebook = new AtPhonebook(mContext);
159        mPhoneState = new HeadsetPhoneState(context, this);
160        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
161        mAdapter = BluetoothAdapter.getDefaultAdapter();
162        mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
163        if (!context.bindService(new Intent(IBluetoothHeadsetPhone.class.getName()),
164                                 mConnection, 0)) {
165            Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
166        }
167
168        initializeNative();
169
170        mDisconnected = new Disconnected();
171        mPending = new Pending();
172        mConnected = new Connected();
173        mAudioOn = new AudioOn();
174
175        if (sVoiceCommandIntent == null) {
176            sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
177            sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
178        }
179
180        addState(mDisconnected);
181        addState(mPending);
182        addState(mConnected);
183        addState(mAudioOn);
184
185        setInitialState(mDisconnected);
186    }
187
188    public void cleanup() {
189        cleanupNative();
190        if (mPhoneProxy != null) {
191            if (DBG) Log.d(TAG,"Unbinding service...");
192            synchronized (mConnection) {
193                try {
194                    mPhoneProxy = null;
195                    mContext.unbindService(mConnection);
196                } catch (Exception re) {
197                    Log.e(TAG,"",re);
198                }
199        }
200        }
201    }
202
203    private class Disconnected extends State {
204        @Override
205        public void enter() {
206            log("Enter Disconnected: " + getCurrentMessage().what);
207            mPhoneState.listenForPhoneState(false);
208        }
209
210        @Override
211        public boolean processMessage(Message message) {
212            log("Disconnected process message: " + message.what);
213            if (DBG) {
214                if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
215                    log("ERROR: current, target, or mIncomingDevice not null in Disconnected");
216                    return NOT_HANDLED;
217                }
218            }
219
220            boolean retValue = HANDLED;
221            switch(message.what) {
222                case CONNECT:
223                    BluetoothDevice device = (BluetoothDevice) message.obj;
224                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
225                                   BluetoothProfile.STATE_DISCONNECTED);
226
227                    if (!connectHfpNative(getByteAddress(device)) ) {
228                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
229                                       BluetoothProfile.STATE_CONNECTING);
230                        break;
231                    }
232
233                    synchronized (HeadsetStateMachine.this) {
234                        mTargetDevice = device;
235                        transitionTo(mPending);
236                    }
237                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
238                    //          sends back events consistently
239                    sendMessageDelayed(CONNECT_TIMEOUT, 30000);
240                    break;
241                case DISCONNECT:
242                    // ignore
243                    break;
244                case INTENT_BATTERY_CHANGED:
245                    processIntentBatteryChanged((Intent) message.obj);
246                    break;
247                case ROAM_CHANGED:
248                    processRoamChanged((Boolean) message.obj);
249                    break;
250                case CALL_STATE_CHANGED:
251                    processCallState((HeadsetCallState) message.obj);
252                    break;
253                case STACK_EVENT:
254                    StackEvent event = (StackEvent) message.obj;
255                    if (DBG) {
256                        log("event type: " + event.type);
257                    }
258                    switch (event.type) {
259                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
260                            processConnectionEvent(event.valueInt, event.device);
261                            break;
262                        default:
263                            Log.e(TAG, "Unexpected stack event: " + event.type);
264                            break;
265                    }
266                    break;
267                default:
268                    return NOT_HANDLED;
269            }
270            return retValue;
271        }
272
273        @Override
274        public void exit() {
275            log("Exit Disconnected: " + getCurrentMessage().what);
276            mPhoneState.listenForPhoneState(true);
277        }
278
279        // in Disconnected state
280        private void processConnectionEvent(int state, BluetoothDevice device) {
281            switch (state) {
282            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
283                Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
284                break;
285            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
286                // TODO(BT) Assume it's incoming connection
287                //     Do we need to check priority and accept/reject accordingly?
288                broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
289                                         BluetoothProfile.STATE_DISCONNECTED);
290                synchronized (HeadsetStateMachine.this) {
291                    mIncomingDevice = device;
292                    transitionTo(mPending);
293                }
294                break;
295            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
296                Log.w(TAG, "HFP Connected from Disconnected state");
297                broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
298                                         BluetoothProfile.STATE_DISCONNECTED);
299                synchronized (HeadsetStateMachine.this) {
300                    mCurrentDevice = device;
301                    transitionTo(mConnected);
302                }
303                break;
304            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
305                Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
306                break;
307            default:
308                Log.e(TAG, "Incorrect state: " + state);
309                break;
310            }
311        }
312    }
313
314    private class Pending extends State {
315        @Override
316        public void enter() {
317            log("Enter Pending: " + getCurrentMessage().what);
318        }
319
320        @Override
321        public boolean processMessage(Message message) {
322            log("Pending process message: " + message.what);
323
324            boolean retValue = HANDLED;
325            switch(message.what) {
326                case CONNECT:
327                case CONNECT_AUDIO:
328                    deferMessage(message);
329                    break;
330                case CONNECT_TIMEOUT:
331                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
332                                             getByteAddress(mTargetDevice));
333                    break;
334                case DISCONNECT:
335                    BluetoothDevice device = (BluetoothDevice) message.obj;
336                    if (mCurrentDevice != null && mTargetDevice != null &&
337                        mTargetDevice.equals(device) ) {
338                        // cancel connection to the mTargetDevice
339                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
340                                       BluetoothProfile.STATE_CONNECTING);
341                        synchronized (HeadsetStateMachine.this) {
342                            mTargetDevice = null;
343                        }
344                    } else {
345                        deferMessage(message);
346                    }
347                    break;
348                case INTENT_BATTERY_CHANGED:
349                    processIntentBatteryChanged((Intent) message.obj);
350                    break;
351                case ROAM_CHANGED:
352                    processRoamChanged((Boolean) message.obj);
353                    break;
354                case CALL_STATE_CHANGED:
355                    processCallState((HeadsetCallState) message.obj);
356                    break;
357                case STACK_EVENT:
358                    StackEvent event = (StackEvent) message.obj;
359                    if (DBG) {
360                        log("event type: " + event.type);
361                    }
362                    switch (event.type) {
363                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
364                            removeMessages(CONNECT_TIMEOUT);
365                            processConnectionEvent(event.valueInt, event.device);
366                            break;
367                        default:
368                            Log.e(TAG, "Unexpected event: " + event.type);
369                            break;
370                    }
371                    break;
372                default:
373                    return NOT_HANDLED;
374            }
375            return retValue;
376        }
377
378        // in Pending state
379        private void processConnectionEvent(int state, BluetoothDevice device) {
380            switch (state) {
381                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
382                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
383                        broadcastConnectionState(mCurrentDevice,
384                                                 BluetoothProfile.STATE_DISCONNECTED,
385                                                 BluetoothProfile.STATE_DISCONNECTING);
386                        synchronized (HeadsetStateMachine.this) {
387                            mCurrentDevice = null;
388                        }
389
390                        if (mTargetDevice != null) {
391                            if (!connectHfpNative(getByteAddress(mTargetDevice))) {
392                                broadcastConnectionState(mTargetDevice,
393                                                         BluetoothProfile.STATE_DISCONNECTED,
394                                                         BluetoothProfile.STATE_CONNECTING);
395                                synchronized (HeadsetStateMachine.this) {
396                                    mTargetDevice = null;
397                                    transitionTo(mDisconnected);
398                                }
399                            }
400                        } else {
401                            synchronized (HeadsetStateMachine.this) {
402                                mIncomingDevice = null;
403                                transitionTo(mDisconnected);
404                            }
405                        }
406                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
407                        // outgoing connection failed
408                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
409                                                 BluetoothProfile.STATE_CONNECTING);
410                        synchronized (HeadsetStateMachine.this) {
411                            mTargetDevice = null;
412                            transitionTo(mDisconnected);
413                        }
414                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
415                        broadcastConnectionState(mIncomingDevice,
416                                                 BluetoothProfile.STATE_DISCONNECTED,
417                                                 BluetoothProfile.STATE_CONNECTING);
418                        synchronized (HeadsetStateMachine.this) {
419                            mIncomingDevice = null;
420                            transitionTo(mDisconnected);
421                        }
422                    } else {
423                        Log.e(TAG, "Unknown device Disconnected: " + device);
424                    }
425                    break;
426            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
427                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
428                    // disconnection failed
429                    broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
430                                             BluetoothProfile.STATE_DISCONNECTING);
431                    if (mTargetDevice != null) {
432                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
433                                                 BluetoothProfile.STATE_CONNECTING);
434                    }
435                    synchronized (HeadsetStateMachine.this) {
436                        mTargetDevice = null;
437                        transitionTo(mConnected);
438                    }
439                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
440                    broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
441                                             BluetoothProfile.STATE_CONNECTING);
442                    synchronized (HeadsetStateMachine.this) {
443                        mCurrentDevice = mTargetDevice;
444                        mTargetDevice = null;
445                        transitionTo(mConnected);
446                    }
447                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
448                    broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
449                                             BluetoothProfile.STATE_CONNECTING);
450                    synchronized (HeadsetStateMachine.this) {
451                        mCurrentDevice = mIncomingDevice;
452                        mIncomingDevice = null;
453                        transitionTo(mConnected);
454                    }
455                } else {
456                    Log.e(TAG, "Unknown device Connected: " + device);
457                    // something is wrong here, but sync our state with stack
458                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
459                                             BluetoothProfile.STATE_DISCONNECTED);
460                    synchronized (HeadsetStateMachine.this) {
461                        mCurrentDevice = device;
462                        mTargetDevice = null;
463                        mIncomingDevice = null;
464                        transitionTo(mConnected);
465                    }
466                }
467                break;
468            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
469                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
470                    log("current device tries to connect back");
471                    // TODO(BT) ignore or reject
472                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
473                    // The stack is connecting to target device or
474                    // there is an incoming connection from the target device at the same time
475                    // we already broadcasted the intent, doing nothing here
476                    if (DBG) {
477                        log("Stack and target device are connecting");
478                    }
479                }
480                else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
481                    Log.e(TAG, "Another connecting event on the incoming device");
482                } else {
483                    // We get an incoming connecting request while Pending
484                    // TODO(BT) is stack handing this case? let's ignore it for now
485                    log("Incoming connection while pending, ignore");
486                }
487                break;
488            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
489                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
490                    // we already broadcasted the intent, doing nothing here
491                    if (DBG) {
492                        log("stack is disconnecting mCurrentDevice");
493                    }
494                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
495                    Log.e(TAG, "TargetDevice is getting disconnected");
496                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
497                    Log.e(TAG, "IncomingDevice is getting disconnected");
498                } else {
499                    Log.e(TAG, "Disconnecting unknow device: " + device);
500                }
501                break;
502            default:
503                Log.e(TAG, "Incorrect state: " + state);
504                break;
505            }
506        }
507
508    }
509
510    private class Connected extends State {
511        @Override
512        public void enter() {
513            log("Enter Connected: " + getCurrentMessage().what);
514        }
515
516        @Override
517        public boolean processMessage(Message message) {
518            log("Connected process message: " + message.what);
519            if (DBG) {
520                if (mCurrentDevice == null) {
521                    log("ERROR: mCurrentDevice is null in Connected");
522                    return NOT_HANDLED;
523                }
524            }
525
526            boolean retValue = HANDLED;
527            switch(message.what) {
528                case CONNECT:
529                {
530                    BluetoothDevice device = (BluetoothDevice) message.obj;
531                    if (mCurrentDevice.equals(device)) {
532                        break;
533                    }
534
535                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
536                                   BluetoothProfile.STATE_DISCONNECTED);
537                    if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) {
538                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
539                                       BluetoothProfile.STATE_CONNECTING);
540                        break;
541                    }
542
543                    synchronized (HeadsetStateMachine.this) {
544                        mTargetDevice = device;
545                        transitionTo(mPending);
546                    }
547                }
548                    break;
549                case DISCONNECT:
550                {
551                    BluetoothDevice device = (BluetoothDevice) message.obj;
552                    if (!mCurrentDevice.equals(device)) {
553                        break;
554                    }
555                    broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
556                                   BluetoothProfile.STATE_CONNECTED);
557                    if (!disconnectHfpNative(getByteAddress(device))) {
558                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
559                                       BluetoothProfile.STATE_DISCONNECTED);
560                        break;
561                    }
562                    transitionTo(mPending);
563                }
564                    break;
565                case CONNECT_AUDIO:
566                    // TODO(BT) when failure, broadcast audio connecting to disconnected intent
567                    //          check if device matches mCurrentDevice
568                    connectAudioNative(getByteAddress(mCurrentDevice));
569                    break;
570                case VOICE_RECOGNITION_START:
571                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
572                    break;
573                case VOICE_RECOGNITION_STOP:
574                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
575                    break;
576                case CALL_STATE_CHANGED:
577                    processCallState((HeadsetCallState) message.obj);
578                    break;
579                case INTENT_BATTERY_CHANGED:
580                    processIntentBatteryChanged((Intent) message.obj);
581                    break;
582                case ROAM_CHANGED:
583                    processRoamChanged((Boolean) message.obj);
584                    break;
585                case DEVICE_STATE_CHANGED:
586                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
587                    break;
588                case SEND_CCLC_RESPONSE:
589                    processSendClccResponse((HeadsetClccResponse) message.obj);
590                    break;
591                case DIALING_OUT_TIMEOUT:
592                    if (mDialingOut) {
593                        mDialingOut= false;
594                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
595                    }
596                    break;
597                case START_VR_TIMEOUT:
598                    if (mWaitingForVoiceRecognition) {
599                        mWaitingForVoiceRecognition = false;
600                        Log.e(TAG, "Timeout waiting for voice recognition to start");
601                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
602                    }
603                    break;
604                case STACK_EVENT:
605                    StackEvent event = (StackEvent) message.obj;
606                    if (DBG) {
607                        log("event type: " + event.type);
608                    }
609                    switch (event.type) {
610                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
611                            processConnectionEvent(event.valueInt, event.device);
612                            break;
613                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
614                            processAudioEvent(event.valueInt, event.device);
615                            break;
616                        case EVENT_TYPE_VR_STATE_CHANGED:
617                            processVrEvent(event.valueInt);
618                            break;
619                        case EVENT_TYPE_ANSWER_CALL:
620                            // TODO(BT) could answer call happen on Connected state?
621                            processAnswerCall();
622                            break;
623                        case EVENT_TYPE_HANGUP_CALL:
624                            // TODO(BT) could hangup call happen on Connected state?
625                            processHangupCall();
626                            break;
627                        case EVENT_TYPE_VOLUME_CHANGED:
628                            processVolumeEvent(event.valueInt, event.valueInt2);
629                            break;
630                        case EVENT_TYPE_DIAL_CALL:
631                            processDialCall(event.valueString);
632                            break;
633                        case EVENT_TYPE_SEND_DTMF:
634                            processSendDtmf(event.valueInt);
635                            break;
636                        case EVENT_TYPE_AT_CHLD:
637                            processAtChld(event.valueInt);
638                            break;
639                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
640                            processSubscriberNumberRequest();
641                            break;
642                        case EVENT_TYPE_AT_CIND:
643                            processAtCind();
644                            break;
645                        case EVENT_TYPE_AT_COPS:
646                            processAtCops();
647                            break;
648                        case EVENT_TYPE_AT_CLCC:
649                            processAtClcc();
650                            break;
651                        case EVENT_TYPE_UNKNOWN_AT:
652                            processUnknownAt(event.valueString);
653                            break;
654                        case EVENT_TYPE_KEY_PRESSED:
655                            processKeyPressed();
656                            break;
657                        default:
658                            Log.e(TAG, "Unknown stack event: " + event.type);
659                            break;
660                    }
661                    break;
662                default:
663                    return NOT_HANDLED;
664            }
665            return retValue;
666        }
667
668        // in Connected state
669        private void processConnectionEvent(int state, BluetoothDevice device) {
670            switch (state) {
671                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
672                    if (mCurrentDevice.equals(device)) {
673                        broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
674                                                 BluetoothProfile.STATE_CONNECTED);
675                        synchronized (HeadsetStateMachine.this) {
676                            mCurrentDevice = null;
677                            transitionTo(mDisconnected);
678                        }
679                    } else {
680                        Log.e(TAG, "Disconnected from unknown device: " + device);
681                    }
682                    break;
683                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
684                    processSlcConnected();
685                    break;
686              default:
687                  Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
688                  break;
689            }
690        }
691
692        // in Connected state
693        private void processAudioEvent(int state, BluetoothDevice device) {
694            if (!mCurrentDevice.equals(device)) {
695                Log.e(TAG, "Audio changed on disconnected device: " + device);
696                return;
697            }
698
699            switch (state) {
700                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
701                    // TODO(BT) should I save the state for next broadcast as the prevState?
702                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
703                    mAudioManager.setBluetoothScoOn(true);
704                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
705                                        BluetoothHeadset.STATE_AUDIO_CONNECTING);
706                    transitionTo(mAudioOn);
707                    break;
708                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
709                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
710                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
711                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
712                    break;
713                    // TODO(BT) process other states
714                default:
715                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
716                    break;
717            }
718        }
719
720        private void processSlcConnected() {
721            if (mPhoneProxy != null) {
722                try {
723                    mPhoneProxy.queryPhoneState();
724                } catch (RemoteException e) {
725                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
726                }
727            } else {
728                Log.e(TAG, "Handsfree phone proxy null for query phone state");
729            }
730
731        }
732    }
733
734    private class AudioOn extends State {
735        // Audio parameters
736        private static final String HEADSET_NAME = "bt_headset_name";
737        private static final String HEADSET_NREC = "bt_headset_nrec";
738
739        @Override
740        public void enter() {
741            log("Enter AudioOn: " + getCurrentMessage().what);
742            mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" +
743                                        HEADSET_NREC + "=on");
744        }
745
746        @Override
747        public boolean processMessage(Message message) {
748            log("AudioOn process message: " + message.what);
749            if (DBG) {
750                if (mCurrentDevice == null) {
751                    log("ERROR: mCurrentDevice is null in AudioOn");
752                    return NOT_HANDLED;
753                }
754            }
755
756            boolean retValue = HANDLED;
757            switch(message.what) {
758                case DISCONNECT:
759                {
760                    BluetoothDevice device = (BluetoothDevice) message.obj;
761                    if (!mCurrentDevice.equals(device)) {
762                        break;
763                    }
764                    deferMessage(obtainMessage(DISCONNECT, message.obj));
765                }
766                // fall through
767                case DISCONNECT_AUDIO:
768                    disconnectAudioNative(getByteAddress(mCurrentDevice));
769                    break;
770                case VOICE_RECOGNITION_START:
771                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
772                    break;
773                case VOICE_RECOGNITION_STOP:
774                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
775                    break;
776                case INTENT_SCO_VOLUME_CHANGED:
777                    processIntentScoVolume((Intent) message.obj);
778                    break;
779                case CALL_STATE_CHANGED:
780                    processCallState((HeadsetCallState) message.obj);
781                    break;
782                case INTENT_BATTERY_CHANGED:
783                    processIntentBatteryChanged((Intent) message.obj);
784                    break;
785                case ROAM_CHANGED:
786                    processRoamChanged((Boolean) message.obj);
787                    break;
788                case DEVICE_STATE_CHANGED:
789                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
790                    break;
791                case SEND_CCLC_RESPONSE:
792                    processSendClccResponse((HeadsetClccResponse) message.obj);
793                    break;
794                case DIALING_OUT_TIMEOUT:
795                    if (mDialingOut) {
796                        mDialingOut= false;
797                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
798                    }
799                    break;
800                case START_VR_TIMEOUT:
801                    if (mWaitingForVoiceRecognition) {
802                        mWaitingForVoiceRecognition = false;
803                        Log.e(TAG, "Timeout waiting for voice recognition to start");
804                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
805                    }
806                    break;
807                case STACK_EVENT:
808                    StackEvent event = (StackEvent) message.obj;
809                    if (DBG) {
810                        log("event type: " + event.type);
811                    }
812                    switch (event.type) {
813                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
814                            processAudioEvent(event.valueInt, event.device);
815                            break;
816                        case EVENT_TYPE_VR_STATE_CHANGED:
817                            processVrEvent(event.valueInt);
818                            break;
819                        case EVENT_TYPE_ANSWER_CALL:
820                            processAnswerCall();
821                            break;
822                        case EVENT_TYPE_HANGUP_CALL:
823                            processHangupCall();
824                            break;
825                        case EVENT_TYPE_VOLUME_CHANGED:
826                            processVolumeEvent(event.valueInt, event.valueInt2);
827                            break;
828                        case EVENT_TYPE_DIAL_CALL:
829                            processDialCall(event.valueString);
830                            break;
831                        case EVENT_TYPE_SEND_DTMF:
832                            processSendDtmf(event.valueInt);
833                            break;
834                        case EVENT_TYPE_NOICE_REDUCTION:
835                            processNoiceReductionEvent(event.valueInt);
836                            break;
837                        case EVENT_TYPE_AT_CHLD:
838                            processAtChld(event.valueInt);
839                            break;
840                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
841                            processSubscriberNumberRequest();
842                            break;
843                        case EVENT_TYPE_AT_CIND:
844                            processAtCind();
845                            break;
846                        case EVENT_TYPE_AT_COPS:
847                            processAtCops();
848                            break;
849                        case EVENT_TYPE_AT_CLCC:
850                            processAtClcc();
851                            break;
852                        case EVENT_TYPE_UNKNOWN_AT:
853                            processUnknownAt(event.valueString);
854                            break;
855                        case EVENT_TYPE_KEY_PRESSED:
856                            processKeyPressed();
857                            break;
858                        default:
859                            Log.e(TAG, "Unknown stack event: " + event.type);
860                            break;
861                    }
862                    break;
863                default:
864                    return NOT_HANDLED;
865            }
866            return retValue;
867        }
868
869        // in AudioOn state
870        private void processAudioEvent(int state, BluetoothDevice device) {
871            if (!mCurrentDevice.equals(device)) {
872                Log.e(TAG, "Audio changed on disconnected device: " + device);
873                return;
874            }
875
876            switch (state) {
877                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
878                    mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
879                    mAudioManager.setBluetoothScoOn(false);
880                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
881                                        BluetoothHeadset.STATE_AUDIO_CONNECTED);
882                    transitionTo(mConnected);
883                    break;
884                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
885                    // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
886                    //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
887                    //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
888                    break;
889                default:
890                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
891                    break;
892            }
893        }
894
895        // enable 1 enable noice reduction
896        //        0 disable noice reduction
897        private void processNoiceReductionEvent(int enable) {
898            if (enable == 1) {
899                mAudioManager.setParameters(HEADSET_NREC + "=on");
900            } else {
901                mAudioManager.setParameters(HEADSET_NREC + "off");
902            }
903        }
904
905        private void processIntentScoVolume(Intent intent) {
906            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
907            if (mPhoneState.getSpeakerVolume() != volumeValue) {
908                mPhoneState.setSpeakerVolume(volumeValue);
909                setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue);
910            }
911        }
912    }
913
914    private ServiceConnection mConnection = new ServiceConnection() {
915        public void onServiceConnected(ComponentName className, IBinder service) {
916            if (DBG) Log.d(TAG, "Proxy object connected");
917            mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
918        }
919
920        public void onServiceDisconnected(ComponentName className) {
921            if (DBG) Log.d(TAG, "Proxy object disconnected");
922            mPhoneProxy = null;
923        }
924    };
925
926    // HFP Connection state of the device could be changed by the state machine
927    // in separate thread while this method is executing.
928    int getConnectionState(BluetoothDevice device) {
929        if (getCurrentState() == mDisconnected) {
930            return BluetoothProfile.STATE_DISCONNECTED;
931        }
932
933        synchronized (this) {
934            IState currentState = getCurrentState();
935            if (currentState == mPending) {
936                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
937                    return BluetoothProfile.STATE_CONNECTING;
938                }
939                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
940                    return BluetoothProfile.STATE_DISCONNECTING;
941                }
942                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
943                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
944                }
945                return BluetoothProfile.STATE_DISCONNECTED;
946            }
947
948            if (currentState == mConnected || currentState == mAudioOn) {
949                if (mCurrentDevice.equals(device)) {
950                    return BluetoothProfile.STATE_CONNECTED;
951                }
952                return BluetoothProfile.STATE_DISCONNECTED;
953            } else {
954                Log.e(TAG, "Bad currentState: " + currentState);
955                return BluetoothProfile.STATE_DISCONNECTED;
956            }
957        }
958    }
959
960    List<BluetoothDevice> getConnectedDevices() {
961        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
962        synchronized(this) {
963            if (isConnected()) {
964                devices.add(mCurrentDevice);
965            }
966        }
967        return devices;
968    }
969
970    boolean isAudioOn() {
971        return (getCurrentState() == mAudioOn);
972    }
973
974    boolean isAudioConnected(BluetoothDevice device) {
975        synchronized(this) {
976            if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)) {
977                return true;
978            }
979        }
980        return false;
981    }
982
983    int getAudioState(BluetoothDevice device) {
984        synchronized(this) {
985            if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
986                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
987            }
988        }
989        return mAudioState;
990    }
991
992    private void processVrEvent(int state) {
993        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " +
994            mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition +
995            " isInCall: " + isInCall());
996        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
997            // TODO(BT) handle virtualcall
998            if (!mVoiceRecognitionStarted &&
999                !isInCall())
1000            {
1001                try {
1002                    mContext.startActivity(sVoiceCommandIntent);
1003                } catch (ActivityNotFoundException e) {
1004                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1005                    return;
1006                }
1007                expectVoiceRecognition();
1008            }
1009        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
1010            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
1011            {
1012                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
1013                mVoiceRecognitionStarted = false;
1014                mWaitingForVoiceRecognition = false;
1015                if (!isInCall())
1016                    disconnectAudioNative(getByteAddress(mCurrentDevice));
1017            }
1018            else
1019            {
1020                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1021            }
1022        } else {
1023            Log.e(TAG, "Bad Voice Recognition state: " + state);
1024        }
1025    }
1026
1027    private void processLocalVrEvent(int state)
1028    {
1029        if (state == HeadsetHalConstants.VR_STATE_STARTED)
1030        {
1031            boolean needAudio = true;
1032            if (mVoiceRecognitionStarted || isInCall())
1033            {
1034                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() +
1035                    " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
1036                return;
1037            }
1038            mVoiceRecognitionStarted = true;
1039
1040            if (mWaitingForVoiceRecognition)
1041            {
1042                Log.d(TAG, "Voice recognition started successfully");
1043                mWaitingForVoiceRecognition = false;
1044                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
1045                removeMessages(START_VR_TIMEOUT);
1046            }
1047            else
1048            {
1049                Log.d(TAG, "Voice recognition started locally");
1050                needAudio = startVoiceRecognitionNative();
1051            }
1052
1053            if (needAudio && !isAudioOn())
1054            {
1055                Log.d(TAG, "Initiating audio connection for Voice Recognition");
1056                connectAudioNative(getByteAddress(mCurrentDevice));
1057            }
1058
1059            if (mStartVoiceRecognitionWakeLock.isHeld()) {
1060                mStartVoiceRecognitionWakeLock.release();
1061            }
1062        }
1063        else
1064        {
1065            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted +
1066                " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
1067            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
1068            {
1069                mVoiceRecognitionStarted = false;
1070                mWaitingForVoiceRecognition = false;
1071
1072                if (stopVoiceRecognitionNative() && !isInCall())
1073                    disconnectAudioNative(getByteAddress(mCurrentDevice));
1074            }
1075        }
1076    }
1077
1078    private synchronized void expectVoiceRecognition() {
1079        mWaitingForVoiceRecognition = true;
1080        sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE);
1081        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
1082            mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
1083        }
1084    }
1085
1086    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1087        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
1088        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
1089        int connectionState;
1090        synchronized (this) {
1091            for (BluetoothDevice device : bondedDevices) {
1092                ParcelUuid[] featureUuids = device.getUuids();
1093                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
1094                    continue;
1095                }
1096                connectionState = getConnectionState(device);
1097                for(int i = 0; i < states.length; i++) {
1098                    if (connectionState == states[i]) {
1099                        deviceList.add(device);
1100                    }
1101                }
1102            }
1103        }
1104        return deviceList;
1105    }
1106
1107    // This method does not check for error conditon (newState == prevState)
1108    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
1109        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
1110        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1111        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1112        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1113        mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
1114        if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState);
1115        try {
1116            mAdapterService.sendConnectionStateChange(device, BluetoothProfile.HEADSET, newState,
1117                                                      prevState);
1118        } catch (RemoteException e) {
1119            Log.e(TAG, Log.getStackTraceString(new Throwable()));
1120        }
1121    }
1122
1123    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
1124        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
1125        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1126        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1127        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1128        mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
1129        if (DBG) log("Audio state " + device + ": " + prevState + "->" + newState);
1130    }
1131
1132    private void processAnswerCall() {
1133        if (mPhoneProxy != null) {
1134            try {
1135                mPhoneProxy.answerCall();
1136            } catch (RemoteException e) {
1137                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1138            }
1139        } else {
1140            Log.e(TAG, "Handsfree phone proxy null for answering call");
1141        }
1142    }
1143
1144    private void processHangupCall() {
1145        if (mPhoneProxy != null) {
1146            try {
1147                mPhoneProxy.hangupCall();
1148            } catch (RemoteException e) {
1149                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1150            }
1151        } else {
1152            Log.e(TAG, "Handsfree phone proxy null for hanging up call");
1153        }
1154    }
1155
1156    private void processDialCall(String number) {
1157        String dialNumber;
1158        if ((number == null) || (number.length() == 0)) {
1159            dialNumber = mPhonebook.getLastDialledNumber();
1160            if (dialNumber == null) {
1161                if (DBG) log("processDialCall, last dial number null");
1162                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1163                return;
1164            }
1165        } else if (number.charAt(0) == '>') {
1166            // Yuck - memory dialling requested.
1167            // Just dial last number for now
1168            if (number.startsWith(">9999")) {   // for PTS test
1169                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1170                return;
1171            }
1172            if (DBG) log("processDialCall, memory dial do last dial for now");
1173            dialNumber = mPhonebook.getLastDialledNumber();
1174            if (dialNumber == null) {
1175                if (DBG) log("processDialCall, last dial number null");
1176                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1177                return;
1178            }
1179        } else {
1180            // Remove trailing ';'
1181            if (number.charAt(number.length() - 1) == ';') {
1182                number = number.substring(0, number.length() - 1);
1183            }
1184
1185            dialNumber = PhoneNumberUtils.convertPreDial(number);
1186        }
1187        // TODO(BT) do we need to terminate virtual call first
1188        //          like call terminateScoUsingVirtualVoiceCall()?
1189        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1190                                   Uri.fromParts(SCHEME_TEL, dialNumber, null));
1191        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1192        mContext.startActivity(intent);
1193        // TODO(BT) continue send OK reults code after call starts
1194        //          hold wait lock, start a timer, set wait call flag
1195        //          Get call started indication from bluetooth phone
1196        mDialingOut = true;
1197        sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE);
1198    }
1199
1200    private void processVolumeEvent(int volumeType, int volume) {
1201        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
1202            mPhoneState.setSpeakerVolume(volume);
1203            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
1204            mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
1205        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
1206            mPhoneState.setMicVolume(volume);
1207        } else {
1208            Log.e(TAG, "Bad voluem type: " + volumeType);
1209        }
1210    }
1211
1212    private void processSendDtmf(int dtmf) {
1213        if (mPhoneProxy != null) {
1214            try {
1215                mPhoneProxy.sendDtmf(dtmf);
1216            } catch (RemoteException e) {
1217                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1218            }
1219        } else {
1220            Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
1221        }
1222    }
1223
1224    private void processCallState(HeadsetCallState callState) {
1225        mPhoneState.setNumActiveCall(callState.mNumActive);
1226        mPhoneState.setNumHeldCall(callState.mNumHeld);
1227        mPhoneState.setCallState(callState.mCallState);
1228        if (mDialingOut && callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) {
1229                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
1230                removeMessages(DIALING_OUT_TIMEOUT);
1231                mDialingOut = false;
1232        }
1233        log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld +
1234            " mCallState: " + callState.mCallState);
1235        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
1236        if (getCurrentState() != mDisconnected) {
1237            phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, callState.mCallState,
1238                                   callState.mNumber, callState.mType);
1239        }
1240    }
1241
1242    private void processAtChld(int chld) {
1243        if (mPhoneProxy != null) {
1244            try {
1245                if (mPhoneProxy.processChld(chld)) {
1246                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
1247                } else {
1248                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1249                }
1250            } catch (RemoteException e) {
1251                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1252                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1253            }
1254        } else {
1255            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
1256            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1257        }
1258    }
1259
1260    private void processSubscriberNumberRequest() {
1261        if (mPhoneProxy != null) {
1262            try {
1263                String number = mPhoneProxy.getSubscriberNumber();
1264                if (number != null) {
1265                    atResponseStringNative("+CNUM: ,\"" + number + "\"," +
1266                                           PhoneNumberUtils.toaFromString(number) + ",,4");
1267                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
1268                }
1269            } catch (RemoteException e) {
1270                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1271                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1272            }
1273        } else {
1274            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
1275        }
1276    }
1277
1278    private void processAtCind() {
1279        cindResponseNative(mPhoneState.getService(), mPhoneState.getNumActiveCall(),
1280                           mPhoneState.getNumHeldCall(), mPhoneState.getCallState(),
1281                           mPhoneState.getSignal(), mPhoneState.getRoam(),
1282                           mPhoneState.getBatteryCharge());
1283    }
1284
1285    private void processAtCops() {
1286        if (mPhoneProxy != null) {
1287            try {
1288                String operatorName = mPhoneProxy.getNetworkOperator();
1289                if (operatorName == null) {
1290                    operatorName = "";
1291                }
1292                copsResponseNative(operatorName);
1293            } catch (RemoteException e) {
1294                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1295                copsResponseNative("");
1296            }
1297        } else {
1298            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
1299            copsResponseNative("");
1300        }
1301    }
1302
1303    private void processAtClcc() {
1304        if (mPhoneProxy != null) {
1305            try {
1306                if (!mPhoneProxy.listCurrentCalls()) {
1307                    clccResponseNative(0, 0, 0, 0, false, "", 0);
1308                }
1309            } catch (RemoteException e) {
1310                Log.e(TAG, Log.getStackTraceString(new Throwable()));
1311                clccResponseNative(0, 0, 0, 0, false, "", 0);
1312            }
1313        } else {
1314            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
1315            clccResponseNative(0, 0, 0, 0, false, "", 0);
1316        }
1317    }
1318
1319    private void processUnknownAt(String atString) {
1320        // TODO (BT)
1321        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
1322    }
1323
1324    private void processKeyPressed() {
1325        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
1326            if (mPhoneProxy != null) {
1327                try {
1328                    mPhoneProxy.answerCall();
1329                } catch (RemoteException e) {
1330                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1331                }
1332            } else {
1333                Log.e(TAG, "Handsfree phone proxy null for answering call");
1334            }
1335        } else if (mPhoneState.getNumActiveCall() > 0) {
1336            if (!isAudioOn())
1337            {
1338                connectAudioNative(getByteAddress(mCurrentDevice));
1339            }
1340            else
1341            {
1342                if (mPhoneProxy != null) {
1343                    try {
1344                        mPhoneProxy.hangupCall();
1345                    } catch (RemoteException e) {
1346                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
1347                    }
1348                } else {
1349                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
1350                }
1351            }
1352        } else {
1353            String dialNumber = mPhonebook.getLastDialledNumber();
1354            if (dialNumber == null) {
1355                if (DBG) log("processKeyPressed, last dial number null");
1356                return;
1357            }
1358            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1359                                       Uri.fromParts(SCHEME_TEL, dialNumber, null));
1360            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1361            mContext.startActivity(intent);
1362        }
1363    }
1364
1365    private void onConnectionStateChanged(int state, byte[] address) {
1366        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
1367        event.valueInt = state;
1368        event.device = getDevice(address);
1369        sendMessage(STACK_EVENT, event);
1370    }
1371
1372    private void onAudioStateChanged(int state, byte[] address) {
1373        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
1374        event.valueInt = state;
1375        event.device = getDevice(address);
1376        sendMessage(STACK_EVENT, event);
1377    }
1378
1379    private void onVrStateChanged(int state) {
1380        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
1381        event.valueInt = state;
1382        sendMessage(STACK_EVENT, event);
1383    }
1384
1385    private void onAnswerCall() {
1386        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
1387        sendMessage(STACK_EVENT, event);
1388    }
1389
1390    private void onHangupCall() {
1391        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
1392        sendMessage(STACK_EVENT, event);
1393    }
1394
1395    private void onVolumeChanged(int type, int volume) {
1396        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
1397        event.valueInt = type;
1398        event.valueInt2 = volume;
1399        sendMessage(STACK_EVENT, event);
1400    }
1401
1402    private void onDialCall(String number) {
1403        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
1404        event.valueString = number;
1405        sendMessage(STACK_EVENT, event);
1406    }
1407
1408    private void onSendDtmf(int dtmf) {
1409        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
1410        event.valueInt = dtmf;
1411        sendMessage(STACK_EVENT, event);
1412    }
1413
1414    private void onNoiceReductionEnable(boolean enable) {
1415        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
1416        event.valueInt = enable ? 1 : 0;
1417        sendMessage(STACK_EVENT, event);
1418    }
1419
1420    private void onAtChld(int chld) {
1421        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
1422        event.valueInt = chld;
1423        sendMessage(STACK_EVENT, event);
1424    }
1425
1426    private void onAtCnum() {
1427        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
1428        sendMessage(STACK_EVENT, event);
1429    }
1430
1431    private void onAtCind() {
1432        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
1433        sendMessage(STACK_EVENT, event);
1434    }
1435
1436    private void onAtCops() {
1437        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
1438        sendMessage(STACK_EVENT, event);
1439    }
1440
1441    private void onAtClcc() {
1442        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
1443        sendMessage(STACK_EVENT, event);
1444    }
1445
1446    private void onUnknownAt(String atString) {
1447        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
1448        event.valueString = atString;
1449        sendMessage(STACK_EVENT, event);
1450    }
1451
1452    private void onKeyPressed() {
1453        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
1454        sendMessage(STACK_EVENT, event);
1455    }
1456
1457    private void processIntentBatteryChanged(Intent intent) {
1458        int batteryLevel = intent.getIntExtra("level", -1);
1459        int scale = intent.getIntExtra("scale", -1);
1460        if (batteryLevel == -1 || scale == -1 || scale == 0) {
1461            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
1462            return;
1463        }
1464        batteryLevel = batteryLevel * 5 / scale;
1465        mPhoneState.setBatteryCharge(batteryLevel);
1466    }
1467
1468    private void processRoamChanged(boolean roam) {
1469        mPhoneState.setRoam(roam ? HeadsetHalConstants.SERVICE_TYPE_ROAMING :
1470                            HeadsetHalConstants.SERVICE_TYPE_HOME);
1471    }
1472
1473    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
1474        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
1475                                 deviceState.mBatteryCharge);
1476    }
1477
1478    private void processSendClccResponse(HeadsetClccResponse clcc) {
1479        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
1480                           clcc.mNumber, clcc.mType);
1481    }
1482
1483    private String getCurrentDeviceName() {
1484        String defaultName = "<unknown>";
1485        if (mCurrentDevice == null) {
1486            return defaultName;
1487        }
1488        String deviceName = mCurrentDevice.getName();
1489        if (deviceName == null) {
1490            return defaultName;
1491        }
1492        return deviceName;
1493    }
1494
1495    private byte[] getByteAddress(BluetoothDevice device) {
1496        return Utils.getBytesFromAddress(device.getAddress());
1497    }
1498
1499    private BluetoothDevice getDevice(byte[] address) {
1500        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
1501    }
1502
1503    private boolean isInCall() {
1504        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
1505                (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
1506    }
1507
1508    boolean isConnected() {
1509        IState currentState = getCurrentState();
1510        return (currentState == mConnected || currentState == mAudioOn);
1511    }
1512
1513    private void log(String msg) {
1514        if (DBG) {
1515            Log.d(TAG, msg);
1516        }
1517    }
1518
1519    private static final String SCHEME_TEL = "tel";
1520
1521    // Event types for STACK_EVENT message
1522    final private static int EVENT_TYPE_NONE = 0;
1523    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
1524    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
1525    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
1526    final private static int EVENT_TYPE_ANSWER_CALL = 4;
1527    final private static int EVENT_TYPE_HANGUP_CALL = 5;
1528    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
1529    final private static int EVENT_TYPE_DIAL_CALL = 7;
1530    final private static int EVENT_TYPE_SEND_DTMF = 8;
1531    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
1532    final private static int EVENT_TYPE_AT_CHLD = 10;
1533    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
1534    final private static int EVENT_TYPE_AT_CIND = 12;
1535    final private static int EVENT_TYPE_AT_COPS = 13;
1536    final private static int EVENT_TYPE_AT_CLCC = 14;
1537    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
1538    final private static int EVENT_TYPE_KEY_PRESSED = 16;
1539
1540    private class StackEvent {
1541        int type = EVENT_TYPE_NONE;
1542        int valueInt = 0;
1543        int valueInt2 = 0;
1544        String valueString = null;
1545        BluetoothDevice device = null;
1546
1547        private StackEvent(int type) {
1548            this.type = type;
1549        }
1550    }
1551
1552    private native static void classInitNative();
1553    private native void initializeNative();
1554    private native void cleanupNative();
1555    private native boolean connectHfpNative(byte[] address);
1556    private native boolean disconnectHfpNative(byte[] address);
1557    private native boolean connectAudioNative(byte[] address);
1558    private native boolean disconnectAudioNative(byte[] address);
1559    private native boolean startVoiceRecognitionNative();
1560    private native boolean stopVoiceRecognitionNative();
1561    private native boolean setVolumeNative(int volumeType, int volume);
1562    private native boolean cindResponseNative(int service, int numActive, int numHeld,
1563                                              int callState, int signal, int roam,
1564                                              int batteryCharge);
1565    private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
1566                                                    int batteryCharge);
1567    private native boolean atResponseCodeNative(int responseCode);
1568    private native boolean clccResponseNative(int index, int dir, int status, int mode,
1569                                              boolean mpty, String number, int type);
1570    private native boolean copsResponseNative(String operatorName);
1571    private native boolean atResponseStringNative(String responseString);
1572    private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
1573                                                  String number, int type);
1574}
1575