HeadsetStateMachine.java revision 1f7304a0df9fa7e1ccee0cd9a61933a0f9be3e8c
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
17package com.android.bluetooth.hfp;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothAssignedNumbers;
21import android.bluetooth.BluetoothDevice;
22import android.bluetooth.BluetoothHeadset;
23import android.bluetooth.BluetoothProfile;
24import android.bluetooth.BluetoothUuid;
25import android.content.ActivityNotFoundException;
26import android.content.Context;
27import android.content.Intent;
28import android.media.AudioManager;
29import android.net.Uri;
30import android.os.IDeviceIdleController;
31import android.os.Looper;
32import android.os.Message;
33import android.os.ParcelUuid;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.os.UserHandle;
37import android.support.annotation.VisibleForTesting;
38import android.telephony.PhoneNumberUtils;
39import android.util.Log;
40
41import com.android.bluetooth.btservice.AdapterService;
42import com.android.bluetooth.btservice.ProfileService;
43import com.android.internal.util.State;
44import com.android.internal.util.StateMachine;
45
46import java.io.FileDescriptor;
47import java.io.PrintWriter;
48import java.io.StringWriter;
49import java.util.ArrayList;
50import java.util.HashMap;
51import java.util.List;
52import java.util.Map;
53import java.util.Set;
54
55/**
56 * Bluetooth Handset StateMachine
57 *                        (Disconnected)
58 *                           |      ^
59 *                   CONNECT |      | DISCONNECTED
60 *                           V      |
61 *                  (Connecting)   (Disconnecting)
62 *                           |      ^
63 *                 CONNECTED |      | DISCONNECT
64 *                           V      |
65 *                          (Connected)
66 *                           |      ^
67 *             CONNECT_AUDIO |      | AUDIO_DISCONNECTED
68 *                           V      |
69 *             (AudioConnecting)   (AudioDiconnecting)
70 *                           |      ^
71 *           AUDIO_CONNECTED |      | DISCONNECT_AUDIO
72 *                           V      |
73 *                           (AudioOn)
74 */
75final class HeadsetStateMachine extends StateMachine {
76    private static final String TAG = "HeadsetStateMachine";
77    private static final boolean DBG = false;
78
79    private static final String HEADSET_NAME = "bt_headset_name";
80    private static final String HEADSET_NREC = "bt_headset_nrec";
81    private static final String HEADSET_WBS = "bt_wbs";
82
83    /* Telephone URI scheme */
84    private static final String SCHEME_TEL = "tel";
85
86    static final int CONNECT = 1;
87    static final int DISCONNECT = 2;
88    static final int CONNECT_AUDIO = 3;
89    static final int DISCONNECT_AUDIO = 4;
90    static final int VOICE_RECOGNITION_START = 5;
91    static final int VOICE_RECOGNITION_STOP = 6;
92
93    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
94    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
95    static final int INTENT_SCO_VOLUME_CHANGED = 7;
96    static final int INTENT_CONNECTION_ACCESS_REPLY = 8;
97    static final int CALL_STATE_CHANGED = 9;
98    static final int DEVICE_STATE_CHANGED = 11;
99    static final int SEND_CCLC_RESPONSE = 12;
100    static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
101
102    static final int VIRTUAL_CALL_START = 14;
103    static final int VIRTUAL_CALL_STOP = 15;
104
105    static final int STACK_EVENT = 101;
106    private static final int DIALING_OUT_TIMEOUT = 102;
107    private static final int START_VR_TIMEOUT = 103;
108    private static final int CLCC_RSP_TIMEOUT = 104;
109
110    private static final int CONNECT_TIMEOUT = 201;
111
112    private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
113    private static final int START_VR_TIMEOUT_VALUE = 5000;
114    private static final int CLCC_RSP_TIMEOUT_VALUE = 5000;
115    // NOTE: the value is not "final" - it is modified in the unit tests
116    @VisibleForTesting static int sConnectTimeoutMillis = 30000;
117
118    private BluetoothDevice mCurrentDevice;
119
120    // State machine states
121    private final Disconnected mDisconnected = new Disconnected();
122    private final Connecting mConnecting = new Connecting();
123    private final Disconnecting mDisconnecting = new Disconnecting();
124    private final Connected mConnected = new Connected();
125    private final AudioOn mAudioOn = new AudioOn();
126    private final AudioConnecting mAudioConnecting = new AudioConnecting();
127    private final AudioDisconnecting mAudioDisconnecting = new AudioDisconnecting();
128    private HeadsetStateBase mPrevState;
129
130    // Run time dependencies
131    private final HeadsetService mService;
132    private final HeadsetNativeInterface mNativeInterface;
133    private final HeadsetSystemInterface mSystemInterface;
134    private final BluetoothAdapter mAdapter;
135
136    // Runtime states
137    private boolean mVirtualCallStarted;
138    private boolean mVoiceRecognitionStarted;
139    private boolean mWaitingForVoiceRecognition;
140    private boolean mDialingOut;
141    private int mSpeakerVolume;
142    private int mMicVolume;
143    // Indicates whether audio can be routed to the device.
144    private boolean mAudioRouteAllowed = true;
145    // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
146    private boolean mForceScoAudio;
147    // Audio Parameters like NREC
148    private final HashMap<String, Integer> mAudioParams = new HashMap<>();
149    // AT Phone book keeps a group of states used by AT+CPBR commands
150    private final AtPhonebook mPhonebook;
151
152    private static final ParcelUuid[] HEADSET_UUIDS = {
153            BluetoothUuid.HSP, BluetoothUuid.Handsfree,
154    };
155    // Keys are AT commands, and values are the company IDs.
156    private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
157    // Intent that get sent during voice recognition events.
158    private static final Intent VOICE_COMMAND_INTENT;
159
160    static {
161        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<>();
162        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
163                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT,
164                BluetoothAssignedNumbers.PLANTRONICS);
165        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
166                BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID,
167                BluetoothAssignedNumbers.GOOGLE);
168        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
169                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL,
170                BluetoothAssignedNumbers.APPLE);
171        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put(
172                BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV,
173                BluetoothAssignedNumbers.APPLE);
174        VOICE_COMMAND_INTENT = new Intent(Intent.ACTION_VOICE_COMMAND);
175        VOICE_COMMAND_INTENT.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
176    }
177
178    private HeadsetStateMachine(Looper looper, HeadsetService service,
179            HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) {
180        super(TAG, looper);
181        // Enable/Disable StateMachine debug logs
182        setDbg(DBG);
183        mService = service;
184        mNativeInterface = nativeInterface;
185        mSystemInterface = systemInterface;
186
187        // Connect to system services and construct helper objects
188        mAdapter = BluetoothAdapter.getDefaultAdapter();
189        mPhonebook = new AtPhonebook(mService, mNativeInterface);
190
191        // Initialize state machine
192        addState(mDisconnected);
193        addState(mConnecting);
194        addState(mDisconnecting);
195        addState(mConnected);
196        addState(mAudioOn);
197        addState(mAudioConnecting);
198        addState(mAudioDisconnecting);
199        setInitialState(mDisconnected);
200    }
201
202    static HeadsetStateMachine make(Looper looper, HeadsetService service,
203            HeadsetNativeInterface nativeInterface, HeadsetSystemInterface systemInterface) {
204        Log.i(TAG, "make");
205        HeadsetStateMachine stateMachine =
206                new HeadsetStateMachine(looper, service, nativeInterface, systemInterface);
207        stateMachine.start();
208        return stateMachine;
209    }
210
211    static void destroy(HeadsetStateMachine stateMachine) {
212        Log.i(TAG, "destroy");
213        if (stateMachine == null) {
214            Log.w(TAG, "destroy(), stateMachine is null");
215            return;
216        }
217        stateMachine.quitNow();
218        stateMachine.cleanup();
219    }
220
221    public void cleanup() {
222        if (mPhonebook != null) {
223            mPhonebook.cleanup();
224        }
225        mAudioParams.clear();
226    }
227
228    public void dump(StringBuilder sb) {
229        ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
230        ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
231        ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
232        ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
233        ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio);
234        ProfileService.println(sb, "mDialingOut: " + mDialingOut);
235        ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed);
236        ProfileService.println(sb, "StateMachine: " + this);
237        ProfileService.println(sb, "PreviousState: " + mPrevState);
238        ProfileService.println(sb, "mAudioState: " + getAudioState());
239        // Dump the state machine logs
240        StringWriter stringWriter = new StringWriter();
241        PrintWriter printWriter = new PrintWriter(stringWriter);
242        super.dump(new FileDescriptor(), printWriter, new String[]{});
243        printWriter.flush();
244        stringWriter.flush();
245        ProfileService.println(sb, "StateMachineLog: " + stringWriter.toString());
246    }
247
248    /**
249     * Base class for states used in this state machine to share common infrastructures
250     */
251    private abstract class HeadsetStateBase extends State {
252        @Override
253        public void enter() {
254            // Crash if current device is null and state is not Disconnected
255            if (!(this instanceof Disconnected) && mCurrentDevice == null) {
256                throw new IllegalStateException("mCurrentDevice is null on enter()");
257            }
258            // Crash if mPrevState is null and state is not Disconnected
259            if (!(this instanceof Disconnected) && mPrevState == null) {
260                throw new IllegalStateException("mPrevState is null on enter()");
261            }
262            enforceValidConnectionStateTransition();
263        }
264
265        @Override
266        public void exit() {
267            Message message = getCurrentMessage();
268            if (message != null && !isQuit(message) && mCurrentDevice == null) {
269                throw new IllegalStateException(
270                        "mCurrentDevice is null on exit() to non-quitting state");
271            }
272            mPrevState = this;
273        }
274
275        @Override
276        public String toString() {
277            return getName();
278        }
279
280        /**
281         * Broadcast audio and connection state changes to the system. This should be called at the
282         * end of enter() method after all the setup is done
283         */
284        void broadcastStateTransitions() {
285            if (mPrevState == null || mCurrentDevice == null) {
286                return;
287            }
288            // TODO: Add STATE_AUDIO_DISCONNECTING constant to get rid of the 2nd part of this logic
289            if (getAudioStateInt() != mPrevState.getAudioStateInt() || (
290                    mPrevState instanceof AudioDisconnecting && this instanceof AudioOn)) {
291                stateLogD("audio state changed: " + mCurrentDevice + ": " + mPrevState + " -> "
292                        + this);
293                broadcastAudioState(mCurrentDevice, mPrevState.getAudioStateInt(),
294                        getAudioStateInt());
295            }
296            if (getConnectionStateInt() != mPrevState.getConnectionStateInt()) {
297                stateLogD("connection state changed: " + mCurrentDevice + ": " + mPrevState + " -> "
298                        + this);
299                broadcastConnectionState(mCurrentDevice, mPrevState.getConnectionStateInt(),
300                        getConnectionStateInt());
301            }
302        }
303
304        // Should not be called from enter() method
305        void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
306            stateLogD("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
307            if (fromState == BluetoothProfile.STATE_CONNECTED) {
308                // Headset is disconnecting, stop Virtual call if active.
309                terminateScoUsingVirtualVoiceCall();
310            }
311            mService.connectionStateChanged(device, fromState, toState);
312            Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
313            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
314            intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
315            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
316            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
317            mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
318        }
319
320        // Should not be called from enter() method
321        void broadcastAudioState(BluetoothDevice device, int fromState, int toState) {
322            stateLogD("broadcastAudioState: " + device + ": " + fromState + "->" + toState);
323            if (fromState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
324                // When SCO gets disconnected during call transfer, Virtual call
325                // needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
326                terminateScoUsingVirtualVoiceCall();
327            }
328            Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
329            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
330            intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
331            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
332            mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
333        }
334
335        /**
336         * Verify if the current state transition is legal. This is supposed to be called from
337         * enter() method and crash if the state transition is out of the specification
338         *
339         * Note:
340         * This method uses state objects to verify transition because these objects should be final
341         * and any other instances are invalid
342         */
343        void enforceValidConnectionStateTransition() {
344            boolean result = false;
345            if (this == mDisconnected) {
346                result = mPrevState == null || mPrevState == mConnecting
347                        || mPrevState == mDisconnecting
348                        // TODO: edges to be removed after native stack refactoring
349                        // all transitions to disconnected state should go through a pending state
350                        // also, states should not go directly from an active audio state to
351                        // disconnected state
352                        || mPrevState == mConnected || mPrevState == mAudioOn
353                        || mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting;
354            } else if (this == mConnecting) {
355                result = mPrevState == mDisconnected;
356            } else if (this == mDisconnecting) {
357                result = mPrevState == mConnected
358                        // TODO: edges to be removed after native stack refactoring
359                        // all transitions to disconnecting state should go through connected state
360                        || mPrevState == mAudioConnecting || mPrevState == mAudioOn
361                        || mPrevState == mAudioDisconnecting;
362            } else if (this == mConnected) {
363                result = mPrevState == mConnecting || mPrevState == mAudioDisconnecting
364                        || mPrevState == mDisconnecting || mPrevState == mAudioConnecting
365                        // TODO: edges to be removed after native stack refactoring
366                        // all transitions to connected state should go through a pending state
367                        || mPrevState == mAudioOn || mPrevState == mDisconnected;
368            } else if (this == mAudioConnecting) {
369                result = mPrevState == mConnected;
370            } else if (this == mAudioDisconnecting) {
371                result = mPrevState == mAudioOn;
372            } else if (this == mAudioOn) {
373                result = mPrevState == mAudioConnecting || mPrevState == mAudioDisconnecting
374                        // TODO: edges to be removed after native stack refactoring
375                        // all transitions to audio connected state should go through a pending
376                        // state
377                        || mPrevState == mConnected;
378            }
379            if (!result) {
380                throw new IllegalStateException(
381                        "Invalid state transition from " + mPrevState + " to " + this
382                                + " for device " + mCurrentDevice);
383            }
384        }
385
386        void stateLogD(String msg) {
387            log(getName() + ": " + msg);
388        }
389
390        void stateLogW(String msg) {
391            logw(getName() + ": " + msg);
392        }
393
394        void stateLogE(String msg) {
395            loge(getName() + ": " + msg);
396        }
397
398        void stateLogV(String msg) {
399            logv(getName() + ": " + msg);
400        }
401
402        void stateLogI(String msg) {
403            logi(getName() + ": " + msg);
404        }
405
406        void stateLogWtfStack(String msg) {
407            Log.wtfStack(TAG, getName() + ": " + msg);
408        }
409
410        /**
411         * Process connection event
412         *
413         * @param message the current message for the event
414         * @param state connection state to transition to
415         * @param device associated device
416         */
417        public abstract void processConnectionEvent(Message message, int state,
418                BluetoothDevice device);
419
420        /**
421         * Get a state value from {@link BluetoothProfile} that represents the connection state of
422         * this headset state
423         *
424         * @return a value in {@link BluetoothProfile#STATE_DISCONNECTED},
425         * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
426         * {@link BluetoothProfile#STATE_DISCONNECTING}
427         */
428        abstract int getConnectionStateInt();
429
430        /**
431         * Get an audio state value from {@link BluetoothHeadset}
432         * @return a value in {@link BluetoothHeadset#STATE_AUDIO_DISCONNECTED},
433         * {@link BluetoothHeadset#STATE_AUDIO_CONNECTING}, or
434         * {@link BluetoothHeadset#STATE_AUDIO_CONNECTED}
435         */
436        abstract int getAudioStateInt();
437
438    }
439
440    class Disconnected extends HeadsetStateBase {
441        @Override
442        int getConnectionStateInt() {
443            return BluetoothProfile.STATE_DISCONNECTED;
444        }
445
446        @Override
447        int getAudioStateInt() {
448            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
449        }
450
451        @Override
452        public void enter() {
453            super.enter();
454            mPhonebook.resetAtState();
455            mSystemInterface.getHeadsetPhoneState().listenForPhoneState(false);
456            mVoiceRecognitionStarted = false;
457            mWaitingForVoiceRecognition = false;
458            mAudioParams.clear();
459            processWBSEvent(HeadsetHalConstants.BTHF_WBS_NO);
460            broadcastStateTransitions();
461            mCurrentDevice = null;
462        }
463
464        @Override
465        public boolean processMessage(Message message) {
466            if (mCurrentDevice != null) {
467                stateLogE("mCurrentDevice is not null");
468                return NOT_HANDLED;
469            }
470            switch (message.what) {
471                case CONNECT:
472                    BluetoothDevice device = (BluetoothDevice) message.obj;
473                    stateLogD("Connecting to " + device);
474                    if (!mNativeInterface.connectHfp(device)) {
475                        // No state transition is involved, fire broadcast immediately
476                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
477                                BluetoothProfile.STATE_DISCONNECTED);
478                        break;
479                    }
480                    mCurrentDevice = device;
481                    transitionTo(mConnecting);
482                    break;
483                case DISCONNECT:
484                    // ignore
485                    break;
486                case CALL_STATE_CHANGED:
487                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
488                    break;
489                case DEVICE_STATE_CHANGED:
490                    stateLogD("Ignoring DEVICE_STATE_CHANGED event");
491                    break;
492                case STACK_EVENT:
493                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
494                    stateLogD("STACK_EVENT: " + event);
495                    switch (event.type) {
496                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
497                            processConnectionEvent(message, event.valueInt, event.device);
498                            break;
499                        default:
500                            stateLogE("Unexpected stack event: " + event);
501                            break;
502                    }
503                    break;
504                default:
505                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
506                    return NOT_HANDLED;
507            }
508            return HANDLED;
509        }
510
511        // in Disconnected state
512        @Override
513        public void processConnectionEvent(Message message, int state, BluetoothDevice device) {
514            stateLogD("processConnectionEvent, state=" + state + ", device=" + device);
515            switch (state) {
516                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
517                    stateLogW("ignore DISCONNECTED event, device=" + device);
518                    break;
519                // Both events result in Connecting state as SLC establishment is still required
520                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
521                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
522                    if (okToAcceptConnection(device)) {
523                        stateLogI("connected/connecting incoming HF, device=" + device);
524                        mCurrentDevice = device;
525                        transitionTo(mConnecting);
526                    } else {
527                        stateLogI("rejected incoming HF, priority=" + mService.getPriority(device)
528                                + " bondState=" + device.getBondState() + ", device=" + device);
529                        // Reject the connection and stay in Disconnected state itself
530                        if (!mNativeInterface.disconnectHfp(device)) {
531                            stateLogE("Failed to disconnect from " + device);
532                        }
533                        // Indicate rejection to other components.
534                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
535                                BluetoothProfile.STATE_DISCONNECTED);
536                    }
537                    break;
538                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
539                    stateLogW("Ignore DISCONNECTING event, device=" + device);
540                    break;
541                default:
542                    stateLogE("Incorrect state: " + state);
543                    break;
544            }
545        }
546    }
547
548    // Per HFP 1.7.1 spec page 23/144, Pending state needs to handle
549    //      AT+BRSF, AT+CIND, AT+CMER, AT+BIND, +CHLD
550    // commands during SLC establishment
551    class Connecting extends HeadsetStateBase {
552        @Override
553        int getConnectionStateInt() {
554            return BluetoothProfile.STATE_CONNECTING;
555        }
556
557        @Override
558        int getAudioStateInt() {
559            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
560        }
561
562        @Override
563        public void enter() {
564            super.enter();
565            sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis);
566            broadcastStateTransitions();
567        }
568
569        @Override
570        public boolean processMessage(Message message) {
571            switch (message.what) {
572                case CONNECT:
573                case CONNECT_AUDIO:
574                case DISCONNECT:
575                    deferMessage(message);
576                    break;
577                case CONNECT_TIMEOUT:
578                    // We timed out trying to connect, transition to Disconnected state
579                    stateLogW("Connection timeout for " + mCurrentDevice);
580                    transitionTo(mDisconnected);
581                    break;
582                case CALL_STATE_CHANGED:
583                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
584                    break;
585                case DEVICE_STATE_CHANGED:
586                    stateLogD("ignoring DEVICE_STATE_CHANGED event");
587                    break;
588                case STACK_EVENT:
589                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
590                    stateLogD("STACK_EVENT: " + event);
591                    switch (event.type) {
592                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
593                            processConnectionEvent(message, event.valueInt, event.device);
594                            break;
595                        case HeadsetStackEvent.EVENT_TYPE_AT_CHLD:
596                            processAtChld(event.valueInt, event.device);
597                            break;
598                        case HeadsetStackEvent.EVENT_TYPE_AT_CIND:
599                            processAtCind(event.device);
600                            break;
601                        case HeadsetStackEvent.EVENT_TYPE_WBS:
602                            processWBSEvent(event.valueInt);
603                            break;
604                        case HeadsetStackEvent.EVENT_TYPE_BIND:
605                            processAtBind(event.valueString, event.device);
606                            break;
607                        // Unexpected AT commands, we only handle them for comparability reasons
608                        case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED:
609                            stateLogW("Unexpected VR event, device=" + event.device + ", state="
610                                    + event.valueInt);
611                            processVrEvent(event.valueInt, event.device);
612                            break;
613                        case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL:
614                            stateLogW("Unexpected dial event, device=" + event.device);
615                            processDialCall(event.valueString, event.device);
616                            break;
617                        case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
618                            stateLogW("Unexpected subscriber number event for" + event.device
619                                    + ", state=" + event.valueInt);
620                            processSubscriberNumberRequest(event.device);
621                            break;
622                        case HeadsetStackEvent.EVENT_TYPE_AT_COPS:
623                            stateLogW("Unexpected COPS event for " + event.device);
624                            processAtCops(event.device);
625                            break;
626                        case HeadsetStackEvent.EVENT_TYPE_AT_CLCC:
627                            Log.w(TAG, "Connecting: Unexpected CLCC event for" + event.device);
628                            processAtClcc(event.device);
629                            break;
630                        case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT:
631                            stateLogW("Unexpected unknown AT event for" + event.device + ", cmd="
632                                    + event.valueString);
633                            processUnknownAt(event.valueString, event.device);
634                            break;
635                        case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED:
636                            stateLogW("Unexpected key-press event for " + event.device);
637                            processKeyPressed(event.device);
638                            break;
639                        case HeadsetStackEvent.EVENT_TYPE_BIEV:
640                            stateLogW("Unexpected BIEV event for " + event.device + ", indId="
641                                    + event.valueInt + ", indVal=" + event.valueInt2);
642                            processAtBiev(event.valueInt, event.valueInt2, event.device);
643                            break;
644                        case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED:
645                            stateLogW("Unexpected volume event for " + event.device);
646                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
647                            break;
648                        case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL:
649                            stateLogW("Unexpected answer event for " + event.device);
650                            mSystemInterface.answerCall(event.device);
651                            break;
652                        case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL:
653                            stateLogW("Unexpected hangup event for " + event.device);
654                            mSystemInterface.hangupCall(event.device, isVirtualCallInProgress());
655                            break;
656                        default:
657                            stateLogE("Unexpected event: " + event);
658                            break;
659                    }
660                    break;
661                default:
662                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
663                    return NOT_HANDLED;
664            }
665            return HANDLED;
666        }
667
668        @Override
669        public void processConnectionEvent(Message message, int state, BluetoothDevice device) {
670            stateLogD("processConnectionEvent, state=" + state + ", device=" + device);
671            switch (state) {
672                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
673                    if (!mCurrentDevice.equals(device)) {
674                        stateLogW("Unknown device disconnected" + device);
675                        break;
676                    }
677                    transitionTo(mDisconnected);
678                    break;
679                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
680                    stateLogD("RFCOMM connected for " + device);
681                    if (!mCurrentDevice.equals(device)) {
682                        stateLogW("Reject connection from unknown device " + device);
683                        if (!mNativeInterface.disconnectHfp(device)) {
684                            stateLogE("Disconnect from " + device + " failed");
685                        }
686                    }
687                    break;
688                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
689                    stateLogD("SLC connected for " + device);
690                    if (!mCurrentDevice.equals(device)) {
691                        stateLogW("Reject SLC from unknown device " + device);
692                        if (!mNativeInterface.disconnectHfp(device)) {
693                            stateLogE("Disconnect SLC from " + device + " failed");
694                        }
695                        break;
696                    }
697                    configAudioParameters(device);
698                    mSystemInterface.queryPhoneState();
699                    transitionTo(mConnected);
700                    break;
701                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
702                    // Ignored
703                    break;
704                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
705                    stateLogD("Disconnecting for " + device);
706                    if (mCurrentDevice.equals(device)) {
707                        stateLogW("Current device disconnecting");
708                        // ignored, wait for it to be disconnected
709                    }
710                    break;
711                default:
712                    stateLogE("Incorrect state " + state);
713                    break;
714            }
715        }
716
717        @Override
718        public void exit() {
719            removeMessages(CONNECT_TIMEOUT);
720            super.exit();
721        }
722    }
723
724    class Disconnecting extends HeadsetStateBase {
725        @Override
726        int getConnectionStateInt() {
727            return BluetoothProfile.STATE_DISCONNECTING;
728        }
729
730        @Override
731        int getAudioStateInt() {
732            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
733        }
734
735        @Override
736        public void enter() {
737            super.enter();
738            sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis);
739            broadcastStateTransitions();
740        }
741
742        @Override
743        public boolean processMessage(Message message) {
744            switch (message.what) {
745                case CONNECT:
746                case CONNECT_AUDIO:
747                case DISCONNECT:
748                    deferMessage(message);
749                    break;
750                case CONNECT_TIMEOUT:
751                    stateLogE("timeout");
752                    transitionTo(mDisconnected);
753                    break;
754                case STACK_EVENT:
755                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
756                    stateLogD("STACK_EVENT: " + event);
757                    switch (event.type) {
758                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
759                            processConnectionEvent(message, event.valueInt, event.device);
760                            break;
761                        default:
762                            stateLogE("Unexpected event: " + event);
763                            break;
764                    }
765                    break;
766                default:
767                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
768                    return NOT_HANDLED;
769            }
770            return HANDLED;
771        }
772
773        // in Disconnecting state
774        @Override
775        public void processConnectionEvent(Message message, int state, BluetoothDevice device) {
776            if (!mCurrentDevice.equals(device)) {
777                stateLogW("processConnectionEvent, unknown device " + device);
778                return;
779            }
780            switch (state) {
781                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
782                    stateLogD("Device disconnected, device=" + device);
783                    transitionTo(mDisconnected);
784                    break;
785                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
786                    stateLogD("Device connected, device=" + device);
787                    transitionTo(mConnected);
788                    break;
789                default:
790                    stateLogE("Device: " + device + " bad state: " + state);
791                    break;
792            }
793        }
794
795        @Override
796        public void exit() {
797            removeMessages(CONNECT_TIMEOUT);
798            super.exit();
799        }
800    }
801
802    /**
803     * Base class for Connected, AudioConnecting, AudioOn, AudioDisconnecting states
804     */
805    private abstract class ConnectedBase extends HeadsetStateBase {
806        @Override
807        int getConnectionStateInt() {
808            return BluetoothProfile.STATE_CONNECTED;
809        }
810
811        /**
812         * Handle common messages in connected states. However, state specific messages must be
813         * handled individually.
814         *
815         * @param message Incoming message to handle
816         * @return True if handled successfully, False otherwise
817         */
818        @Override
819        public boolean processMessage(Message message) {
820            switch (message.what) {
821                case CONNECT:
822                case DISCONNECT:
823                case CONNECT_AUDIO:
824                case DISCONNECT_AUDIO:
825                case CONNECT_TIMEOUT:
826                    stateLogWtfStack("Illegal message in generic handler: " + message);
827                    break;
828                case VOICE_RECOGNITION_START:
829                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED,
830                            (BluetoothDevice) message.obj);
831                    break;
832                case VOICE_RECOGNITION_STOP:
833                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED,
834                            (BluetoothDevice) message.obj);
835                    break;
836                case CALL_STATE_CHANGED:
837                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
838                    break;
839                case DEVICE_STATE_CHANGED:
840                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
841                    break;
842                case SEND_CCLC_RESPONSE:
843                    processSendClccResponse((HeadsetClccResponse) message.obj);
844                    break;
845                case CLCC_RSP_TIMEOUT: {
846                    BluetoothDevice device = (BluetoothDevice) message.obj;
847                    mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
848                }
849                break;
850                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
851                    processSendVendorSpecificResultCode(
852                            (HeadsetVendorSpecificResultCode) message.obj);
853                    break;
854                case DIALING_OUT_TIMEOUT: {
855                    BluetoothDevice device = (BluetoothDevice) message.obj;
856                    if (mDialingOut) {
857                        mDialingOut = false;
858                        mNativeInterface.atResponseCode(device,
859                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
860                    }
861                }
862                break;
863                case VIRTUAL_CALL_START:
864                    initiateScoUsingVirtualVoiceCall();
865                    break;
866                case VIRTUAL_CALL_STOP:
867                    terminateScoUsingVirtualVoiceCall();
868                    break;
869                case START_VR_TIMEOUT: {
870                    BluetoothDevice device = (BluetoothDevice) message.obj;
871                    if (mWaitingForVoiceRecognition) {
872                        device = (BluetoothDevice) message.obj;
873                        mWaitingForVoiceRecognition = false;
874                        stateLogE("Timeout waiting for voice recognition to start");
875                        mNativeInterface.atResponseCode(device,
876                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
877                    }
878                }
879                break;
880                case INTENT_CONNECTION_ACCESS_REPLY:
881                    handleAccessPermissionResult((Intent) message.obj);
882                    break;
883                case STACK_EVENT:
884                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
885                    stateLogD("STACK_EVENT: " + event);
886                    switch (event.type) {
887                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
888                            processConnectionEvent(message, event.valueInt, event.device);
889                            break;
890                        case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
891                            processAudioEvent(event.valueInt, event.device);
892                            break;
893                        case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED:
894                            processVrEvent(event.valueInt, event.device);
895                            break;
896                        case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL:
897                            mSystemInterface.answerCall(event.device);
898                            break;
899                        case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL:
900                            mSystemInterface.hangupCall(event.device, mVirtualCallStarted);
901                            break;
902                        case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED:
903                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
904                            break;
905                        case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL:
906                            processDialCall(event.valueString, event.device);
907                            break;
908                        case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF:
909                            mSystemInterface.sendDtmf(event.valueInt, event.device);
910                            break;
911                        case HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION:
912                            processNoiseReductionEvent(event.valueInt == 1, event.device);
913                            break;
914                        case HeadsetStackEvent.EVENT_TYPE_WBS:
915                            processWBSEvent(event.valueInt);
916                            break;
917                        case HeadsetStackEvent.EVENT_TYPE_AT_CHLD:
918                            processAtChld(event.valueInt, event.device);
919                            break;
920                        case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
921                            processSubscriberNumberRequest(event.device);
922                            break;
923                        case HeadsetStackEvent.EVENT_TYPE_AT_CIND:
924                            processAtCind(event.device);
925                            break;
926                        case HeadsetStackEvent.EVENT_TYPE_AT_COPS:
927                            processAtCops(event.device);
928                            break;
929                        case HeadsetStackEvent.EVENT_TYPE_AT_CLCC:
930                            processAtClcc(event.device);
931                            break;
932                        case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT:
933                            processUnknownAt(event.valueString, event.device);
934                            break;
935                        case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED:
936                            processKeyPressed(event.device);
937                            break;
938                        case HeadsetStackEvent.EVENT_TYPE_BIND:
939                            processAtBind(event.valueString, event.device);
940                            break;
941                        case HeadsetStackEvent.EVENT_TYPE_BIEV:
942                            processAtBiev(event.valueInt, event.valueInt2, event.device);
943                            break;
944                        default:
945                            stateLogE("Unknown stack event: " + event);
946                            break;
947                    }
948                    break;
949                default:
950                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
951                    return NOT_HANDLED;
952            }
953            return HANDLED;
954        }
955
956        @Override
957        public void processConnectionEvent(Message message, int state, BluetoothDevice device) {
958            stateLogD("processConnectionEvent, state=" + state + ", device=" + device);
959            switch (state) {
960                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
961                    if (mCurrentDevice.equals(device)) {
962                        stateLogE("Same device connect RFCOMM again, should never happen");
963                        break;
964                    }
965                    // reject the connection and stay in Connected state itself
966                    stateLogI("Incoming Hf rejected. priority=" + mService.getPriority(device)
967                            + " bondState=" + device.getBondState());
968                    if (!mNativeInterface.disconnectHfp(device)) {
969                        stateLogW("Fail to disconnect " + device);
970                        break;
971                    }
972                    break;
973                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
974                    // Should have been rejected in CONNECTION_STATE_CONNECTED
975                    if (mCurrentDevice.equals(device)) {
976                        stateLogE("Same device connected SLC again");
977                        break;
978                    }
979                    // reject the connection and stay in Connected state itself
980                    stateLogI("Incoming Hf SLC rejected. priority=" + mService.getPriority(device)
981                            + " bondState=" + device.getBondState());
982                    if (!mNativeInterface.disconnectHfp(device)) {
983                        stateLogW("Fail to disconnect " + device);
984                        break;
985                    }
986                    break;
987                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
988                    if (!mCurrentDevice.equals(device)) {
989                        stateLogW("Unknown device disconnecting, device=" + device);
990                        break;
991                    }
992                    stateLogI("Current device disconnecting " + mCurrentDevice);
993                    transitionTo(mDisconnecting);
994                    break;
995                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
996                    if (!mCurrentDevice.equals(device)) {
997                        stateLogW("Unknown device disconnected " + device);
998                        break;
999                    }
1000                    stateLogI("Current device disconnected " + mCurrentDevice);
1001                    transitionTo(mDisconnected);
1002                    break;
1003                default:
1004                    stateLogE("Connection State Device: " + device + " bad state: " + state);
1005                    break;
1006            }
1007        }
1008
1009        /**
1010         * Each state should handle audio events differently
1011         *
1012         * @param state audio state
1013         * @param device associated device
1014         */
1015        public abstract void processAudioEvent(int state, BluetoothDevice device);
1016    }
1017
1018    class Connected extends ConnectedBase {
1019        @Override
1020        int getAudioStateInt() {
1021            return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1022        }
1023
1024        @Override
1025        public void enter() {
1026            super.enter();
1027            // start phone state listener here so that the CIND response as part of SLC can be
1028            // responded to, correctly.
1029            // listenForPhoneState(boolean) internally handles multiple calls to start listen
1030            mSystemInterface.getHeadsetPhoneState().listenForPhoneState(true);
1031            if (mPrevState == mConnecting) {
1032                // Remove pending connection attempts that were deferred during the pending
1033                // state. This is to prevent auto connect attempts from disconnecting
1034                // devices that previously successfully connected.
1035                removeDeferredMessages(CONNECT);
1036            }
1037            broadcastStateTransitions();
1038        }
1039
1040        @Override
1041        public boolean processMessage(Message message) {
1042            switch (message.what) {
1043                case CONNECT: {
1044                    BluetoothDevice device = (BluetoothDevice) message.obj;
1045                    stateLogI("CONNECT, device " + device);
1046                    if (mCurrentDevice.equals(device)) {
1047                        stateLogW("CONNECT, device " + device + " is already connected");
1048                        break;
1049                    }
1050                    stateLogD("CONNECT, disconnect current device " + mCurrentDevice);
1051                    if (!mNativeInterface.disconnectHfp(mCurrentDevice)) {
1052                        stateLogW("CONNECT, Failed to disconnect " + mCurrentDevice);
1053                        // broadcast immediately as no state transition is involved
1054                        // TODO: to be removed with multi-HFP implementation
1055                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1056                                BluetoothProfile.STATE_DISCONNECTED);
1057                        break;
1058                    }
1059                    // Defer connect message to future state
1060                    deferMessage(message);
1061                    transitionTo(mDisconnecting);
1062                }
1063                break;
1064                case DISCONNECT: {
1065                    BluetoothDevice device = (BluetoothDevice) message.obj;
1066                    stateLogD("DISCONNECT from device=" + device);
1067                    if (!mCurrentDevice.equals(device)) {
1068                        stateLogW("DISCONNECT, device " + device + " not connected");
1069                        break;
1070                    }
1071                    if (!mNativeInterface.disconnectHfp(device)) {
1072                        // broadcast immediately as no state transition is involved
1073                        stateLogE("DISCONNECT from " + device + " failed");
1074                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1075                                BluetoothProfile.STATE_CONNECTED);
1076                        break;
1077                    }
1078                    transitionTo(mDisconnecting);
1079                }
1080                break;
1081                case CONNECT_AUDIO:
1082                    stateLogD("CONNECT_AUDIO, device=" + mCurrentDevice);
1083                    if (!isScoAcceptable()) {
1084                        stateLogW("CONNECT_AUDIO No Active/Held call, no call setup, and no "
1085                                + "in-band ringing, not allowing SCO, device=" + mCurrentDevice);
1086                        break;
1087                    }
1088                    if (!mNativeInterface.connectAudio(mCurrentDevice)) {
1089                        stateLogE("Failed to connect SCO audio for " + mCurrentDevice);
1090                        // No state change involved, fire broadcast immediately
1091                        broadcastAudioState(mCurrentDevice,
1092                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1093                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1094                        break;
1095                    }
1096                    transitionTo(mAudioConnecting);
1097                    break;
1098                case DISCONNECT_AUDIO:
1099                    stateLogD("DISCONNECT_AUDIO, device=" + mCurrentDevice);
1100                    // ignore
1101                    break;
1102                default:
1103                    return super.processMessage(message);
1104            }
1105            return HANDLED;
1106        }
1107
1108        @Override
1109        public void processAudioEvent(int state, BluetoothDevice device) {
1110            stateLogD("processAudioEvent, state=" + state + ", device=" + device);
1111            if (!mCurrentDevice.equals(device)) {
1112                // Crash if audio is connected for unknown device
1113                stateLogWtfStack("Audio changed on unknown device: " + device);
1114                return;
1115            }
1116            switch (state) {
1117                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1118                    if (!isScoAcceptable()) {
1119                        stateLogW("Rejecting incoming audio connection from " + device);
1120                        if (!mNativeInterface.disconnectAudio(device)) {
1121                            stateLogE("Fail to disconnect audio for " + device);
1122                        }
1123                        break;
1124                    }
1125                    stateLogI("Audio connected for " + device);
1126                    transitionTo(mAudioOn);
1127                    break;
1128                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1129                    if (!isScoAcceptable()) {
1130                        stateLogW("Rejecting incoming pending audio connection from " + device);
1131                        if (!mNativeInterface.disconnectAudio(device)) {
1132                            stateLogE("Fail to disconnect audio for " + device);
1133                        }
1134                        break;
1135                    }
1136                    stateLogI("Audio connecting for " + device);
1137                    transitionTo(mAudioConnecting);
1138                    break;
1139                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1140                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1141                    // ignore
1142                    break;
1143                default:
1144                    stateLogE("Audio State Device: " + device + " bad state: " + state);
1145                    break;
1146            }
1147        }
1148    }
1149
1150    class AudioConnecting extends ConnectedBase {
1151        @Override
1152        int getAudioStateInt() {
1153            return BluetoothHeadset.STATE_AUDIO_CONNECTING;
1154        }
1155
1156        @Override
1157        public void enter() {
1158            super.enter();
1159            sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis);
1160            broadcastStateTransitions();
1161        }
1162
1163        @Override
1164        public boolean processMessage(Message message) {
1165            switch (message.what) {
1166                case CONNECT:
1167                case DISCONNECT:
1168                case CONNECT_AUDIO:
1169                case DISCONNECT_AUDIO:
1170                    deferMessage(message);
1171                    break;
1172                case CONNECT_TIMEOUT: {
1173                    BluetoothDevice device = (BluetoothDevice) message.obj;
1174                    if (!mCurrentDevice.equals(device)) {
1175                        stateLogW("CONNECT_TIMEOUT for unknown device " + device);
1176                        break;
1177                    }
1178                    stateLogW("CONNECT_TIMEOUT");
1179                    transitionTo(mConnected);
1180                    break;
1181                }
1182                default:
1183                    return super.processMessage(message);
1184            }
1185            return HANDLED;
1186        }
1187
1188        @Override
1189        public void processAudioEvent(int state, BluetoothDevice device) {
1190            if (!mCurrentDevice.equals(device)) {
1191                // Crash on unknown device audio state change
1192                stateLogWtfStack("Audio state changed on unknown device: " + device);
1193                return;
1194            }
1195            switch (state) {
1196                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1197                    stateLogW("Audio connection failed");
1198                    transitionTo(mConnected);
1199                    break;
1200                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1201                    // ignore, already in audio connecting state
1202                    break;
1203                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1204                    // ignore, there is no BluetoothHeadset.STATE_AUDIO_DISCONNECTING
1205                    break;
1206                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1207                    stateLogI("Audio connected for device " + device);
1208                    transitionTo(mAudioOn);
1209                    break;
1210                default:
1211                    stateLogE("Audio State Device: " + device + " bad state: " + state);
1212                    break;
1213            }
1214        }
1215
1216        @Override
1217        public void exit() {
1218            removeMessages(CONNECT_TIMEOUT);
1219            super.exit();
1220        }
1221    }
1222
1223    class AudioOn extends ConnectedBase {
1224        @Override
1225        int getAudioStateInt() {
1226            return BluetoothHeadset.STATE_AUDIO_CONNECTED;
1227        }
1228
1229        @Override
1230        public void enter() {
1231            super.enter();
1232            removeDeferredMessages(CONNECT_AUDIO);
1233            setAudioParameters(mCurrentDevice);
1234            mSystemInterface.getAudioManager().setBluetoothScoOn(true);
1235            broadcastStateTransitions();
1236        }
1237
1238        @Override
1239        public boolean processMessage(Message message) {
1240            switch (message.what) {
1241                case CONNECT: {
1242                    BluetoothDevice device = (BluetoothDevice) message.obj;
1243                    stateLogD("CONNECT, device=" + device);
1244                    if (mCurrentDevice.equals(device)) {
1245                        stateLogW("CONNECT, device " + device + " is connected");
1246                        break;
1247                    }
1248                    // When connecting separate device, disconnect the current one first
1249                    // Disconnect audio and then disconnect SLC
1250                    stateLogD("Disconnecting SCO, device=" + mCurrentDevice);
1251                    if (!mNativeInterface.disconnectAudio(mCurrentDevice)) {
1252                        stateLogE("Disconnect SCO failed, device=" + mCurrentDevice
1253                                + ", abort connection to " + device);
1254                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1255                                BluetoothProfile.STATE_DISCONNECTED);
1256                        break;
1257                    }
1258                    deferMessage(message);
1259                    transitionTo(mAudioDisconnecting);
1260                    break;
1261                }
1262                case DISCONNECT: {
1263                    BluetoothDevice device = (BluetoothDevice) message.obj;
1264                    stateLogD("DISCONNECT, device=" + device);
1265                    if (!mCurrentDevice.equals(device)) {
1266                        stateLogW("DISCONNECT, device " + device + " not connected");
1267                        break;
1268                    }
1269                    // Disconnect BT SCO first
1270                    if (!mNativeInterface.disconnectAudio(mCurrentDevice)) {
1271                        stateLogW("DISCONNECT failed, device=" + mCurrentDevice);
1272                        // if disconnect BT SCO failed, transition to mConnected state to force
1273                        // disconnect device
1274                    }
1275                    deferMessage(obtainMessage(DISCONNECT, mCurrentDevice));
1276                    transitionTo(mConnected);
1277                    break;
1278                }
1279                case CONNECT_AUDIO: {
1280                    BluetoothDevice device = (BluetoothDevice) message.obj;
1281                    if (!mCurrentDevice.equals(device)) {
1282                        stateLogW("CONNECT_AUDIO device is not connected " + device);
1283                        break;
1284                    }
1285                    stateLogW("CONNECT_AUDIO device auido is already connected " + device);
1286                    break;
1287                }
1288                case DISCONNECT_AUDIO:
1289                    if (mNativeInterface.disconnectAudio(mCurrentDevice)) {
1290                        stateLogD("DISCONNECT_AUDIO, device=" + mCurrentDevice);
1291                        transitionTo(mAudioDisconnecting);
1292                    } else {
1293                        stateLogW("DISCONNECT_AUDIO failed, device=" + mCurrentDevice);
1294                    }
1295                    break;
1296                case VOICE_RECOGNITION_START:
1297                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED,
1298                            (BluetoothDevice) message.obj);
1299                    break;
1300                case VOICE_RECOGNITION_STOP:
1301                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED,
1302                            (BluetoothDevice) message.obj);
1303                    break;
1304                case INTENT_SCO_VOLUME_CHANGED:
1305                    processIntentScoVolume((Intent) message.obj, mCurrentDevice);
1306                    break;
1307                case CALL_STATE_CHANGED:
1308                    processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
1309                    break;
1310                case DEVICE_STATE_CHANGED:
1311                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1312                    break;
1313                case SEND_CCLC_RESPONSE:
1314                    processSendClccResponse((HeadsetClccResponse) message.obj);
1315                    break;
1316                case CLCC_RSP_TIMEOUT: {
1317                    BluetoothDevice device = (BluetoothDevice) message.obj;
1318                    mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
1319                    break;
1320                }
1321                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
1322                    processSendVendorSpecificResultCode(
1323                            (HeadsetVendorSpecificResultCode) message.obj);
1324                    break;
1325
1326                case VIRTUAL_CALL_START:
1327                    initiateScoUsingVirtualVoiceCall();
1328                    break;
1329                case VIRTUAL_CALL_STOP:
1330                    terminateScoUsingVirtualVoiceCall();
1331                    break;
1332
1333                case DIALING_OUT_TIMEOUT: {
1334                    if (mDialingOut) {
1335                        BluetoothDevice device = (BluetoothDevice) message.obj;
1336                        mDialingOut = false;
1337                        mNativeInterface.atResponseCode(device,
1338                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1339                    }
1340                    break;
1341                }
1342                case START_VR_TIMEOUT: {
1343                    if (mWaitingForVoiceRecognition) {
1344                        BluetoothDevice device = (BluetoothDevice) message.obj;
1345                        mWaitingForVoiceRecognition = false;
1346                        stateLogE("Timeout waiting for voice recognition to start");
1347                        mNativeInterface.atResponseCode(device,
1348                                HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1349                    }
1350                    break;
1351                }
1352                case STACK_EVENT:
1353                    HeadsetStackEvent event = (HeadsetStackEvent) message.obj;
1354                    stateLogD("STACK_EVENT: " + event);
1355                    switch (event.type) {
1356                        case HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
1357                            processConnectionEvent(message, event.valueInt, event.device);
1358                            break;
1359                        case HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
1360                            processAudioEvent(event.valueInt, event.device);
1361                            break;
1362                        case HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED:
1363                            processVrEvent(event.valueInt, event.device);
1364                            break;
1365                        case HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL:
1366                            mSystemInterface.answerCall(event.device);
1367                            break;
1368                        case HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL:
1369                            mSystemInterface.hangupCall(event.device, mVirtualCallStarted);
1370                            break;
1371                        case HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED:
1372                            processVolumeEvent(event.valueInt, event.valueInt2, event.device);
1373                            break;
1374                        case HeadsetStackEvent.EVENT_TYPE_DIAL_CALL:
1375                            processDialCall(event.valueString, event.device);
1376                            break;
1377                        case HeadsetStackEvent.EVENT_TYPE_SEND_DTMF:
1378                            mSystemInterface.sendDtmf(event.valueInt, event.device);
1379                            break;
1380                        case HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION:
1381                            processNoiseReductionEvent(event.valueInt == 1, event.device);
1382                            break;
1383                        case HeadsetStackEvent.EVENT_TYPE_AT_CHLD:
1384                            processAtChld(event.valueInt, event.device);
1385                            break;
1386                        case HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1387                            processSubscriberNumberRequest(event.device);
1388                            break;
1389                        case HeadsetStackEvent.EVENT_TYPE_AT_CIND:
1390                            processAtCind(event.device);
1391                            break;
1392                        case HeadsetStackEvent.EVENT_TYPE_AT_COPS:
1393                            processAtCops(event.device);
1394                            break;
1395                        case HeadsetStackEvent.EVENT_TYPE_AT_CLCC:
1396                            processAtClcc(event.device);
1397                            break;
1398                        case HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT:
1399                            processUnknownAt(event.valueString, event.device);
1400                            break;
1401                        case HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED:
1402                            processKeyPressed(event.device);
1403                            break;
1404                        case HeadsetStackEvent.EVENT_TYPE_BIND:
1405                            processAtBind(event.valueString, event.device);
1406                            break;
1407                        case HeadsetStackEvent.EVENT_TYPE_BIEV:
1408                            processAtBiev(event.valueInt, event.valueInt2, event.device);
1409                            break;
1410                        default:
1411                            stateLogE("Unknown stack event: " + event);
1412                            break;
1413                    }
1414                    break;
1415                default:
1416                    stateLogE("Unexpected msg " + getMessageName(message.what) + ": " + message);
1417                    return NOT_HANDLED;
1418            }
1419            return HANDLED;
1420        }
1421
1422        // in AudioOn state
1423        @Override
1424        public void processAudioEvent(int state, BluetoothDevice device) {
1425            if (!mCurrentDevice.equals(device)) {
1426                stateLogE("Audio changed on unknown device: " + device);
1427                return;
1428            }
1429            switch (state) {
1430                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1431                    stateLogI("Audio disconnected by remote");
1432                    transitionTo(mConnected);
1433                    break;
1434                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1435                    stateLogI("Audio being disconnected by remote");
1436                    transitionTo(mAudioDisconnecting);
1437                    break;
1438                default:
1439                    stateLogE("Audio State Device: " + device + " bad state: " + state);
1440                    break;
1441            }
1442        }
1443
1444        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1445            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1446            if (mSpeakerVolume != volumeValue) {
1447                mSpeakerVolume = volumeValue;
1448                mNativeInterface.setVolume(device, HeadsetHalConstants.VOLUME_TYPE_SPK,
1449                        mSpeakerVolume);
1450            }
1451        }
1452
1453        @Override
1454        public void exit() {
1455            mSystemInterface.getAudioManager().setBluetoothScoOn(false);
1456            super.exit();
1457        }
1458    }
1459
1460    class AudioDisconnecting extends ConnectedBase {
1461        @Override
1462        int getAudioStateInt() {
1463            // TODO: need BluetoothHeadset.STATE_AUDIO_DISCONNECTING
1464            return BluetoothHeadset.STATE_AUDIO_CONNECTED;
1465        }
1466
1467        @Override
1468        public void enter() {
1469            super.enter();
1470            sendMessageDelayed(CONNECT_TIMEOUT, mCurrentDevice, sConnectTimeoutMillis);
1471            broadcastStateTransitions();
1472        }
1473
1474        @Override
1475        public boolean processMessage(Message message) {
1476            switch (message.what) {
1477                case CONNECT:
1478                case DISCONNECT:
1479                case CONNECT_AUDIO:
1480                case DISCONNECT_AUDIO:
1481                    deferMessage(message);
1482                    break;
1483                case CONNECT_TIMEOUT: {
1484                    BluetoothDevice device = (BluetoothDevice) message.obj;
1485                    if (!mCurrentDevice.equals(device)) {
1486                        stateLogW("CONNECT_TIMEOUT for unknown device " + device);
1487                        break;
1488                    }
1489                    stateLogW("CONNECT_TIMEOUT");
1490                    transitionTo(mConnected);
1491                    break;
1492                }
1493                default:
1494                    return super.processMessage(message);
1495            }
1496            return HANDLED;
1497        }
1498
1499        @Override
1500        public void processAudioEvent(int state, BluetoothDevice device) {
1501            if (!mCurrentDevice.equals(device)) {
1502                // Crash if audio state change happen for unknown device
1503                stateLogWtfStack("Audio changed on unknown device: " + device);
1504                return;
1505            }
1506            switch (state) {
1507                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1508                    stateLogI("Audio disconnected for " + device);
1509                    transitionTo(mConnected);
1510                    break;
1511                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1512                    // ignore
1513                    break;
1514                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1515                    stateLogW("Audio disconnection failed for " + device);
1516                    transitionTo(mAudioOn);
1517                    break;
1518                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1519                    // ignore, see if it goes into connected state, otherwise, timeout
1520                    break;
1521                default:
1522                    stateLogE("Audio State Device: " + device + " bad state: " + state);
1523                    break;
1524            }
1525        }
1526
1527        @Override
1528        public void exit() {
1529            removeMessages(CONNECT_TIMEOUT);
1530            super.exit();
1531        }
1532    }
1533
1534    synchronized BluetoothDevice getCurrentDevice() {
1535        return mCurrentDevice;
1536    }
1537
1538    synchronized int getConnectionState(BluetoothDevice device) {
1539        if (mCurrentDevice == null) {
1540            return BluetoothProfile.STATE_DISCONNECTED;
1541        }
1542        if (!mCurrentDevice.equals(device)) {
1543            return BluetoothProfile.STATE_DISCONNECTED;
1544        }
1545        return ((HeadsetStateBase) getCurrentState()).getConnectionStateInt();
1546    }
1547
1548    List<BluetoothDevice> getConnectedDevices() {
1549        List<BluetoothDevice> devices = new ArrayList<>();
1550        synchronized (this) {
1551            if (getCurrentState() instanceof ConnectedBase) {
1552                devices.add(mCurrentDevice);
1553            }
1554        }
1555        return devices;
1556    }
1557
1558    void setAudioRouteAllowed(boolean allowed) {
1559        mAudioRouteAllowed = allowed;
1560        mNativeInterface.setScoAllowed(allowed);
1561    }
1562
1563    boolean getAudioRouteAllowed() {
1564        return mAudioRouteAllowed;
1565    }
1566
1567    void setForceScoAudio(boolean forced) {
1568        mForceScoAudio = forced;
1569    }
1570
1571    synchronized int getAudioState() {
1572        return ((HeadsetStateBase) getCurrentState()).getAudioStateInt();
1573    }
1574
1575    private void processVrEvent(int state, BluetoothDevice device) {
1576        if (device == null) {
1577            Log.w(TAG, "processVrEvent device is null");
1578            return;
1579        }
1580        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: "
1581                + mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: "
1582                + mWaitingForVoiceRecognition + " isInCall: " + isInCall());
1583        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
1584            if (!isVirtualCallInProgress() && !isInCall()) {
1585                IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
1586                        ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
1587                if (dic != null) {
1588                    try {
1589                        dic.exitIdle("voice-command");
1590                    } catch (RemoteException e) {
1591                    }
1592                }
1593                try {
1594                    mService.startActivity(VOICE_COMMAND_INTENT);
1595                } catch (ActivityNotFoundException e) {
1596                    mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR,
1597                            0);
1598                    return;
1599                }
1600                expectVoiceRecognition(device);
1601            } else {
1602                // send error response if call is ongoing
1603                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1604                return;
1605            }
1606        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
1607            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
1608                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
1609                mVoiceRecognitionStarted = false;
1610                mWaitingForVoiceRecognition = false;
1611                if (!isInCall() && (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)) {
1612                    mNativeInterface.disconnectAudio(mCurrentDevice);
1613                    mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
1614                }
1615            } else {
1616                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1617            }
1618        } else {
1619            Log.e(TAG, "Bad Voice Recognition state: " + state);
1620        }
1621    }
1622
1623    private void processLocalVrEvent(int state, BluetoothDevice device1) {
1624        BluetoothDevice device = null;
1625        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
1626            boolean needAudio = true;
1627            if (mVoiceRecognitionStarted || isInCall()) {
1628                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall()
1629                        + " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
1630                return;
1631            }
1632            mVoiceRecognitionStarted = true;
1633
1634            if (mWaitingForVoiceRecognition) {
1635                device = getDeviceForMessage(START_VR_TIMEOUT);
1636                if (device == null) {
1637                    return;
1638                }
1639
1640                Log.d(TAG, "Voice recognition started successfully");
1641                mWaitingForVoiceRecognition = false;
1642                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
1643                removeMessages(START_VR_TIMEOUT);
1644            } else {
1645                Log.d(TAG, "Voice recognition started locally");
1646                needAudio = mNativeInterface.startVoiceRecognition(mCurrentDevice);
1647                if (mCurrentDevice != null) {
1648                    device = mCurrentDevice;
1649                }
1650            }
1651
1652            if (needAudio && getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1653                Log.d(TAG, "Initiating audio connection for Voice Recognition");
1654                // At this stage, we need to be sure that AVDTP is not streaming. This is needed
1655                // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
1656                // streaming state while a SCO connection is established.
1657                // This is needed for VoiceDial scenario alone and not for
1658                // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
1659                // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
1660                // Whereas for VoiceDial we want to activate the SCO connection but we are still
1661                // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
1662                mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true");
1663                if (device != null) {
1664                    mNativeInterface.connectAudio(device);
1665                } else {
1666                    Log.e(TAG, "device not found for VR");
1667                }
1668            }
1669
1670            if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1671                mSystemInterface.getVoiceRecognitionWakeLock().release();
1672            }
1673        } else {
1674            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: "
1675                    + mVoiceRecognitionStarted + " mWaitingForVoiceRecognition: "
1676                    + mWaitingForVoiceRecognition);
1677            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition) {
1678                mVoiceRecognitionStarted = false;
1679                mWaitingForVoiceRecognition = false;
1680
1681                if (mNativeInterface.stopVoiceRecognition(mCurrentDevice) && !isInCall()
1682                        && getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1683                    mNativeInterface.disconnectAudio(mCurrentDevice);
1684                    mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
1685                }
1686            }
1687        }
1688    }
1689
1690    private synchronized void expectVoiceRecognition(BluetoothDevice device) {
1691        mWaitingForVoiceRecognition = true;
1692        Message m = obtainMessage(START_VR_TIMEOUT);
1693        m.obj = getMatchingDevice(device);
1694        sendMessageDelayed(m, START_VR_TIMEOUT_VALUE);
1695
1696        if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) {
1697            mSystemInterface.getVoiceRecognitionWakeLock().acquire(START_VR_TIMEOUT_VALUE);
1698        }
1699    }
1700
1701    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1702        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
1703        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
1704        if (bondedDevices == null) {
1705            return deviceList;
1706        }
1707        synchronized (this) {
1708            for (BluetoothDevice device : bondedDevices) {
1709                ParcelUuid[] featureUuids = device.getUuids();
1710                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
1711                    continue;
1712                }
1713                int connectionState = getConnectionState(device);
1714                for (int state : states) {
1715                    if (connectionState == state) {
1716                        deviceList.add(device);
1717                    }
1718                }
1719            }
1720        }
1721        return deviceList;
1722    }
1723
1724    private BluetoothDevice getDeviceForMessage(int what) {
1725        if (what == CONNECT_TIMEOUT) {
1726            log("getDeviceForMessage: returning mTargetDevice for what=" + what);
1727            return mCurrentDevice;
1728        }
1729        if (mCurrentDevice == null) {
1730            log("getDeviceForMessage: No connected device. what=" + what);
1731            return null;
1732        }
1733        if (getHandler().hasMessages(what, mCurrentDevice)) {
1734            log("getDeviceForMessage: returning " + mCurrentDevice);
1735            return mCurrentDevice;
1736        }
1737        log("getDeviceForMessage: No matching device for " + what + ". Returning null");
1738        return null;
1739    }
1740
1741    private BluetoothDevice getMatchingDevice(BluetoothDevice device) {
1742        if (mCurrentDevice.equals(device)) {
1743            return mCurrentDevice;
1744        }
1745        return null;
1746    }
1747
1748    /*
1749     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
1750     */
1751    private void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
1752            Object[] arguments, BluetoothDevice device) {
1753        log("broadcastVendorSpecificEventIntent(" + command + ")");
1754        Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
1755        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
1756        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
1757        // assert: all elements of args are Serializable
1758        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
1759        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1760
1761        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
1762                + Integer.toString(companyId));
1763
1764        mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
1765    }
1766
1767    private void configAudioParameters(BluetoothDevice device) {
1768        // Reset NREC on connect event. Headset will override later
1769        mAudioParams.put("NREC", 1);
1770        mSystemInterface.getAudioManager()
1771                .setParameters(
1772                        HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" + HEADSET_NREC
1773                                + "=on");
1774        Log.d(TAG,
1775                "configAudioParameters for device:" + device + " are: nrec = " + mAudioParams.get(
1776                        "NREC"));
1777    }
1778
1779    private void setAudioParameters(BluetoothDevice device) {
1780        // 1. update nrec value
1781        // 2. update headset name
1782        int mNrec = 0;
1783        if (!mAudioParams.isEmpty()) {
1784            mNrec = mAudioParams.get("NREC");
1785        } else {
1786            Log.e(TAG, "setAudioParameters: audioParam not found");
1787        }
1788
1789        if (mNrec == 1) {
1790            Log.d(TAG, "Set NREC: 1 for device:" + device);
1791            mSystemInterface.getAudioManager().setParameters(HEADSET_NREC + "=on");
1792        } else {
1793            Log.d(TAG, "Set NREC: 0 for device:" + device);
1794            mSystemInterface.getAudioManager().setParameters(HEADSET_NREC + "=off");
1795        }
1796        mSystemInterface.getAudioManager()
1797                .setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device));
1798    }
1799
1800    private String parseUnknownAt(String atString) {
1801        StringBuilder atCommand = new StringBuilder(atString.length());
1802        String result = null;
1803
1804        for (int i = 0; i < atString.length(); i++) {
1805            char c = atString.charAt(i);
1806            if (c == '"') {
1807                int j = atString.indexOf('"', i + 1); // search for closing "
1808                if (j == -1) { // unmatched ", insert one.
1809                    atCommand.append(atString.substring(i, atString.length()));
1810                    atCommand.append('"');
1811                    break;
1812                }
1813                atCommand.append(atString.substring(i, j + 1));
1814                i = j;
1815            } else if (c != ' ') {
1816                atCommand.append(Character.toUpperCase(c));
1817            }
1818        }
1819        result = atCommand.toString();
1820        return result;
1821    }
1822
1823    private int getAtCommandType(String atCommand) {
1824        int commandType = AtPhonebook.TYPE_UNKNOWN;
1825        String atString = null;
1826        atCommand = atCommand.trim();
1827        if (atCommand.length() > 5) {
1828            atString = atCommand.substring(5);
1829            if (atString.startsWith("?")) { // Read
1830                commandType = AtPhonebook.TYPE_READ;
1831            } else if (atString.startsWith("=?")) { // Test
1832                commandType = AtPhonebook.TYPE_TEST;
1833            } else if (atString.startsWith("=")) { // Set
1834                commandType = AtPhonebook.TYPE_SET;
1835            } else {
1836                commandType = AtPhonebook.TYPE_UNKNOWN;
1837            }
1838        }
1839        return commandType;
1840    }
1841
1842    /* Method to check if Virtual Call in Progress */
1843    private boolean isVirtualCallInProgress() {
1844        return mVirtualCallStarted;
1845    }
1846
1847    private void setVirtualCallInProgress(boolean state) {
1848        mVirtualCallStarted = state;
1849    }
1850
1851    /* NOTE: Currently the VirtualCall API does not support handling of
1852    call transfers. If it is initiated from the handsfree device,
1853    HeadsetStateMachine will end the virtual call by calling
1854    terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
1855    private synchronized boolean initiateScoUsingVirtualVoiceCall() {
1856        log("initiateScoUsingVirtualVoiceCall: Received");
1857        // 1. Check if the SCO state is idle
1858        if (isInCall() || mVoiceRecognitionStarted) {
1859            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
1860            return false;
1861        }
1862
1863        // 2. Send virtual phone state changed to initialize SCO
1864        processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0),
1865                true);
1866        processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0),
1867                true);
1868        processCallState(new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0),
1869                true);
1870        setVirtualCallInProgress(true);
1871        // Done
1872        log("initiateScoUsingVirtualVoiceCall: Done");
1873        return true;
1874    }
1875
1876    private synchronized boolean terminateScoUsingVirtualVoiceCall() {
1877        log("terminateScoUsingVirtualVoiceCall: Received");
1878
1879        if (!isVirtualCallInProgress()) {
1880            Log.w(TAG, "terminateScoUsingVirtualVoiceCall: No present call to terminate");
1881            return false;
1882        }
1883
1884        // 2. Send virtual phone state changed to close SCO
1885        processCallState(new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0),
1886                true);
1887        setVirtualCallInProgress(false);
1888        // Done
1889        log("terminateScoUsingVirtualVoiceCall: Done");
1890        return true;
1891    }
1892
1893
1894    private void processDialCall(String number, BluetoothDevice device) {
1895        if (device == null) {
1896            Log.w(TAG, "processDialCall device is null");
1897            return;
1898        }
1899
1900        String dialNumber;
1901        if (mDialingOut) {
1902            log("processDialCall, already dialling");
1903            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1904            return;
1905        }
1906        if ((number == null) || (number.length() == 0)) {
1907            dialNumber = mPhonebook.getLastDialledNumber();
1908            if (dialNumber == null) {
1909                log("processDialCall, last dial number null");
1910                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1911                return;
1912            }
1913        } else if (number.charAt(0) == '>') {
1914            // Yuck - memory dialling requested.
1915            // Just dial last number for now
1916            if (number.startsWith(">9999")) { // for PTS test
1917                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1918                return;
1919            }
1920            log("processDialCall, memory dial do last dial for now");
1921            dialNumber = mPhonebook.getLastDialledNumber();
1922            if (dialNumber == null) {
1923                log("processDialCall, last dial number null");
1924                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1925                return;
1926            }
1927        } else {
1928            // Remove trailing ';'
1929            if (number.charAt(number.length() - 1) == ';') {
1930                number = number.substring(0, number.length() - 1);
1931            }
1932
1933            dialNumber = PhoneNumberUtils.convertPreDial(number);
1934        }
1935        // Check for virtual call to terminate before sending Call Intent
1936        terminateScoUsingVirtualVoiceCall();
1937
1938        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
1939                Uri.fromParts(SCHEME_TEL, dialNumber, null));
1940        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1941        mService.startActivity(intent);
1942        // TODO(BT) continue send OK reults code after call starts
1943        //          hold wait lock, start a timer, set wait call flag
1944        //          Get call started indication from bluetooth phone
1945        mDialingOut = true;
1946        Message m = obtainMessage(DIALING_OUT_TIMEOUT);
1947        m.obj = getMatchingDevice(device);
1948        sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE);
1949    }
1950
1951    private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) {
1952        if (!mCurrentDevice.equals(device)) {
1953            Log.w(TAG, "processVolumeEvent, ignored for unknown device " + device);
1954            return;
1955        }
1956        // When there is an active call, only device in audio focus can change SCO volume
1957        if (mSystemInterface.getHeadsetPhoneState().isInCall()
1958                && getAudioState() != BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1959            Log.w(TAG, "processVolumeEvent, ignored because " + mCurrentDevice
1960                    + " does not have audio focus");
1961        }
1962        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
1963            mSpeakerVolume = volume;
1964            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
1965            mSystemInterface.getAudioManager()
1966                    .setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
1967        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
1968            // Not used currently
1969            mMicVolume = volume;
1970        } else {
1971            Log.e(TAG, "Bad voluem type: " + volumeType);
1972        }
1973    }
1974
1975    private void processCallState(HeadsetCallState callState, boolean isVirtualCall) {
1976        mSystemInterface.getHeadsetPhoneState().setNumActiveCall(callState.mNumActive);
1977        mSystemInterface.getHeadsetPhoneState().setNumHeldCall(callState.mNumHeld);
1978        mSystemInterface.getHeadsetPhoneState().setCallState(callState.mCallState);
1979        if (mDialingOut) {
1980            if (callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) {
1981                BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT);
1982                if (device == null) {
1983                    return;
1984                }
1985                mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
1986                removeMessages(DIALING_OUT_TIMEOUT);
1987            } else if (callState.mCallState == HeadsetHalConstants.CALL_STATE_ACTIVE
1988                    || callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE) {
1989                mDialingOut = false;
1990            }
1991        }
1992
1993        log("mNumActive: " + callState.mNumActive + " mNumHeld: " + callState.mNumHeld
1994                + " mCallState: " + callState.mCallState);
1995        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
1996
1997        if (isVirtualCall) {
1998            // virtual call state update
1999            if (getCurrentState() != mDisconnected) {
2000                mNativeInterface.phoneStateChange(callState);
2001            }
2002        } else {
2003            // circuit-switch voice call update
2004            // stop virtual voice call if there is a CSV call ongoing
2005            if (callState.mNumActive > 0 || callState.mNumHeld > 0
2006                    || callState.mCallState != HeadsetHalConstants.CALL_STATE_IDLE) {
2007                terminateScoUsingVirtualVoiceCall();
2008            }
2009
2010            // Specific handling for case of starting MO/MT call while VOIP
2011            // ongoing, terminateScoUsingVirtualVoiceCall() resets callState
2012            // INCOMING/DIALING to IDLE. Some HS send AT+CIND? to read call
2013            // and get wrong value of callsetup. This case is hit only
2014            // SCO for VOIP call is not terminated via SDK API call.
2015            if (mSystemInterface.getHeadsetPhoneState().getCallState() != callState.mCallState) {
2016                mSystemInterface.getHeadsetPhoneState().setCallState(callState.mCallState);
2017            }
2018
2019            // at this step: if there is virtual call ongoing, it means there is no CSV call
2020            // let virtual call continue and skip phone state update
2021            if (!isVirtualCallInProgress()) {
2022                if (getCurrentState() != mDisconnected) {
2023                    mNativeInterface.phoneStateChange(callState);
2024                }
2025            }
2026        }
2027    }
2028
2029    private void processNoiseReductionEvent(boolean enable, BluetoothDevice device) {
2030        if (!mAudioParams.isEmpty()) {
2031            if (enable) {
2032                mAudioParams.put("NREC", 1);
2033            } else {
2034                mAudioParams.put("NREC", 0);
2035            }
2036            log("NREC value for device :" + device + " is: " + mAudioParams.get("NREC"));
2037        } else {
2038            Log.e(TAG, "processNoiseReductionEvent: audioParamNrec is null ");
2039        }
2040
2041        if (mCurrentDevice != null && mCurrentDevice.equals(device)
2042                && getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2043            setAudioParameters(device);
2044        }
2045    }
2046
2047    private void processWBSEvent(int wbsConfig) {
2048        switch (wbsConfig) {
2049            case HeadsetHalConstants.BTHF_WBS_YES:
2050                Log.d(TAG, "AudioManager.setParameters: bt_wbs=on");
2051                mSystemInterface.getAudioManager().setParameters(HEADSET_WBS + "=on");
2052                break;
2053            case HeadsetHalConstants.BTHF_WBS_NO:
2054            case HeadsetHalConstants.BTHF_WBS_NONE:
2055                Log.d(TAG, "AudioManager.setParameters: bt_wbs=off, wbsConfig=" + wbsConfig);
2056                mSystemInterface.getAudioManager().setParameters(HEADSET_WBS + "=off");
2057                break;
2058            default:
2059                Log.e(TAG, "processWBSEvent, unknown wbsConfig " + wbsConfig);
2060        }
2061    }
2062
2063    private void processAtChld(int chld, BluetoothDevice device) {
2064        if (device == null) {
2065            Log.w(TAG, "processAtChld device is null");
2066            return;
2067        }
2068        if (mSystemInterface.processChld(chld)) {
2069            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
2070        } else {
2071            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2072        }
2073    }
2074
2075    private void processSubscriberNumberRequest(BluetoothDevice device) {
2076        if (device == null) {
2077            Log.w(TAG, "processSubscriberNumberRequest device is null");
2078            return;
2079        }
2080        String number = mSystemInterface.getSubscriberNumber();
2081        if (number != null) {
2082            mNativeInterface.atResponseString(device,
2083                    "+CNUM: ,\"" + number + "\"," + PhoneNumberUtils.toaFromString(number) + ",,4");
2084            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
2085        } else {
2086            Log.e(TAG, "getSubscriberNumber returns null");
2087            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2088        }
2089    }
2090
2091    private void processAtCind(BluetoothDevice device) {
2092        int call, callSetup;
2093        if (device == null) {
2094            Log.w(TAG, "processAtCind device is null");
2095            return;
2096        }
2097        final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState();
2098
2099        /* Handsfree carkits expect that +CIND is properly responded to
2100         Hence we ensure that a proper response is sent
2101         for the virtual call too.*/
2102        if (isVirtualCallInProgress()) {
2103            call = 1;
2104            callSetup = 0;
2105        } else {
2106            // regular phone call
2107            call = phoneState.getNumActiveCall();
2108            callSetup = phoneState.getNumHeldCall();
2109        }
2110
2111        mNativeInterface.cindResponse(device, phoneState.getCindService(), call, callSetup,
2112                phoneState.getCallState(), phoneState.getCindSignal(), phoneState.getCindRoam(),
2113                phoneState.getCindBatteryCharge());
2114    }
2115
2116    private void processAtCops(BluetoothDevice device) {
2117        if (device == null) {
2118            Log.w(TAG, "processAtCops device is null");
2119            return;
2120        }
2121        String operatorName = mSystemInterface.getNetworkOperator();
2122        if (operatorName == null) {
2123            operatorName = "";
2124        }
2125        mNativeInterface.copsResponse(device, operatorName);
2126    }
2127
2128    private void processAtClcc(BluetoothDevice device) {
2129        if (device == null) {
2130            Log.w(TAG, "processAtClcc device is null");
2131            return;
2132        }
2133        if (isVirtualCallInProgress()) {
2134            // In virtual call, send our phone number instead of remote phone number
2135            String phoneNumber = mSystemInterface.getSubscriberNumber();
2136            if (phoneNumber == null) {
2137                phoneNumber = "";
2138            }
2139            int type = PhoneNumberUtils.toaFromString(phoneNumber);
2140            mNativeInterface.clccResponse(device, 1, 0, 0, 0, false, phoneNumber, type);
2141            mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
2142        } else {
2143            // In Telecom call, ask Telecom to send send remote phone number
2144            if (!mSystemInterface.listCurrentCalls()) {
2145                Log.e(TAG, "processAtClcc: failed to list current calls for " + device);
2146                mNativeInterface.clccResponse(device, 0, 0, 0, 0, false, "", 0);
2147            } else {
2148                Message m = obtainMessage(CLCC_RSP_TIMEOUT);
2149                m.obj = getMatchingDevice(device);
2150                sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE);
2151            }
2152        }
2153    }
2154
2155    private void processAtCscs(String atString, int type, BluetoothDevice device) {
2156        log("processAtCscs - atString = " + atString);
2157        if (mPhonebook != null) {
2158            mPhonebook.handleCscsCommand(atString, type, device);
2159        } else {
2160            Log.e(TAG, "Phonebook handle null for At+CSCS");
2161            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2162        }
2163    }
2164
2165    private void processAtCpbs(String atString, int type, BluetoothDevice device) {
2166        log("processAtCpbs - atString = " + atString);
2167        if (mPhonebook != null) {
2168            mPhonebook.handleCpbsCommand(atString, type, device);
2169        } else {
2170            Log.e(TAG, "Phonebook handle null for At+CPBS");
2171            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2172        }
2173    }
2174
2175    private void processAtCpbr(String atString, int type, BluetoothDevice device) {
2176        log("processAtCpbr - atString = " + atString);
2177        if (mPhonebook != null) {
2178            mPhonebook.handleCpbrCommand(atString, type, device);
2179        } else {
2180            Log.e(TAG, "Phonebook handle null for At+CPBR");
2181            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2182        }
2183    }
2184
2185    /**
2186     * Find a character ch, ignoring quoted sections.
2187     * Return input.length() if not found.
2188     */
2189    private static int findChar(char ch, String input, int fromIndex) {
2190        for (int i = fromIndex; i < input.length(); i++) {
2191            char c = input.charAt(i);
2192            if (c == '"') {
2193                i = input.indexOf('"', i + 1);
2194                if (i == -1) {
2195                    return input.length();
2196                }
2197            } else if (c == ch) {
2198                return i;
2199            }
2200        }
2201        return input.length();
2202    }
2203
2204    /**
2205     * Break an argument string into individual arguments (comma delimited).
2206     * Integer arguments are turned into Integer objects. Otherwise a String
2207     * object is used.
2208     */
2209    private static Object[] generateArgs(String input) {
2210        int i = 0;
2211        int j;
2212        ArrayList<Object> out = new ArrayList<Object>();
2213        while (i <= input.length()) {
2214            j = findChar(',', input, i);
2215
2216            String arg = input.substring(i, j);
2217            try {
2218                out.add(new Integer(arg));
2219            } catch (NumberFormatException e) {
2220                out.add(arg);
2221            }
2222
2223            i = j + 1; // move past comma
2224        }
2225        return out.toArray();
2226    }
2227
2228    /**
2229     * Process vendor specific AT commands
2230     *
2231     * @param atString AT command after the "AT+" prefix
2232     * @param device Remote device that has sent this command
2233     */
2234    private void processVendorSpecificAt(String atString, BluetoothDevice device) {
2235        log("processVendorSpecificAt - atString = " + atString);
2236
2237        // Currently we accept only SET type commands.
2238        int indexOfEqual = atString.indexOf("=");
2239        if (indexOfEqual == -1) {
2240            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2241            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2242            return;
2243        }
2244
2245        String command = atString.substring(0, indexOfEqual);
2246        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
2247        if (companyId == null) {
2248            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
2249            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2250            return;
2251        }
2252
2253        String arg = atString.substring(indexOfEqual + 1);
2254        if (arg.startsWith("?")) {
2255            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2256            mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
2257            return;
2258        }
2259
2260        Object[] args = generateArgs(arg);
2261        if (command.equals(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XAPL)) {
2262            processAtXapl(args, device);
2263        }
2264        broadcastVendorSpecificEventIntent(command, companyId, BluetoothHeadset.AT_CMD_TYPE_SET,
2265                args, device);
2266        mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
2267    }
2268
2269    /**
2270     * Process AT+XAPL AT command
2271     *
2272     * @param args command arguments after the equal sign
2273     * @param device Remote device that has sent this command
2274     */
2275    private void processAtXapl(Object[] args, BluetoothDevice device) {
2276        if (args.length != 2) {
2277            Log.w(TAG, "processAtXapl() args length must be 2: " + String.valueOf(args.length));
2278            return;
2279        }
2280        if (!(args[0] instanceof String) || !(args[1] instanceof Integer)) {
2281            Log.w(TAG, "processAtXapl() argument types not match");
2282            return;
2283        }
2284        // feature = 2 indicates that we support battery level reporting only
2285        mNativeInterface.atResponseString(device, "+XAPL=iPhone," + String.valueOf(2));
2286    }
2287
2288    private void processUnknownAt(String atString, BluetoothDevice device) {
2289        if (device == null) {
2290            Log.w(TAG, "processUnknownAt device is null");
2291            return;
2292        }
2293        log("processUnknownAt - atString = " + atString);
2294        String atCommand = parseUnknownAt(atString);
2295        int commandType = getAtCommandType(atCommand);
2296        if (atCommand.startsWith("+CSCS")) {
2297            processAtCscs(atCommand.substring(5), commandType, device);
2298        } else if (atCommand.startsWith("+CPBS")) {
2299            processAtCpbs(atCommand.substring(5), commandType, device);
2300        } else if (atCommand.startsWith("+CPBR")) {
2301            processAtCpbr(atCommand.substring(5), commandType, device);
2302        } else {
2303            processVendorSpecificAt(atCommand, device);
2304        }
2305    }
2306
2307    private void processKeyPressed(BluetoothDevice device) {
2308        if (device == null) {
2309            Log.w(TAG, "processKeyPressed device is null");
2310            return;
2311        }
2312        final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState();
2313        if (phoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
2314            mSystemInterface.answerCall(device);
2315        } else if (phoneState.getNumActiveCall() > 0) {
2316            if (getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2317                mNativeInterface.connectAudio(mCurrentDevice);
2318            } else {
2319                mSystemInterface.hangupCall(device, false);
2320            }
2321        } else {
2322            String dialNumber = mPhonebook.getLastDialledNumber();
2323            if (dialNumber == null) {
2324                log("processKeyPressed, last dial number null");
2325                return;
2326            }
2327            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
2328                    Uri.fromParts(SCHEME_TEL, dialNumber, null));
2329            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2330            mService.startActivity(intent);
2331        }
2332    }
2333
2334    /**
2335     * Send HF indicator value changed intent
2336     *
2337     * @param device Device whose HF indicator value has changed
2338     * @param indId Indicator ID [0-65535]
2339     * @param indValue Indicator Value [0-65535], -1 means invalid but indId is supported
2340     */
2341    private void sendIndicatorIntent(BluetoothDevice device, int indId, int indValue) {
2342        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
2343        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2344        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId);
2345        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue);
2346
2347        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
2348    }
2349
2350    private void processAtBind(String atString, BluetoothDevice device) {
2351        log("processAtBind: " + atString);
2352
2353        // Parse the AT String to find the Indicator Ids that are supported
2354        int indId = 0;
2355        int iter = 0;
2356        int iter1 = 0;
2357
2358        while (iter < atString.length()) {
2359            iter1 = findChar(',', atString, iter);
2360            String id = atString.substring(iter, iter1);
2361
2362            try {
2363                indId = Integer.valueOf(id);
2364            } catch (NumberFormatException e) {
2365                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2366            }
2367
2368            switch (indId) {
2369                case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY:
2370                    log("Send Broadcast intent for the Enhanced Driver Safety indicator.");
2371                    sendIndicatorIntent(device, indId, -1);
2372                    break;
2373                case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS:
2374                    log("Send Broadcast intent for the Battery Level indicator.");
2375                    sendIndicatorIntent(device, indId, -1);
2376                    break;
2377                default:
2378                    log("Invalid HF Indicator Received");
2379                    break;
2380            }
2381
2382            iter = iter1 + 1; // move past comma
2383        }
2384    }
2385
2386    private void processAtBiev(int indId, int indValue, BluetoothDevice device) {
2387        log("processAtBiev: ind_id=" + indId + ", ind_value=" + indValue);
2388        sendIndicatorIntent(device, indId, indValue);
2389    }
2390
2391    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
2392        mNativeInterface.notifyDeviceStatus(deviceState);
2393    }
2394
2395    private void processSendClccResponse(HeadsetClccResponse clcc) {
2396        BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT);
2397        if (device == null) {
2398            return;
2399        }
2400        if (clcc.mIndex == 0) {
2401            removeMessages(CLCC_RSP_TIMEOUT);
2402        }
2403        mNativeInterface.clccResponse(device, clcc.mIndex, clcc.mDirection, clcc.mStatus,
2404                clcc.mMode, clcc.mMpty, clcc.mNumber, clcc.mType);
2405    }
2406
2407    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
2408        String stringToSend = resultCode.mCommand + ": ";
2409        if (resultCode.mArg != null) {
2410            stringToSend += resultCode.mArg;
2411        }
2412        mNativeInterface.atResponseString(resultCode.mDevice, stringToSend);
2413    }
2414
2415    private String getCurrentDeviceName(BluetoothDevice device) {
2416        String defaultName = "<unknown>";
2417
2418        if (device == null) {
2419            return defaultName;
2420        }
2421
2422        String deviceName = device.getName();
2423        if (deviceName == null) {
2424            return defaultName;
2425        }
2426        return deviceName;
2427    }
2428
2429    private boolean isInCall() {
2430        final HeadsetPhoneState phoneState = mSystemInterface.getHeadsetPhoneState();
2431        return ((phoneState.getNumActiveCall() > 0) || (phoneState.getNumHeldCall() > 0) || (
2432                (phoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE) && (
2433                        phoneState.getCallState() != HeadsetHalConstants.CALL_STATE_INCOMING)));
2434    }
2435
2436    private boolean isRinging() {
2437        return mSystemInterface.getHeadsetPhoneState().getCallState()
2438                == HeadsetHalConstants.CALL_STATE_INCOMING;
2439    }
2440
2441    // Accept incoming SCO only when there is in-band ringing, incoming call,
2442    // active call, VR activated, active VOIP call
2443    private boolean isScoAcceptable() {
2444        if (mForceScoAudio) {
2445            return true;
2446        }
2447        if (!mService.getAudioRouteAllowed()) {
2448            return false;
2449        }
2450        if (isInCall() || mVoiceRecognitionStarted) {
2451            return true;
2452        }
2453        if (isRinging() && mService.isInbandRingingEnabled()) {
2454            return true;
2455        }
2456        return false;
2457    }
2458
2459    private boolean okToAcceptConnection(BluetoothDevice device) {
2460        AdapterService adapterService = AdapterService.getAdapterService();
2461        // check if this is an incoming connection in Quiet mode.
2462        if (adapterService == null) {
2463            Log.e(TAG, "okToAcceptConnection, cannot get adapterService");
2464            return false;
2465        }
2466        if (adapterService.isQuietModeEnabled() && mCurrentDevice == null) {
2467            Log.i(TAG, "okToAcceptConnection, quiet mode enabled and current device is null");
2468            return false;
2469        }
2470        // check priority and accept or reject the connection. if priority is undefined
2471        // it is likely that our SDP has not completed and peer is initiating the
2472        // connection. Allow this connection, provided the device is bonded
2473        int priority = mService.getPriority(device);
2474        int bondState = device.getBondState();
2475        if ((priority > BluetoothProfile.PRIORITY_OFF) || (
2476                (priority == BluetoothProfile.PRIORITY_UNDEFINED)
2477                        && bondState != BluetoothDevice.BOND_NONE)) {
2478            return true;
2479        }
2480        Log.i(TAG, "okToAcceptConnection, rejected, priority=" + priority + ", bondState="
2481                + bondState);
2482        return false;
2483    }
2484
2485    @Override
2486    protected void log(String msg) {
2487        if (DBG) {
2488            super.log(msg);
2489        }
2490    }
2491
2492    private void handleAccessPermissionResult(Intent intent) {
2493        log("handleAccessPermissionResult");
2494        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2495        if (!mPhonebook.getCheckingAccessPermission()) {
2496            return;
2497        }
2498        int atCommandResult = 0;
2499        int atCommandErrorCode = 0;
2500        // HeadsetBase headset = mHandsfree.getHeadset();
2501        // ASSERT: (headset != null) && headSet.isConnected()
2502        // REASON: mCheckingAccessPermission is true, otherwise resetAtState
2503        // has set mCheckingAccessPermission to false
2504        if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
2505            if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
2506                    BluetoothDevice.CONNECTION_ACCESS_NO)
2507                    == BluetoothDevice.CONNECTION_ACCESS_YES) {
2508                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
2509                    mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
2510                }
2511                atCommandResult = mPhonebook.processCpbrCommand(device);
2512            } else {
2513                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
2514                    mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
2515                }
2516            }
2517        }
2518        mPhonebook.setCpbrIndex(-1);
2519        mPhonebook.setCheckingAccessPermission(false);
2520        if (atCommandResult >= 0) {
2521            mNativeInterface.atResponseCode(device, atCommandResult, atCommandErrorCode);
2522        } else {
2523            log("handleAccessPermissionResult - RESULT_NONE");
2524        }
2525    }
2526
2527    static String getMessageName(int what) {
2528        switch (what) {
2529            case CONNECT:
2530                return "CONNECT";
2531            case DISCONNECT:
2532                return "DISCONNECT";
2533            case CONNECT_AUDIO:
2534                return "CONNECT_AUDIO";
2535            case DISCONNECT_AUDIO:
2536                return "DISCONNECT_AUDIO";
2537            case VOICE_RECOGNITION_START:
2538                return "VOICE_RECOGNITION_START";
2539            case VOICE_RECOGNITION_STOP:
2540                return "VOICE_RECOGNITION_STOP";
2541            case INTENT_SCO_VOLUME_CHANGED:
2542                return "INTENT_SCO_VOLUME_CHANGED";
2543            case INTENT_CONNECTION_ACCESS_REPLY:
2544                return "INTENT_CONNECTION_ACCESS_REPLY";
2545            case CALL_STATE_CHANGED:
2546                return "CALL_STATE_CHANGED";
2547            case DEVICE_STATE_CHANGED:
2548                return "DEVICE_STATE_CHANGED";
2549            case SEND_CCLC_RESPONSE:
2550                return "SEND_CCLC_RESPONSE";
2551            case SEND_VENDOR_SPECIFIC_RESULT_CODE:
2552                return "SEND_VENDOR_SPECIFIC_RESULT_CODE";
2553            case VIRTUAL_CALL_START:
2554                return "VIRTUAL_CALL_START";
2555            case VIRTUAL_CALL_STOP:
2556                return "VIRTUAL_CALL_STOP";
2557            case STACK_EVENT:
2558                return "STACK_EVENT";
2559            case DIALING_OUT_TIMEOUT:
2560                return "DIALING_OUT_TIMEOUT";
2561            case START_VR_TIMEOUT:
2562                return "START_VR_TIMEOUT";
2563            case CLCC_RSP_TIMEOUT:
2564                return "CLCC_RSP_TIMEOUT";
2565            case CONNECT_TIMEOUT:
2566                return "CONNECT_TIMEOUT";
2567            default:
2568                return "UNKNOWN";
2569        }
2570    }
2571}
2572