HeadsetStateMachine.java revision fc9bed1b79edf77082f5baa21aa68d791a102ea4
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * Bluetooth Handset StateMachine
19 *                      (Disconnected)
20 *                           |    ^
21 *                   CONNECT |    | DISCONNECTED
22 *                           V    |
23 *                         (Pending)
24 *                           |    ^
25 *                 CONNECTED |    | CONNECT
26 *                           V    |
27 *                        (Connected)
28 *                           |    ^
29 *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
30 *                           V    |
31 *                         (AudioOn)
32 */
33package com.android.bluetooth.hfp;
34
35import android.bluetooth.BluetoothAdapter;
36import android.bluetooth.BluetoothAssignedNumbers;
37import android.bluetooth.BluetoothDevice;
38import android.bluetooth.BluetoothHeadset;
39import android.bluetooth.BluetoothProfile;
40import android.bluetooth.BluetoothUuid;
41import android.bluetooth.IBluetooth;
42import android.bluetooth.IBluetoothHeadsetPhone;
43import android.content.ComponentName;
44import android.content.Context;
45import android.content.Intent;
46import android.content.ServiceConnection;
47import android.content.ActivityNotFoundException;
48import android.media.AudioManager;
49import android.net.Uri;
50import android.os.IBinder;
51import android.os.Message;
52import android.os.ParcelUuid;
53import android.os.RemoteException;
54import android.os.ServiceManager;
55import android.os.PowerManager;
56import android.os.PowerManager.WakeLock;
57import android.telephony.PhoneNumberUtils;
58import android.util.Log;
59import com.android.bluetooth.Utils;
60import com.android.bluetooth.btservice.AdapterService;
61import com.android.internal.util.IState;
62import com.android.internal.util.State;
63import com.android.internal.util.StateMachine;
64import java.util.ArrayList;
65import java.util.HashMap;
66import java.util.List;
67import java.util.Map;
68import java.util.Set;
69import android.os.SystemProperties;
70
71final class HeadsetStateMachine extends StateMachine {
72    private static final String TAG = "HeadsetStateMachine";
73    private static final boolean DBG = true;
74    //For Debugging only
75    private static int sRefCount=0;
76
77    private static final String HEADSET_NAME = "bt_headset_name";
78    private static final String HEADSET_NREC = "bt_headset_nrec";
79    private static final String HEADSET_WBS = "bt_wbs";
80
81    static final int CONNECT = 1;
82    static final int DISCONNECT = 2;
83    static final int CONNECT_AUDIO = 3;
84    static final int DISCONNECT_AUDIO = 4;
85    static final int VOICE_RECOGNITION_START = 5;
86    static final int VOICE_RECOGNITION_STOP = 6;
87
88    // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
89    // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
90    static final int INTENT_SCO_VOLUME_CHANGED = 7;
91    static final int SET_MIC_VOLUME = 8;
92    static final int CALL_STATE_CHANGED = 9;
93    static final int INTENT_BATTERY_CHANGED = 10;
94    static final int DEVICE_STATE_CHANGED = 11;
95    static final int SEND_CCLC_RESPONSE = 12;
96    static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
97
98    static final int VIRTUAL_CALL_START = 14;
99    static final int VIRTUAL_CALL_STOP = 15;
100
101    static final int ENABLE_WBS = 16;
102    static final int DISABLE_WBS = 17;
103
104
105    private 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
116    // Max number of HF connections at any time
117    private int max_hf_connections = 1;
118
119    private static final int NBS_CODEC = 1;
120    private static final int WBS_CODEC = 2;
121
122    // Keys are AT commands, and values are the company IDs.
123    private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
124    // Hash for storing the Audio Parameters like NREC for connected headsets
125    private HashMap<BluetoothDevice, HashMap> mHeadsetAudioParam =
126                                          new HashMap<BluetoothDevice, HashMap>();
127    // Hash for storing the Remotedevice BRSF
128    private HashMap<BluetoothDevice, Integer> mHeadsetBrsf =
129                                          new HashMap<BluetoothDevice, Integer>();
130
131    private static final ParcelUuid[] HEADSET_UUIDS = {
132        BluetoothUuid.HSP,
133        BluetoothUuid.Handsfree,
134    };
135
136    private Disconnected mDisconnected;
137    private Pending mPending;
138    private Connected mConnected;
139    private AudioOn mAudioOn;
140    // Multi HFP: add new class object
141    private MultiHFPending mMultiHFPending;
142
143    private HeadsetService mService;
144    private PowerManager mPowerManager;
145    private boolean mVirtualCallStarted = false;
146    private boolean mVoiceRecognitionStarted = false;
147    private boolean mWaitingForVoiceRecognition = false;
148    private WakeLock mStartVoiceRecognitionWakeLock;  // held while waiting for voice recognition
149
150    private boolean mDialingOut = false;
151    private AudioManager mAudioManager;
152    private AtPhonebook mPhonebook;
153
154    private static Intent sVoiceCommandIntent;
155
156    private HeadsetPhoneState mPhoneState;
157    private int mAudioState;
158    private BluetoothAdapter mAdapter;
159    private IBluetoothHeadsetPhone mPhoneProxy;
160    private boolean mNativeAvailable;
161
162    // mCurrentDevice is the device connected before the state changes
163    // mTargetDevice is the device to be connected
164    // mIncomingDevice is the device connecting to us, valid only in Pending state
165    //                when mIncomingDevice is not null, both mCurrentDevice
166    //                  and mTargetDevice are null
167    //                when either mCurrentDevice or mTargetDevice is not null,
168    //                  mIncomingDevice is null
169    // Stable states
170    //   No connection, Disconnected state
171    //                  both mCurrentDevice and mTargetDevice are null
172    //   Connected, Connected state
173    //              mCurrentDevice is not null, mTargetDevice is null
174    // Interim states
175    //   Connecting to a device, Pending
176    //                           mCurrentDevice is null, mTargetDevice is not null
177    //   Disconnecting device, Connecting to new device
178    //     Pending
179    //     Both mCurrentDevice and mTargetDevice are not null
180    //   Disconnecting device Pending
181    //                        mCurrentDevice is not null, mTargetDevice is null
182    //   Incoming connections Pending
183    //                        Both mCurrentDevice and mTargetDevice are null
184    private BluetoothDevice mCurrentDevice = null;
185    private BluetoothDevice mTargetDevice = null;
186    private BluetoothDevice mIncomingDevice = null;
187    private BluetoothDevice mActiveScoDevice = null;
188    private BluetoothDevice mMultiDisconnectDevice = null;
189
190    // Multi HFP: Connected devices list holds all currently connected headsets
191    private ArrayList<BluetoothDevice> mConnectedDevicesList =
192                                             new ArrayList<BluetoothDevice>();
193
194    static {
195        classInitNative();
196
197        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>();
198        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS);
199        VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE);
200    }
201
202    private HeadsetStateMachine(HeadsetService context) {
203        super(TAG);
204        mService = context;
205        mVoiceRecognitionStarted = false;
206        mWaitingForVoiceRecognition = false;
207
208        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
209        mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
210                                                       TAG + ":VoiceRecognition");
211        mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
212
213        mDialingOut = false;
214        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
215        mPhonebook = new AtPhonebook(mService, this);
216        mPhoneState = new HeadsetPhoneState(context, this);
217        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
218        mAdapter = BluetoothAdapter.getDefaultAdapter();
219        Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
220        intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
221        if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
222            Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
223        }
224
225        String max_hfp_clients = SystemProperties.get("bt.max.hfpclient.connections");
226        if (!max_hfp_clients.isEmpty() && (Integer.parseInt(max_hfp_clients) == 2))
227            max_hf_connections = Integer.parseInt(max_hfp_clients);
228        Log.d(TAG, "max_hf_connections = " + max_hf_connections);
229        initializeNative(max_hf_connections);
230        mNativeAvailable=true;
231
232        mDisconnected = new Disconnected();
233        mPending = new Pending();
234        mConnected = new Connected();
235        mAudioOn = new AudioOn();
236        // Multi HFP: initialise new class variable
237        mMultiHFPending = new MultiHFPending();
238
239        if (sVoiceCommandIntent == null) {
240            sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
241            sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
242        }
243
244        addState(mDisconnected);
245        addState(mPending);
246        addState(mConnected);
247        addState(mAudioOn);
248        // Multi HFP: add State
249        addState(mMultiHFPending);
250
251        setInitialState(mDisconnected);
252    }
253
254    static HeadsetStateMachine make(HeadsetService context) {
255        Log.d(TAG, "make");
256        HeadsetStateMachine hssm = new HeadsetStateMachine(context);
257        hssm.start();
258        return hssm;
259    }
260
261
262    public void doQuit() {
263        quitNow();
264    }
265
266    public void cleanup() {
267        if (mPhoneProxy != null) {
268            if (DBG) Log.d(TAG,"Unbinding service...");
269            synchronized (mConnection) {
270                try {
271                    mPhoneProxy = null;
272                    mService.unbindService(mConnection);
273                } catch (Exception re) {
274                    Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re);
275                }
276            }
277        }
278        if (mPhoneState != null) {
279            mPhoneState.listenForPhoneState(false);
280            mPhoneState.cleanup();
281        }
282        if (mPhonebook != null) {
283            mPhonebook.cleanup();
284        }
285        if (mHeadsetAudioParam != null) {
286            mHeadsetAudioParam.clear();
287        }
288        if (mHeadsetBrsf != null) {
289            mHeadsetBrsf.clear();
290        }
291        if (mConnectedDevicesList != null) {
292            mConnectedDevicesList.clear();
293        }
294        if (mNativeAvailable) {
295            cleanupNative();
296            mNativeAvailable = false;
297        }
298    }
299
300    private class Disconnected extends State {
301        @Override
302        public void enter() {
303            log("Enter Disconnected: " + getCurrentMessage().what +
304                                ", size: " + mConnectedDevicesList.size());
305            mPhonebook.resetAtState();
306            mPhoneState.listenForPhoneState(false);
307            mVoiceRecognitionStarted = false;
308            mWaitingForVoiceRecognition = false;
309        }
310
311        @Override
312        public boolean processMessage(Message message) {
313            log("Disconnected process message: " + message.what +
314                                ", size: " + mConnectedDevicesList.size());
315            if (mConnectedDevicesList.size() != 0 || mTargetDevice != null ||
316                                mIncomingDevice != null) {
317                Log.e(TAG, "ERROR: mConnectedDevicesList is not empty," +
318                       "target, or mIncomingDevice not null in Disconnected");
319                return NOT_HANDLED;
320            }
321
322            boolean retValue = HANDLED;
323            switch(message.what) {
324                case CONNECT:
325                    BluetoothDevice device = (BluetoothDevice) message.obj;
326                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
327                                   BluetoothProfile.STATE_DISCONNECTED);
328
329                    if (!connectHfpNative(getByteAddress(device)) ) {
330                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
331                                       BluetoothProfile.STATE_CONNECTING);
332                        break;
333                    }
334
335                    synchronized (HeadsetStateMachine.this) {
336                        mTargetDevice = device;
337                        transitionTo(mPending);
338                    }
339                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
340                    //          sends back events consistently
341                    Message m = obtainMessage(CONNECT_TIMEOUT);
342                    m.obj = device;
343                    sendMessageDelayed(m, 30000);
344                    break;
345                case DISCONNECT:
346                    // ignore
347                    break;
348                case INTENT_BATTERY_CHANGED:
349                    processIntentBatteryChanged((Intent) message.obj);
350                    break;
351                case CALL_STATE_CHANGED:
352                    processCallState((HeadsetCallState) message.obj,
353                        ((message.arg1 == 1)?true:false));
354                    break;
355                case STACK_EVENT:
356                    StackEvent event = (StackEvent) message.obj;
357                    if (DBG) {
358                        log("event type: " + event.type);
359                    }
360                    switch (event.type) {
361                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
362                            processConnectionEvent(event.valueInt, event.device);
363                            break;
364                        default:
365                            Log.e(TAG, "Unexpected stack event: " + event.type);
366                            break;
367                    }
368                    break;
369                default:
370                    return NOT_HANDLED;
371            }
372            return retValue;
373        }
374
375        @Override
376        public void exit() {
377            log("Exit Disconnected: " + getCurrentMessage().what);
378        }
379
380        // in Disconnected state
381        private void processConnectionEvent(int state, BluetoothDevice device) {
382            Log.d(TAG, "processConnectionEvent state = " + state +
383                             ", device = " + device);
384            switch (state) {
385            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
386                Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
387                break;
388            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
389                if (okToConnect(device)) {
390                    Log.i(TAG,"Incoming Hf accepted");
391                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
392                                             BluetoothProfile.STATE_DISCONNECTED);
393                    synchronized (HeadsetStateMachine.this) {
394                        mIncomingDevice = device;
395                        transitionTo(mPending);
396                    }
397                } else {
398                    Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device)+
399                              " bondState=" + device.getBondState());
400                    //reject the connection and stay in Disconnected state itself
401                    disconnectHfpNative(getByteAddress(device));
402                    // the other profile connection should be initiated
403                    AdapterService adapterService = AdapterService.getAdapterService();
404                    if (adapterService != null) {
405                        adapterService.connectOtherProfile(device,
406                                                           AdapterService.PROFILE_CONN_REJECTED);
407                    }
408                }
409                break;
410            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
411                Log.w(TAG, "HFP Connected from Disconnected state");
412                if (okToConnect(device)) {
413                    Log.i(TAG,"Incoming Hf accepted");
414                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
415                                             BluetoothProfile.STATE_DISCONNECTED);
416                    synchronized (HeadsetStateMachine.this) {
417                        if (!mConnectedDevicesList.contains(device)) {
418                            mConnectedDevicesList.add(device);
419                            Log.d(TAG, "device " + device.getAddress() +
420                                          " is adding in Disconnected state");
421                        }
422                        mCurrentDevice = device;
423                        transitionTo(mConnected);
424                    }
425                    configAudioParameters(device);
426                } else {
427                    //reject the connection and stay in Disconnected state itself
428                    Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device) +
429                              " bondState=" + device.getBondState());
430                    disconnectHfpNative(getByteAddress(device));
431                    // the other profile connection should be initiated
432                    AdapterService adapterService = AdapterService.getAdapterService();
433                    if (adapterService != null) {
434                        adapterService.connectOtherProfile(device,
435                                                           AdapterService.PROFILE_CONN_REJECTED);
436                    }
437                }
438                break;
439            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
440                Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
441                break;
442            default:
443                Log.e(TAG, "Incorrect state: " + state);
444                break;
445            }
446        }
447    }
448
449    private class Pending extends State {
450        @Override
451        public void enter() {
452            log("Enter Pending: " + getCurrentMessage().what);
453        }
454
455        @Override
456        public boolean processMessage(Message message) {
457            log("Pending process message: " + message.what + ", size: "
458                                        + mConnectedDevicesList.size());
459
460            boolean retValue = HANDLED;
461            switch(message.what) {
462                case CONNECT:
463                case CONNECT_AUDIO:
464                    deferMessage(message);
465                    break;
466                case CONNECT_TIMEOUT:
467                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
468                                             getByteAddress(mTargetDevice));
469                    break;
470                case DISCONNECT:
471                    BluetoothDevice device = (BluetoothDevice) message.obj;
472                    if (mCurrentDevice != null && mTargetDevice != null &&
473                        mTargetDevice.equals(device) ) {
474                        // cancel connection to the mTargetDevice
475                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
476                                       BluetoothProfile.STATE_CONNECTING);
477                        synchronized (HeadsetStateMachine.this) {
478                            mTargetDevice = null;
479                        }
480                    } else {
481                        deferMessage(message);
482                    }
483                    break;
484                case INTENT_BATTERY_CHANGED:
485                    processIntentBatteryChanged((Intent) message.obj);
486                    break;
487                case CALL_STATE_CHANGED:
488                    processCallState((HeadsetCallState) message.obj,
489                        ((message.arg1 == 1)?true:false));
490                    break;
491                case STACK_EVENT:
492                    StackEvent event = (StackEvent) message.obj;
493                    if (DBG) {
494                        log("event type: " + event.type);
495                    }
496                    switch (event.type) {
497                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
498                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
499                            if (device1 != null && device1.equals(event.device)) {
500                                Log.d(TAG, "remove connect timeout for device = " + device1);
501                                removeMessages(CONNECT_TIMEOUT);
502                            }
503                            processConnectionEvent(event.valueInt, event.device);
504                            break;
505                        default:
506                            Log.e(TAG, "Unexpected event: " + event.type);
507                            break;
508                    }
509                    break;
510                default:
511                    return NOT_HANDLED;
512            }
513            return retValue;
514        }
515
516        // in Pending state
517        private void processConnectionEvent(int state, BluetoothDevice device) {
518            Log.d(TAG, "processConnectionEvent state = " + state +
519                                              ", device = " + device);
520            switch (state) {
521                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
522                    if (mConnectedDevicesList.contains(device)) {
523
524                        synchronized (HeadsetStateMachine.this) {
525                            mConnectedDevicesList.remove(device);
526                            mHeadsetAudioParam.remove(device);
527                            mHeadsetBrsf.remove(device);
528                            Log.d(TAG, "device " + device.getAddress() +
529                                             " is removed in Pending state");
530                        }
531
532                        broadcastConnectionState(device,
533                                                 BluetoothProfile.STATE_DISCONNECTED,
534                                                 BluetoothProfile.STATE_DISCONNECTING);
535                        synchronized (HeadsetStateMachine.this) {
536                            mCurrentDevice = null;
537                        }
538
539                        processWBSEvent(0, device); /* disable WBS audio parameters */
540
541                        if (mTargetDevice != null) {
542                            if (!connectHfpNative(getByteAddress(mTargetDevice))) {
543                                broadcastConnectionState(mTargetDevice,
544                                                         BluetoothProfile.STATE_DISCONNECTED,
545                                                         BluetoothProfile.STATE_CONNECTING);
546                                synchronized (HeadsetStateMachine.this) {
547                                    mTargetDevice = null;
548                                    transitionTo(mDisconnected);
549                                }
550                            }
551                        } else {
552                            synchronized (HeadsetStateMachine.this) {
553                                mIncomingDevice = null;
554                                if (mConnectedDevicesList.size() == 0) {
555                                    transitionTo(mDisconnected);
556                                }
557                                else {
558                                    processMultiHFConnected(device);
559                                }
560                            }
561                        }
562                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
563                        // outgoing connection failed
564                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
565                                                 BluetoothProfile.STATE_CONNECTING);
566                        synchronized (HeadsetStateMachine.this) {
567                            mTargetDevice = null;
568                            if (mConnectedDevicesList.size() == 0) {
569                                transitionTo(mDisconnected);
570                            }
571                            else {
572                                transitionTo(mConnected);
573                            }
574
575                        }
576                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
577                        broadcastConnectionState(mIncomingDevice,
578                                                 BluetoothProfile.STATE_DISCONNECTED,
579                                                 BluetoothProfile.STATE_CONNECTING);
580                        synchronized (HeadsetStateMachine.this) {
581                            mIncomingDevice = null;
582                            if (mConnectedDevicesList.size() == 0) {
583                                transitionTo(mDisconnected);
584                            }
585                            else {
586                                transitionTo(mConnected);
587                            }
588                        }
589                    } else {
590                        Log.e(TAG, "Unknown device Disconnected: " + device);
591                    }
592                    break;
593                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
594                    if (mConnectedDevicesList.contains(device)) {
595                         // disconnection failed
596                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
597                                             BluetoothProfile.STATE_DISCONNECTING);
598                        if (mTargetDevice != null) {
599                            broadcastConnectionState(mTargetDevice,
600                                                 BluetoothProfile.STATE_DISCONNECTED,
601                                                 BluetoothProfile.STATE_CONNECTING);
602                        }
603                        synchronized (HeadsetStateMachine.this) {
604                            mTargetDevice = null;
605                            transitionTo(mConnected);
606                        }
607                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
608
609                        synchronized (HeadsetStateMachine.this) {
610                            mCurrentDevice = device;
611                            mConnectedDevicesList.add(device);
612                            Log.d(TAG, "device " + device.getAddress() +
613                                         " is added in Pending state");
614                            mTargetDevice = null;
615                            transitionTo(mConnected);
616                        }
617                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
618                                             BluetoothProfile.STATE_CONNECTING);
619                        configAudioParameters(device);
620                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
621
622                        synchronized (HeadsetStateMachine.this) {
623                            mCurrentDevice = device;
624                            mConnectedDevicesList.add(device);
625                            Log.d(TAG, "device " + device.getAddress() +
626                                             " is added in Pending state");
627                            mIncomingDevice = null;
628                            transitionTo(mConnected);
629                        }
630                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
631                                             BluetoothProfile.STATE_CONNECTING);
632                        configAudioParameters(device);
633                    } else {
634                        Log.w(TAG, "Some other incoming HF connected in Pending state");
635                        if (okToConnect(device)) {
636                            Log.i(TAG,"Incoming Hf accepted");
637                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
638                                                     BluetoothProfile.STATE_DISCONNECTED);
639                            synchronized (HeadsetStateMachine.this) {
640                                mCurrentDevice = device;
641                                mConnectedDevicesList.add(device);
642                                Log.d(TAG, "device " + device.getAddress() +
643                                             " is added in Pending state");
644                            }
645                            configAudioParameters(device);
646                        } else {
647                            //reject the connection and stay in Pending state itself
648                            Log.i(TAG,"Incoming Hf rejected. priority=" +
649                                mService.getPriority(device) + " bondState=" +
650                                               device.getBondState());
651                            disconnectHfpNative(getByteAddress(device));
652                            // the other profile connection should be initiated
653                            AdapterService adapterService = AdapterService.getAdapterService();
654                            if (adapterService != null) {
655                                adapterService.connectOtherProfile(device,
656                                         AdapterService.PROFILE_CONN_REJECTED);
657                            }
658                        }
659                    }
660                    break;
661                case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
662                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
663                        log("current device tries to connect back");
664                        // TODO(BT) ignore or reject
665                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
666                        // The stack is connecting to target device or
667                        // there is an incoming connection from the target device at the same time
668                        // we already broadcasted the intent, doing nothing here
669                        if (DBG) {
670                            log("Stack and target device are connecting");
671                        }
672                    }
673                    else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
674                        Log.e(TAG, "Another connecting event on the incoming device");
675                    } else {
676                        // We get an incoming connecting request while Pending
677                        // TODO(BT) is stack handing this case? let's ignore it for now
678                        log("Incoming connection while pending, ignore");
679                    }
680                    break;
681                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
682                    if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
683                        // we already broadcasted the intent, doing nothing here
684                        if (DBG) {
685                            log("stack is disconnecting mCurrentDevice");
686                        }
687                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
688                        Log.e(TAG, "TargetDevice is getting disconnected");
689                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
690                        Log.e(TAG, "IncomingDevice is getting disconnected");
691                    } else {
692                        Log.e(TAG, "Disconnecting unknow device: " + device);
693                    }
694                    break;
695                default:
696                    Log.e(TAG, "Incorrect state: " + state);
697                    break;
698            }
699        }
700
701        private void processMultiHFConnected(BluetoothDevice device) {
702            log("Pending state: processMultiHFConnected");
703            /* Assign the current activedevice again if the disconnected
704                         device equals to the current active device*/
705            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
706                transitionTo(mConnected);
707                int deviceSize = mConnectedDevicesList.size();
708                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
709            } else {
710                // The disconnected device is not current active device
711                if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
712                    transitionTo(mAudioOn);
713                else transitionTo(mConnected);
714            }
715            log("processMultiHFConnected , the latest mCurrentDevice is:"
716                                             + mCurrentDevice);
717            log("Pending state: processMultiHFConnected ," +
718                           "fake broadcasting for mCurrentDevice");
719            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
720                                         BluetoothProfile.STATE_DISCONNECTED);
721        }
722    }
723
724    private class Connected extends State {
725        @Override
726        public void enter() {
727            log("Enter Connected: " + getCurrentMessage().what +
728                           ", size: " + mConnectedDevicesList.size());
729            // start phone state listener here so that the CIND response as part of SLC can be
730            // responded to, correctly.
731            // we may enter Connected from Disconnected/Pending/AudioOn. listenForPhoneState
732            // internally handles multiple calls to start listen
733            mPhoneState.listenForPhoneState(true);
734        }
735
736        @Override
737        public boolean processMessage(Message message) {
738            log("Connected process message: " + message.what +
739                          ", size: " + mConnectedDevicesList.size());
740            if (DBG) {
741                if (mConnectedDevicesList.size() == 0) {
742                    log("ERROR: mConnectedDevicesList is empty in Connected");
743                    return NOT_HANDLED;
744                }
745            }
746
747            boolean retValue = HANDLED;
748            switch(message.what) {
749                case CONNECT:
750                {
751                    BluetoothDevice device = (BluetoothDevice) message.obj;
752                    if (device == null) {
753                        break;
754                    }
755
756                    if (mConnectedDevicesList.contains(device)) {
757                        Log.e(TAG, "ERROR: Connect received for already connected device, Ignore");
758                        break;
759                    }
760
761                   if (mConnectedDevicesList.size() >= max_hf_connections) {
762                       BluetoothDevice DisconnectConnectedDevice = null;
763                       IState CurrentAudioState = getCurrentState();
764                       Log.d(TAG, "Reach to max size, disconnect one of them first");
765                       /* TODO: Disconnect based on CoD */
766                       DisconnectConnectedDevice = mConnectedDevicesList.get(0);
767
768                       broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
769                                   BluetoothProfile.STATE_DISCONNECTED);
770
771                       if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
772                           broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
773                                       BluetoothProfile.STATE_CONNECTING);
774                           break;
775                       } else {
776                           broadcastConnectionState(DisconnectConnectedDevice,
777                                       BluetoothProfile.STATE_DISCONNECTING,
778                                       BluetoothProfile.STATE_CONNECTED);
779                       }
780
781                       synchronized (HeadsetStateMachine.this) {
782                           mTargetDevice = device;
783                           if (max_hf_connections == 1) {
784                               transitionTo(mPending);
785                           } else {
786                               mMultiDisconnectDevice = DisconnectConnectedDevice;
787                               transitionTo(mMultiHFPending);
788                           }
789                           DisconnectConnectedDevice = null;
790                       }
791                    }else if (mConnectedDevicesList.size() < max_hf_connections) {
792                       broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
793                         BluetoothProfile.STATE_DISCONNECTED);
794                       if (!connectHfpNative(getByteAddress(device))) {
795                           broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
796                               BluetoothProfile.STATE_CONNECTING);
797                           break;
798                       }
799                       synchronized (HeadsetStateMachine.this) {
800                           mTargetDevice = device;
801                           // Transtion to MultiHFPending state for Multi HF connection
802                           transitionTo(mMultiHFPending);
803                       }
804                    }
805                    Message m = obtainMessage(CONNECT_TIMEOUT);
806                    m.obj = device;
807                    sendMessageDelayed(m, 30000);
808                }
809                    break;
810                case DISCONNECT:
811                {
812                    BluetoothDevice device = (BluetoothDevice) message.obj;
813                    if (!mConnectedDevicesList.contains(device)) {
814                        break;
815                    }
816                    broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
817                                   BluetoothProfile.STATE_CONNECTED);
818                    if (!disconnectHfpNative(getByteAddress(device))) {
819                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
820                                       BluetoothProfile.STATE_DISCONNECTED);
821                        break;
822                    }
823
824                    if (mConnectedDevicesList.size() > 1) {
825                        mMultiDisconnectDevice = device;
826                        transitionTo(mMultiHFPending);
827                    } else {
828                        transitionTo(mPending);
829                    }
830                }
831                    break;
832                case CONNECT_AUDIO:
833                {
834                    BluetoothDevice device = mCurrentDevice;
835                    // TODO(BT) when failure, broadcast audio connecting to disconnected intent
836                    //          check if device matches mCurrentDevice
837                    if (mActiveScoDevice != null) {
838                        log("connectAudioNative in Connected; mActiveScoDevice is not null");
839                        device = mActiveScoDevice;
840                    }
841                    log("connectAudioNative in Connected for device = " + device);
842                    connectAudioNative(getByteAddress(device));
843                }
844                    break;
845                case VOICE_RECOGNITION_START:
846                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
847                    break;
848                case VOICE_RECOGNITION_STOP:
849                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
850                    break;
851                case CALL_STATE_CHANGED:
852                    processCallState((HeadsetCallState) message.obj, ((message.arg1==1)?true:false));
853                    break;
854                case INTENT_BATTERY_CHANGED:
855                    processIntentBatteryChanged((Intent) message.obj);
856                    break;
857                case DEVICE_STATE_CHANGED:
858                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
859                    break;
860                case SEND_CCLC_RESPONSE:
861                    processSendClccResponse((HeadsetClccResponse) message.obj);
862                    break;
863                case CLCC_RSP_TIMEOUT:
864                {
865                    BluetoothDevice device = (BluetoothDevice) message.obj;
866                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
867                }
868                    break;
869                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
870                    processSendVendorSpecificResultCode(
871                            (HeadsetVendorSpecificResultCode) message.obj);
872                    break;
873                case DIALING_OUT_TIMEOUT:
874                {
875                    BluetoothDevice device = (BluetoothDevice) message.obj;
876                    if (mDialingOut) {
877                        mDialingOut= false;
878                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
879                                                   0, getByteAddress(device));
880                    }
881                }
882                    break;
883                case VIRTUAL_CALL_START:
884                    initiateScoUsingVirtualVoiceCall();
885                    break;
886                case VIRTUAL_CALL_STOP:
887                    terminateScoUsingVirtualVoiceCall();
888                    break;
889                case ENABLE_WBS:
890                {
891                    BluetoothDevice device = (BluetoothDevice) message.obj;
892                    configureWBSNative(getByteAddress(device),WBS_CODEC);
893                }
894                    break;
895                case DISABLE_WBS:
896                {
897                    BluetoothDevice device = (BluetoothDevice) message.obj;
898                    configureWBSNative(getByteAddress(device),NBS_CODEC);
899                }
900                    break;
901                case START_VR_TIMEOUT:
902                {
903                    BluetoothDevice device = (BluetoothDevice) message.obj;
904                    if (mWaitingForVoiceRecognition) {
905                        device = (BluetoothDevice) message.obj;
906                        mWaitingForVoiceRecognition = false;
907                        Log.e(TAG, "Timeout waiting for voice recognition to start");
908                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
909                                                   0, getByteAddress(device));
910                    }
911                }
912                    break;
913                case STACK_EVENT:
914                    StackEvent event = (StackEvent) message.obj;
915                    if (DBG) {
916                        log("event type: " + event.type + "event device : "
917                                                  + event.device);
918                    }
919                    switch (event.type) {
920                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
921                            processConnectionEvent(event.valueInt, event.device);
922                            break;
923                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
924                            processAudioEvent(event.valueInt, event.device);
925                            break;
926                        case EVENT_TYPE_VR_STATE_CHANGED:
927                            processVrEvent(event.valueInt, event.device);
928                            break;
929                        case EVENT_TYPE_ANSWER_CALL:
930                            // TODO(BT) could answer call happen on Connected state?
931                            processAnswerCall(event.device);
932                            break;
933                        case EVENT_TYPE_HANGUP_CALL:
934                            // TODO(BT) could hangup call happen on Connected state?
935                            processHangupCall(event.device);
936                            break;
937                        case EVENT_TYPE_VOLUME_CHANGED:
938                            processVolumeEvent(event.valueInt, event.valueInt2,
939                                                        event.device);
940                            break;
941                        case EVENT_TYPE_DIAL_CALL:
942                            processDialCall(event.valueString, event.device);
943                            break;
944                        case EVENT_TYPE_SEND_DTMF:
945                            processSendDtmf(event.valueInt, event.device);
946                            break;
947                        case EVENT_TYPE_NOICE_REDUCTION:
948                            processNoiceReductionEvent(event.valueInt, event.device);
949                            break;
950                        case EVENT_TYPE_WBS:
951                            Log.d(TAG, "EVENT_TYPE_WBS codec is "+event.valueInt);
952                            processWBSEvent(event.valueInt, event.device);
953                            break;
954                        case EVENT_TYPE_AT_CHLD:
955                            processAtChld(event.valueInt, event.device);
956                            break;
957                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
958                            processSubscriberNumberRequest(event.device);
959                            break;
960                        case EVENT_TYPE_AT_CIND:
961                            processAtCind(event.device);
962                            break;
963                        case EVENT_TYPE_AT_COPS:
964                            processAtCops(event.device);
965                            break;
966                        case EVENT_TYPE_AT_CLCC:
967                            processAtClcc(event.device);
968                            break;
969                        case EVENT_TYPE_UNKNOWN_AT:
970                            processUnknownAt(event.valueString, event.device);
971                            break;
972                        case EVENT_TYPE_KEY_PRESSED:
973                            processKeyPressed(event.device);
974                            break;
975                        default:
976                            Log.e(TAG, "Unknown stack event: " + event.type);
977                            break;
978                    }
979                    break;
980                default:
981                    return NOT_HANDLED;
982            }
983            return retValue;
984        }
985
986        // in Connected state
987        private void processConnectionEvent(int state, BluetoothDevice device) {
988        Log.d(TAG, "processConnectionEvent state = " + state + ", device = "
989                                                           + device);
990            switch (state) {
991                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
992                    if (mConnectedDevicesList.contains(device)) {
993                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
994                                                 BluetoothProfile.STATE_CONNECTED);
995                        processWBSEvent(0, device); /* disable WBS audio parameters */
996                        synchronized (HeadsetStateMachine.this) {
997                            mConnectedDevicesList.remove(device);
998                            mHeadsetAudioParam.remove(device);
999                            mHeadsetBrsf.remove(device);
1000                            Log.d(TAG, "device " + device.getAddress() +
1001                                         " is removed in Connected state");
1002
1003                            if (mConnectedDevicesList.size() == 0) {
1004                                mCurrentDevice = null;
1005                                transitionTo(mDisconnected);
1006                            }
1007                            else {
1008                                processMultiHFConnected(device);
1009                            }
1010                        }
1011                    } else {
1012                        Log.e(TAG, "Disconnected from unknown device: " + device);
1013                    }
1014                    break;
1015                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1016                    processSlcConnected();
1017                    break;
1018                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1019                    if (mConnectedDevicesList.contains(device)) {
1020                        mIncomingDevice = null;
1021                        mTargetDevice = null;
1022                        break;
1023                    }
1024                    Log.w(TAG, "HFP to be Connected in Connected state");
1025                    if (okToConnect(device) && (mConnectedDevicesList.size()
1026                                                       < max_hf_connections)) {
1027                        Log.i(TAG,"Incoming Hf accepted");
1028                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1029                                          BluetoothProfile.STATE_DISCONNECTED);
1030                        synchronized (HeadsetStateMachine.this) {
1031                            if(!mConnectedDevicesList.contains(device)) {
1032                                mCurrentDevice = device;
1033                                mConnectedDevicesList.add(device);
1034                                Log.d(TAG, "device " + device.getAddress() +
1035                                             " is added in Connected state");
1036                            }
1037                            transitionTo(mConnected);
1038                        }
1039                        configAudioParameters(device);
1040                    } else {
1041                        // reject the connection and stay in Connected state itself
1042                        Log.i(TAG,"Incoming Hf rejected. priority=" +
1043                               mService.getPriority(device) + " bondState=" +
1044                                        device.getBondState());
1045                        disconnectHfpNative(getByteAddress(device));
1046                        // the other profile connection should be initiated
1047                        AdapterService adapterService = AdapterService.getAdapterService();
1048                        if (adapterService != null) {
1049                            adapterService.connectOtherProfile(device,
1050                                                        AdapterService.PROFILE_CONN_REJECTED);
1051                        }
1052                    }
1053                    break;
1054                default:
1055                  Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1056                    break;
1057            }
1058        }
1059
1060        // in Connected state
1061        private void processAudioEvent(int state, BluetoothDevice device) {
1062            if (!mConnectedDevicesList.contains(device)) {
1063                Log.e(TAG, "Audio changed on disconnected device: " + device);
1064                return;
1065            }
1066
1067            switch (state) {
1068                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1069                    // TODO(BT) should I save the state for next broadcast as the prevState?
1070                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1071                    setAudioParameters(device); /*Set proper Audio Paramters.*/
1072                    mAudioManager.setBluetoothScoOn(true);
1073                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1074                                        BluetoothHeadset.STATE_AUDIO_CONNECTING);
1075                    mActiveScoDevice = device;
1076                    transitionTo(mAudioOn);
1077                    break;
1078                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1079                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1080                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1081                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1082                    break;
1083                    // TODO(BT) process other states
1084                default:
1085                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1086                    break;
1087            }
1088        }
1089
1090        private void processSlcConnected() {
1091            if (mPhoneProxy != null) {
1092                try {
1093                    mPhoneProxy.queryPhoneState();
1094                } catch (RemoteException e) {
1095                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1096                }
1097            } else {
1098                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1099            }
1100
1101        }
1102
1103        private void processMultiHFConnected(BluetoothDevice device) {
1104            log("Connect state: processMultiHFConnected");
1105            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1106                log ("mActiveScoDevice is disconnected, setting it to null");
1107                mActiveScoDevice = null;
1108            }
1109            /* Assign the current activedevice again if the disconnected
1110                         device equals to the current active device */
1111            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1112                transitionTo(mConnected);
1113                int deviceSize = mConnectedDevicesList.size();
1114                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1115            } else {
1116                // The disconnected device is not current active device
1117                transitionTo(mConnected);
1118            }
1119            log("processMultiHFConnected , the latest mCurrentDevice is:" +
1120                                     mCurrentDevice);
1121            log("Connect state: processMultiHFConnected ," +
1122                       "fake broadcasting for mCurrentDevice");
1123            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1124                            BluetoothProfile.STATE_DISCONNECTED);
1125        }
1126    }
1127
1128    private class AudioOn extends State {
1129
1130        @Override
1131        public void enter() {
1132            log("Enter AudioOn: " + getCurrentMessage().what + ", size: " +
1133                                  mConnectedDevicesList.size());
1134        }
1135
1136        @Override
1137        public boolean processMessage(Message message) {
1138            log("AudioOn process message: " + message.what + ", size: " +
1139                                  mConnectedDevicesList.size());
1140            if (DBG) {
1141                if (mConnectedDevicesList.size() == 0) {
1142                    log("ERROR: mConnectedDevicesList is empty in AudioOn");
1143                    return NOT_HANDLED;
1144                }
1145            }
1146
1147            boolean retValue = HANDLED;
1148            switch(message.what) {
1149                case CONNECT:
1150                {
1151                    BluetoothDevice device = (BluetoothDevice) message.obj;
1152                    if (device == null) {
1153                        break;
1154                    }
1155
1156                    if (mConnectedDevicesList.contains(device)) {
1157                        break;
1158                    }
1159
1160                    if (max_hf_connections == 1) {
1161                        deferMessage(obtainMessage(DISCONNECT, mCurrentDevice));
1162                        deferMessage(obtainMessage(CONNECT, device));
1163                        if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
1164                            Log.d(TAG, "Disconnecting SCO audio for device = " + mCurrentDevice);
1165                        } else {
1166                            Log.e(TAG, "disconnectAudioNative failed");
1167                        }
1168                        break;
1169                    }
1170
1171                    if (mConnectedDevicesList.size() >= max_hf_connections) {
1172                        BluetoothDevice DisconnectConnectedDevice = null;
1173                        IState CurrentAudioState = getCurrentState();
1174                        Log.d(TAG, "Reach to max size, disconnect " +
1175                                           "one of them first");
1176                        DisconnectConnectedDevice = mConnectedDevicesList.get(0);
1177
1178                        if (mActiveScoDevice.equals(DisconnectConnectedDevice)) {
1179                           DisconnectConnectedDevice = mConnectedDevicesList.get(1);
1180                        }
1181
1182                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1183                                   BluetoothProfile.STATE_DISCONNECTED);
1184
1185                        if (!disconnectHfpNative(getByteAddress(DisconnectConnectedDevice))) {
1186                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1187                                           BluetoothProfile.STATE_CONNECTING);
1188                            break;
1189                        } else {
1190                            broadcastConnectionState(DisconnectConnectedDevice,
1191                                       BluetoothProfile.STATE_DISCONNECTING,
1192                                       BluetoothProfile.STATE_CONNECTED);
1193                        }
1194
1195                        synchronized (HeadsetStateMachine.this) {
1196                            mTargetDevice = device;
1197                            mMultiDisconnectDevice = DisconnectConnectedDevice;
1198                            transitionTo(mMultiHFPending);
1199                            DisconnectConnectedDevice = null;
1200                        }
1201                    } else if(mConnectedDevicesList.size() < max_hf_connections) {
1202                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
1203                        BluetoothProfile.STATE_DISCONNECTED);
1204                        if (!connectHfpNative(getByteAddress(device))) {
1205                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1206                                BluetoothProfile.STATE_CONNECTING);
1207                            break;
1208                        }
1209                        synchronized (HeadsetStateMachine.this) {
1210                            mTargetDevice = device;
1211                            // Transtion to MultilHFPending state for Multi handsfree connection
1212                            transitionTo(mMultiHFPending);
1213                        }
1214                    }
1215                    Message m = obtainMessage(CONNECT_TIMEOUT);
1216                    m.obj = device;
1217                    sendMessageDelayed(m, 30000);
1218                }
1219                break;
1220                case CONNECT_TIMEOUT:
1221                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1222                                             getByteAddress(mTargetDevice));
1223                break;
1224                case DISCONNECT:
1225                {
1226                    BluetoothDevice device = (BluetoothDevice)message.obj;
1227                    if (!mConnectedDevicesList.contains(device)) {
1228                        break;
1229                    }
1230                    if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1231                        // The disconnected device is active SCO device
1232                        Log.d(TAG, "AudioOn, the disconnected device" +
1233                                            "is active SCO device");
1234                        deferMessage(obtainMessage(DISCONNECT, message.obj));
1235                        // Disconnect BT SCO first
1236                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1237                            log("Disconnecting SCO audio");
1238                        } else {
1239                            // if disconnect BT SCO failed, transition to mConnected state
1240                            transitionTo(mConnected);
1241                        }
1242                    } else {
1243                        /* Do not disconnect BT SCO if the disconnected
1244                           device is not active SCO device */
1245                        Log.d(TAG, "AudioOn, the disconnected device" +
1246                                        "is not active SCO device");
1247                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
1248                                   BluetoothProfile.STATE_CONNECTED);
1249                        // Should be still in AudioOn state
1250                        if (!disconnectHfpNative(getByteAddress(device))) {
1251                            Log.w(TAG, "AudioOn, disconnect device failed");
1252                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1253                                       BluetoothProfile.STATE_DISCONNECTING);
1254                            break;
1255                        }
1256                        /* Transtion to MultiHFPending state for Multi
1257                           handsfree connection */
1258                        if (mConnectedDevicesList.size() > 1) {
1259                            mMultiDisconnectDevice = device;
1260                            transitionTo(mMultiHFPending);
1261                        }
1262                    }
1263                }
1264                break;
1265                case DISCONNECT_AUDIO:
1266                    if (mActiveScoDevice != null) {
1267                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1268                            log("Disconnecting SCO audio for device = " +
1269                                                 mActiveScoDevice);
1270                        } else {
1271                            Log.e(TAG, "disconnectAudioNative failed" +
1272                                      "for device = " + mActiveScoDevice);
1273                        }
1274                    }
1275                    break;
1276                case VOICE_RECOGNITION_START:
1277                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1278                    break;
1279                case VOICE_RECOGNITION_STOP:
1280                    processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1281                    break;
1282                case INTENT_SCO_VOLUME_CHANGED:
1283                    processIntentScoVolume((Intent) message.obj, mActiveScoDevice);
1284                    break;
1285                case CALL_STATE_CHANGED:
1286                    processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false));
1287                    break;
1288                case INTENT_BATTERY_CHANGED:
1289                    processIntentBatteryChanged((Intent) message.obj);
1290                    break;
1291                case DEVICE_STATE_CHANGED:
1292                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1293                    break;
1294                case SEND_CCLC_RESPONSE:
1295                    processSendClccResponse((HeadsetClccResponse) message.obj);
1296                    break;
1297                case CLCC_RSP_TIMEOUT:
1298                {
1299                    BluetoothDevice device = (BluetoothDevice) message.obj;
1300                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1301                }
1302                    break;
1303                case SEND_VENDOR_SPECIFIC_RESULT_CODE:
1304                    processSendVendorSpecificResultCode(
1305                            (HeadsetVendorSpecificResultCode) message.obj);
1306                    break;
1307
1308                case VIRTUAL_CALL_START:
1309                    initiateScoUsingVirtualVoiceCall();
1310                    break;
1311                case VIRTUAL_CALL_STOP:
1312                    terminateScoUsingVirtualVoiceCall();
1313                    break;
1314
1315                case DIALING_OUT_TIMEOUT:
1316                {
1317                    if (mDialingOut) {
1318                        BluetoothDevice device = (BluetoothDevice)message.obj;
1319                        mDialingOut= false;
1320                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1321                                               0, getByteAddress(device));
1322                    }
1323                }
1324                    break;
1325                case START_VR_TIMEOUT:
1326                {
1327                    if (mWaitingForVoiceRecognition) {
1328                        BluetoothDevice device = (BluetoothDevice)message.obj;
1329                        mWaitingForVoiceRecognition = false;
1330                        Log.e(TAG, "Timeout waiting for voice recognition" +
1331                                                     "to start");
1332                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1333                                               0, getByteAddress(device));
1334                    }
1335                }
1336                    break;
1337                case STACK_EVENT:
1338                    StackEvent event = (StackEvent) message.obj;
1339                    if (DBG) {
1340                        log("event type: " + event.type);
1341                    }
1342                    switch (event.type) {
1343                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1344                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1345                            if (device1 != null && device1.equals(event.device)) {
1346                                Log.d(TAG, "remove connect timeout for device = " + device1);
1347                                removeMessages(CONNECT_TIMEOUT);
1348                            }
1349                            processConnectionEvent(event.valueInt, event.device);
1350                            break;
1351                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1352                            processAudioEvent(event.valueInt, event.device);
1353                            break;
1354                        case EVENT_TYPE_VR_STATE_CHANGED:
1355                            processVrEvent(event.valueInt, event.device);
1356                            break;
1357                        case EVENT_TYPE_ANSWER_CALL:
1358                            processAnswerCall(event.device);
1359                            break;
1360                        case EVENT_TYPE_HANGUP_CALL:
1361                            processHangupCall(event.device);
1362                            break;
1363                        case EVENT_TYPE_VOLUME_CHANGED:
1364                            processVolumeEvent(event.valueInt, event.valueInt2,
1365                                                     event.device);
1366                            break;
1367                        case EVENT_TYPE_DIAL_CALL:
1368                            processDialCall(event.valueString, event.device);
1369                            break;
1370                        case EVENT_TYPE_SEND_DTMF:
1371                            processSendDtmf(event.valueInt, event.device);
1372                            break;
1373                        case EVENT_TYPE_NOICE_REDUCTION:
1374                            processNoiceReductionEvent(event.valueInt, event.device);
1375                            break;
1376                        case EVENT_TYPE_AT_CHLD:
1377                            processAtChld(event.valueInt, event.device);
1378                            break;
1379                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1380                            processSubscriberNumberRequest(event.device);
1381                            break;
1382                        case EVENT_TYPE_AT_CIND:
1383                            processAtCind(event.device);
1384                            break;
1385                        case EVENT_TYPE_AT_COPS:
1386                            processAtCops(event.device);
1387                            break;
1388                        case EVENT_TYPE_AT_CLCC:
1389                            processAtClcc(event.device);
1390                            break;
1391                        case EVENT_TYPE_UNKNOWN_AT:
1392                            processUnknownAt(event.valueString, event.device);
1393                            break;
1394                        case EVENT_TYPE_KEY_PRESSED:
1395                            processKeyPressed(event.device);
1396                            break;
1397                        default:
1398                            Log.e(TAG, "Unknown stack event: " + event.type);
1399                            break;
1400                    }
1401                    break;
1402                default:
1403                    return NOT_HANDLED;
1404            }
1405            return retValue;
1406        }
1407
1408        // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
1409        private void processConnectionEvent(int state, BluetoothDevice device) {
1410        Log.d(TAG, "processConnectionEvent state = " + state + ", device = " +
1411                                                   device);
1412            switch (state) {
1413                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1414                    if (mConnectedDevicesList.contains(device)) {
1415                        if (mActiveScoDevice != null
1416                            && mActiveScoDevice.equals(device)&& mAudioState
1417                            != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1418                            processAudioEvent(
1419                                HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
1420                        }
1421
1422                        synchronized (HeadsetStateMachine.this) {
1423                            mConnectedDevicesList.remove(device);
1424                            mHeadsetAudioParam.remove(device);
1425                            mHeadsetBrsf.remove(device);
1426                            Log.d(TAG, "device " + device.getAddress() +
1427                                           " is removed in AudioOn state");
1428                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
1429                                                     BluetoothProfile.STATE_CONNECTED);
1430                            processWBSEvent(0, device); /* disable WBS audio parameters */
1431                            if (mConnectedDevicesList.size() == 0) {
1432                                transitionTo(mDisconnected);
1433                            }
1434                            else {
1435                                processMultiHFConnected(device);
1436                            }
1437                        }
1438                    } else {
1439                        Log.e(TAG, "Disconnected from unknown device: " + device);
1440                    }
1441                    break;
1442               case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
1443                    processSlcConnected();
1444                    break;
1445                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1446                    if (mConnectedDevicesList.contains(device)) {
1447                        mIncomingDevice = null;
1448                        mTargetDevice = null;
1449                        break;
1450                    }
1451                    Log.w(TAG, "HFP to be Connected in AudioOn state");
1452                    if (okToConnect(device) && (mConnectedDevicesList.size()
1453                                                      < max_hf_connections) ) {
1454                        Log.i(TAG,"Incoming Hf accepted");
1455                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1456                                          BluetoothProfile.STATE_DISCONNECTED);
1457                        synchronized (HeadsetStateMachine.this) {
1458                            if (!mConnectedDevicesList.contains(device)) {
1459                                mCurrentDevice = device;
1460                                mConnectedDevicesList.add(device);
1461                                Log.d(TAG, "device " + device.getAddress() +
1462                                              " is added in AudioOn state");
1463                            }
1464                        }
1465                        configAudioParameters(device);
1466                     } else {
1467                         // reject the connection and stay in Connected state itself
1468                         Log.i(TAG,"Incoming Hf rejected. priority="
1469                                      + mService.getPriority(device) +
1470                                       " bondState=" + device.getBondState());
1471                         disconnectHfpNative(getByteAddress(device));
1472                         // the other profile connection should be initiated
1473                         AdapterService adapterService = AdapterService.getAdapterService();
1474                         if (adapterService != null) {
1475                             adapterService.connectOtherProfile(device,
1476                                             AdapterService.PROFILE_CONN_REJECTED);
1477                         }
1478                    }
1479                    break;
1480                default:
1481                    Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
1482                    break;
1483            }
1484        }
1485
1486        // in AudioOn state
1487        private void processAudioEvent(int state, BluetoothDevice device) {
1488            if (!mConnectedDevicesList.contains(device)) {
1489                Log.e(TAG, "Audio changed on disconnected device: " + device);
1490                return;
1491            }
1492
1493            switch (state) {
1494                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1495                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1496                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1497                        mAudioManager.setBluetoothScoOn(false);
1498                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1499                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
1500                    }
1501                    transitionTo(mConnected);
1502                    break;
1503                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
1504                    // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
1505                    //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
1506                    //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
1507                    break;
1508                default:
1509                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1510                    break;
1511            }
1512        }
1513
1514        private void processSlcConnected() {
1515            if (mPhoneProxy != null) {
1516                try {
1517                    mPhoneProxy.queryPhoneState();
1518                } catch (RemoteException e) {
1519                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
1520                }
1521            } else {
1522                Log.e(TAG, "Handsfree phone proxy null for query phone state");
1523            }
1524         }
1525
1526        private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
1527            int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
1528            if (mPhoneState.getSpeakerVolume() != volumeValue) {
1529                mPhoneState.setSpeakerVolume(volumeValue);
1530                setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK,
1531                                        volumeValue, getByteAddress(device));
1532            }
1533        }
1534
1535        private void processMultiHFConnected(BluetoothDevice device) {
1536            log("AudioOn state: processMultiHFConnected");
1537            /* Assign the current activedevice again if the disconnected
1538                          device equals to the current active device */
1539            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1540                int deviceSize = mConnectedDevicesList.size();
1541                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1542            }
1543            if (mAudioState != BluetoothHeadset.STATE_AUDIO_CONNECTED)
1544                transitionTo(mConnected);
1545
1546            log("processMultiHFConnected , the latest mCurrentDevice is:"
1547                                      + mCurrentDevice);
1548            log("AudioOn state: processMultiHFConnected ," +
1549                       "fake broadcasting for mCurrentDevice");
1550            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1551                            BluetoothProfile.STATE_DISCONNECTED);
1552        }
1553    }
1554
1555    /* Add MultiHFPending state when atleast 1 HS is connected
1556            and disconnect/connect new HS */
1557    private class MultiHFPending extends State {
1558        @Override
1559        public void enter() {
1560            log("Enter MultiHFPending: " + getCurrentMessage().what +
1561                         ", size: " + mConnectedDevicesList.size());
1562        }
1563
1564        @Override
1565        public boolean processMessage(Message message) {
1566            log("MultiHFPending process message: " + message.what +
1567                         ", size: " + mConnectedDevicesList.size());
1568
1569            boolean retValue = HANDLED;
1570            switch(message.what) {
1571                case CONNECT:
1572                    deferMessage(message);
1573                    break;
1574
1575                case CONNECT_AUDIO:
1576                    if (mCurrentDevice != null) {
1577                        connectAudioNative(getByteAddress(mCurrentDevice));
1578                    }
1579                    break;
1580                case CONNECT_TIMEOUT:
1581                    onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
1582                                             getByteAddress(mTargetDevice));
1583                    break;
1584
1585                case DISCONNECT_AUDIO:
1586                    if (mActiveScoDevice != null) {
1587                        if (disconnectAudioNative(getByteAddress(mActiveScoDevice))) {
1588                            Log.d(TAG, "MultiHFPending, Disconnecting SCO audio for " +
1589                                                 mActiveScoDevice);
1590                        } else {
1591                            Log.e(TAG, "disconnectAudioNative failed" +
1592                                      "for device = " + mActiveScoDevice);
1593                        }
1594                    }
1595                    break;
1596                case DISCONNECT:
1597                    BluetoothDevice device = (BluetoothDevice) message.obj;
1598                    if (mConnectedDevicesList.contains(device) &&
1599                        mTargetDevice != null && mTargetDevice.equals(device)) {
1600                        // cancel connection to the mTargetDevice
1601                        broadcastConnectionState(device,
1602                                       BluetoothProfile.STATE_DISCONNECTED,
1603                                       BluetoothProfile.STATE_CONNECTING);
1604                        synchronized (HeadsetStateMachine.this) {
1605                            mTargetDevice = null;
1606                        }
1607                    } else {
1608                        deferMessage(message);
1609                    }
1610                    break;
1611                case VOICE_RECOGNITION_START:
1612                    device = (BluetoothDevice) message.obj;
1613                    if (mConnectedDevicesList.contains(device)) {
1614                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
1615                    }
1616                    break;
1617                case VOICE_RECOGNITION_STOP:
1618                    device = (BluetoothDevice) message.obj;
1619                    if (mConnectedDevicesList.contains(device)) {
1620                        processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
1621                    }
1622                    break;
1623                case INTENT_BATTERY_CHANGED:
1624                    processIntentBatteryChanged((Intent) message.obj);
1625                    break;
1626                case CALL_STATE_CHANGED:
1627                    processCallState((HeadsetCallState) message.obj,
1628                                      ((message.arg1 == 1)?true:false));
1629                    break;
1630                case DEVICE_STATE_CHANGED:
1631                    processDeviceStateChanged((HeadsetDeviceState) message.obj);
1632                    break;
1633                case SEND_CCLC_RESPONSE:
1634                    processSendClccResponse((HeadsetClccResponse) message.obj);
1635                    break;
1636                case CLCC_RSP_TIMEOUT:
1637                {
1638                    device = (BluetoothDevice) message.obj;
1639                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
1640                }
1641                    break;
1642                case DIALING_OUT_TIMEOUT:
1643                    if (mDialingOut) {
1644                        device = (BluetoothDevice) message.obj;
1645                        mDialingOut= false;
1646                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1647                                             0, getByteAddress(device));
1648                    }
1649                    break;
1650                case VIRTUAL_CALL_START:
1651                    device = (BluetoothDevice) message.obj;
1652                    if(mConnectedDevicesList.contains(device)) {
1653                        initiateScoUsingVirtualVoiceCall();
1654                    }
1655                    break;
1656                case VIRTUAL_CALL_STOP:
1657                    device = (BluetoothDevice) message.obj;
1658                    if (mConnectedDevicesList.contains(device)) {
1659                        terminateScoUsingVirtualVoiceCall();
1660                    }
1661                    break;
1662                case START_VR_TIMEOUT:
1663                    if (mWaitingForVoiceRecognition) {
1664                        device = (BluetoothDevice) message.obj;
1665                        mWaitingForVoiceRecognition = false;
1666                        Log.e(TAG, "Timeout waiting for voice" +
1667                                             "recognition to start");
1668                        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
1669                                               0, getByteAddress(device));
1670                    }
1671                    break;
1672                case STACK_EVENT:
1673                    StackEvent event = (StackEvent) message.obj;
1674                    if (DBG) {
1675                        log("event type: " + event.type);
1676                    }
1677                    switch (event.type) {
1678                        case EVENT_TYPE_CONNECTION_STATE_CHANGED:
1679                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
1680                            if (device1 != null && device1.equals(event.device)) {
1681                                Log.d(TAG, "remove connect timeout for device = " + device1);
1682                                removeMessages(CONNECT_TIMEOUT);
1683                            }
1684                            processConnectionEvent(event.valueInt, event.device);
1685                            break;
1686                        case EVENT_TYPE_AUDIO_STATE_CHANGED:
1687                            processAudioEvent(event.valueInt, event.device);
1688                            break;
1689                        case EVENT_TYPE_VR_STATE_CHANGED:
1690                            processVrEvent(event.valueInt,event.device);
1691                            break;
1692                        case EVENT_TYPE_ANSWER_CALL:
1693                            //TODO(BT) could answer call happen on Connected state?
1694                            processAnswerCall(event.device);
1695                            break;
1696                        case EVENT_TYPE_HANGUP_CALL:
1697                            // TODO(BT) could hangup call happen on Connected state?
1698                            processHangupCall(event.device);
1699                            break;
1700                        case EVENT_TYPE_VOLUME_CHANGED:
1701                            processVolumeEvent(event.valueInt, event.valueInt2,
1702                                                    event.device);
1703                            break;
1704                        case EVENT_TYPE_DIAL_CALL:
1705                            processDialCall(event.valueString, event.device);
1706                            break;
1707                        case EVENT_TYPE_SEND_DTMF:
1708                            processSendDtmf(event.valueInt, event.device);
1709                            break;
1710                        case EVENT_TYPE_NOICE_REDUCTION:
1711                            processNoiceReductionEvent(event.valueInt, event.device);
1712                            break;
1713                        case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
1714                            processSubscriberNumberRequest(event.device);
1715                            break;
1716                        case EVENT_TYPE_AT_CIND:
1717                            processAtCind(event.device);
1718                            break;
1719                        case EVENT_TYPE_AT_CHLD:
1720                            processAtChld(event.valueInt, event.device);
1721                            break;
1722                        case EVENT_TYPE_AT_COPS:
1723                            processAtCops(event.device);
1724                            break;
1725                        case EVENT_TYPE_AT_CLCC:
1726                            processAtClcc(event.device);
1727                            break;
1728                        case EVENT_TYPE_UNKNOWN_AT:
1729                            processUnknownAt(event.valueString,event.device);
1730                            break;
1731                        case EVENT_TYPE_KEY_PRESSED:
1732                            processKeyPressed(event.device);
1733                            break;
1734                        default:
1735                            Log.e(TAG, "Unexpected event: " + event.type);
1736                            break;
1737                    }
1738                    break;
1739                default:
1740                    return NOT_HANDLED;
1741            }
1742            return retValue;
1743        }
1744
1745        // in MultiHFPending state
1746        private void processConnectionEvent(int state, BluetoothDevice device) {
1747            Log.d(TAG, "processConnectionEvent state = " + state +
1748                                     ", device = " + device);
1749            switch (state) {
1750                case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
1751                    if (mConnectedDevicesList.contains(device)) {
1752                        if (mMultiDisconnectDevice != null &&
1753                                mMultiDisconnectDevice.equals(device)) {
1754                            mMultiDisconnectDevice = null;
1755
1756                          synchronized (HeadsetStateMachine.this) {
1757                              mConnectedDevicesList.remove(device);
1758                              mHeadsetAudioParam.remove(device);
1759                              mHeadsetBrsf.remove(device);
1760                              Log.d(TAG, "device " + device.getAddress() +
1761                                      " is removed in MultiHFPending state");
1762                              broadcastConnectionState(device,
1763                                        BluetoothProfile.STATE_DISCONNECTED,
1764                                        BluetoothProfile.STATE_DISCONNECTING);
1765                          }
1766
1767                          if (mTargetDevice != null) {
1768                              if (!connectHfpNative(getByteAddress(mTargetDevice))) {
1769
1770                                broadcastConnectionState(mTargetDevice,
1771                                          BluetoothProfile.STATE_DISCONNECTED,
1772                                          BluetoothProfile.STATE_CONNECTING);
1773                                  synchronized (HeadsetStateMachine.this) {
1774                                      mTargetDevice = null;
1775                                      if (mConnectedDevicesList.size() == 0) {
1776                                          // Should be not in this state since it has at least
1777                                          // one HF connected in MultiHFPending state
1778                                          Log.d(TAG, "Should be not in this state, error handling");
1779                                          transitionTo(mDisconnected);
1780                                      }
1781                                      else {
1782                                          processMultiHFConnected(device);
1783                                      }
1784                                  }
1785                              }
1786                          } else {
1787                              synchronized (HeadsetStateMachine.this) {
1788                                  mIncomingDevice = null;
1789                                  if (mConnectedDevicesList.size() == 0) {
1790                                      transitionTo(mDisconnected);
1791                                  }
1792                                  else {
1793                                      processMultiHFConnected(device);
1794                                  }
1795                              }
1796                           }
1797                        } else {
1798                            /* Another HF disconnected when one HF is connecting */
1799                            synchronized (HeadsetStateMachine.this) {
1800                              mConnectedDevicesList.remove(device);
1801                              mHeadsetAudioParam.remove(device);
1802                              mHeadsetBrsf.remove(device);
1803                              Log.d(TAG, "device " + device.getAddress() +
1804                                           " is removed in MultiHFPending state");
1805                            }
1806                            broadcastConnectionState(device,
1807                                BluetoothProfile.STATE_DISCONNECTED,
1808                                BluetoothProfile.STATE_CONNECTED);
1809                        }
1810                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1811
1812                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1813                                                 BluetoothProfile.STATE_CONNECTING);
1814                        synchronized (HeadsetStateMachine.this) {
1815                            mTargetDevice = null;
1816                            if (mConnectedDevicesList.size() == 0) {
1817                                transitionTo(mDisconnected);
1818                            }
1819                            else
1820                            {
1821                               if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1822                                   transitionTo(mAudioOn);
1823                               else transitionTo(mConnected);
1824                            }
1825                        }
1826                    } else {
1827                        Log.e(TAG, "Unknown device Disconnected: " + device);
1828                    }
1829                    break;
1830            case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
1831                /* Outgoing disconnection for device failed */
1832                if (mConnectedDevicesList.contains(device)) {
1833
1834                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1835                                             BluetoothProfile.STATE_DISCONNECTING);
1836                    if (mTargetDevice != null) {
1837                        broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
1838                                                 BluetoothProfile.STATE_CONNECTING);
1839                    }
1840                    synchronized (HeadsetStateMachine.this) {
1841                        mTargetDevice = null;
1842                        if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1843                            transitionTo(mAudioOn);
1844                        else transitionTo(mConnected);
1845                    }
1846                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1847
1848                    synchronized (HeadsetStateMachine.this) {
1849                            mCurrentDevice = device;
1850                            mConnectedDevicesList.add(device);
1851                            Log.d(TAG, "device " + device.getAddress() +
1852                                      " is added in MultiHFPending state");
1853                            mTargetDevice = null;
1854                            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1855                                transitionTo(mAudioOn);
1856                            else transitionTo(mConnected);
1857                    }
1858
1859                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1860                                             BluetoothProfile.STATE_CONNECTING);
1861                    configAudioParameters(device);
1862                } else {
1863                    Log.w(TAG, "Some other incoming HF connected" +
1864                                          "in Multi Pending state");
1865                    if (okToConnect(device) &&
1866                            (mConnectedDevicesList.size() < max_hf_connections)) {
1867                        Log.i(TAG,"Incoming Hf accepted");
1868                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
1869                                         BluetoothProfile.STATE_DISCONNECTED);
1870                        synchronized (HeadsetStateMachine.this) {
1871                            if (!mConnectedDevicesList.contains(device)) {
1872                                mCurrentDevice = device;
1873                                mConnectedDevicesList.add(device);
1874                                Log.d(TAG, "device " + device.getAddress() +
1875                                            " is added in MultiHFPending state");
1876                            }
1877                        }
1878                        configAudioParameters(device);
1879                    } else {
1880                        // reject the connection and stay in Pending state itself
1881                        Log.i(TAG,"Incoming Hf rejected. priority=" +
1882                                          mService.getPriority(device) +
1883                                  " bondState=" + device.getBondState());
1884                        disconnectHfpNative(getByteAddress(device));
1885                        // the other profile connection should be initiated
1886                        AdapterService adapterService = AdapterService.getAdapterService();
1887                        if (adapterService != null) {
1888                            adapterService.connectOtherProfile(device,
1889                                          AdapterService.PROFILE_CONN_REJECTED);
1890                        }
1891                    }
1892                }
1893                break;
1894            case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
1895                if (mConnectedDevicesList.contains(device)) {
1896                    Log.e(TAG, "current device tries to connect back");
1897                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1898                    if (DBG) {
1899                        log("Stack and target device are connecting");
1900                    }
1901                }
1902                else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1903                    Log.e(TAG, "Another connecting event on" +
1904                                              "the incoming device");
1905                }
1906                break;
1907            case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
1908                if (mConnectedDevicesList.contains(device)) {
1909                    if (DBG) {
1910                        log("stack is disconnecting mCurrentDevice");
1911                    }
1912                } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
1913                    Log.e(TAG, "TargetDevice is getting disconnected");
1914                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
1915                    Log.e(TAG, "IncomingDevice is getting disconnected");
1916                } else {
1917                    Log.e(TAG, "Disconnecting unknow device: " + device);
1918                }
1919                break;
1920            default:
1921                Log.e(TAG, "Incorrect state: " + state);
1922                break;
1923            }
1924        }
1925
1926        private void processAudioEvent(int state, BluetoothDevice device) {
1927            if (!mConnectedDevicesList.contains(device)) {
1928                Log.e(TAG, "Audio changed on disconnected device: " + device);
1929                return;
1930            }
1931
1932            switch (state) {
1933                case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
1934                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
1935                    setAudioParameters(device); /* Set proper Audio Parameters. */
1936                    mAudioManager.setBluetoothScoOn(true);
1937                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
1938                                        BluetoothHeadset.STATE_AUDIO_CONNECTING);
1939                    mActiveScoDevice = device;
1940                    /* The state should be still in MultiHFPending state when
1941                       audio connected since other device is still connecting/
1942                       disconnecting */
1943                    break;
1944                case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
1945                    mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
1946                    broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
1947                                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1948                    break;
1949                case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
1950                    if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1951                        mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
1952                        mAudioManager.setBluetoothScoOn(false);
1953                        broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
1954                                            BluetoothHeadset.STATE_AUDIO_CONNECTED);
1955                    }
1956                    /* The state should be still in MultiHFPending state when audio
1957                       disconnected since other device is still connecting/
1958                       disconnecting */
1959                    break;
1960
1961                default:
1962                    Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
1963                    break;
1964            }
1965        }
1966
1967        private void processMultiHFConnected(BluetoothDevice device) {
1968            log("MultiHFPending state: processMultiHFConnected");
1969            if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
1970                log ("mActiveScoDevice is disconnected, setting it to null");
1971                mActiveScoDevice = null;
1972            }
1973            /* Assign the current activedevice again if the disconnected
1974               device equals to the current active device */
1975            if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
1976                int deviceSize = mConnectedDevicesList.size();
1977                mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
1978            }
1979            // The disconnected device is not current active device
1980            if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
1981                transitionTo(mAudioOn);
1982            else transitionTo(mConnected);
1983            log("processMultiHFConnected , the latest mCurrentDevice is:"
1984                                            + mCurrentDevice);
1985            log("MultiHFPending state: processMultiHFConnected ," +
1986                         "fake broadcasting for mCurrentDevice");
1987            broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
1988                            BluetoothProfile.STATE_DISCONNECTED);
1989        }
1990
1991    }
1992
1993
1994    private ServiceConnection mConnection = new ServiceConnection() {
1995        public void onServiceConnected(ComponentName className, IBinder service) {
1996            if (DBG) Log.d(TAG, "Proxy object connected");
1997            mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
1998        }
1999
2000        public void onServiceDisconnected(ComponentName className) {
2001            if (DBG) Log.d(TAG, "Proxy object disconnected");
2002            mPhoneProxy = null;
2003        }
2004    };
2005
2006    // HFP Connection state of the device could be changed by the state machine
2007    // in separate thread while this method is executing.
2008    int getConnectionState(BluetoothDevice device) {
2009        if (getCurrentState() == mDisconnected) {
2010            if (DBG) Log.d(TAG, "currentState is Disconnected");
2011            return BluetoothProfile.STATE_DISCONNECTED;
2012        }
2013
2014        synchronized (this) {
2015            IState currentState = getCurrentState();
2016            if (DBG) Log.d(TAG, "currentState = " + currentState);
2017            if (currentState == mPending) {
2018                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2019                    return BluetoothProfile.STATE_CONNECTING;
2020                }
2021                if (mConnectedDevicesList.contains(device)) {
2022                    return BluetoothProfile.STATE_DISCONNECTING;
2023                }
2024                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2025                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2026                }
2027                return BluetoothProfile.STATE_DISCONNECTED;
2028            }
2029
2030            if (currentState == mMultiHFPending) {
2031                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
2032                    return BluetoothProfile.STATE_CONNECTING;
2033                }
2034                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
2035                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
2036                }
2037                if (mConnectedDevicesList.contains(device)) {
2038                    if ((mMultiDisconnectDevice != null) &&
2039                            (!mMultiDisconnectDevice.equals(device))) {
2040                        // The device is still connected
2041                        return BluetoothProfile.STATE_CONNECTED;
2042                    }
2043                    return BluetoothProfile.STATE_DISCONNECTING;
2044                }
2045                return BluetoothProfile.STATE_DISCONNECTED;
2046            }
2047
2048            if (currentState == mConnected || currentState == mAudioOn) {
2049                if (mConnectedDevicesList.contains(device)) {
2050                    return BluetoothProfile.STATE_CONNECTED;
2051                }
2052                return BluetoothProfile.STATE_DISCONNECTED;
2053            } else {
2054                Log.e(TAG, "Bad currentState: " + currentState);
2055                return BluetoothProfile.STATE_DISCONNECTED;
2056            }
2057        }
2058    }
2059
2060    List<BluetoothDevice> getConnectedDevices() {
2061        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
2062        synchronized(this) {
2063            for (int i = 0; i < mConnectedDevicesList.size(); i++)
2064                devices.add(mConnectedDevicesList.get(i));
2065            }
2066
2067        return devices;
2068    }
2069
2070    boolean isAudioOn() {
2071        return (getCurrentState() == mAudioOn);
2072    }
2073
2074    boolean isAudioConnected(BluetoothDevice device) {
2075        synchronized(this) {
2076
2077            /*  Additional check for audio state included for the case when PhoneApp queries
2078            Bluetooth Audio state, before we receive the close event from the stack for the
2079            sco disconnect issued in AudioOn state. This was causing a mismatch in the
2080            Incall screen UI. */
2081
2082            if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
2083                && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
2084            {
2085                return true;
2086            }
2087        }
2088        return false;
2089    }
2090
2091    int getAudioState(BluetoothDevice device) {
2092        synchronized(this) {
2093            if (mConnectedDevicesList.size() == 0) {
2094                return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
2095            }
2096        }
2097        return mAudioState;
2098    }
2099
2100    private void processVrEvent(int state, BluetoothDevice device) {
2101
2102        if(device == null) {
2103            Log.w(TAG, "processVrEvent device is null");
2104            return;
2105        }
2106        Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " +
2107            mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition +
2108            " isInCall: " + isInCall());
2109        if (state == HeadsetHalConstants.VR_STATE_STARTED) {
2110            if (!isVirtualCallInProgress() &&
2111                !isInCall())
2112            {
2113                try {
2114                    mService.startActivity(sVoiceCommandIntent);
2115                } catch (ActivityNotFoundException e) {
2116                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2117                                        0, getByteAddress(device));
2118                    return;
2119                }
2120                expectVoiceRecognition(device);
2121            }
2122        } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
2123            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
2124            {
2125                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2126                                         0, getByteAddress(device));
2127                mVoiceRecognitionStarted = false;
2128                mWaitingForVoiceRecognition = false;
2129                if (!isInCall() && (mActiveScoDevice != null)) {
2130                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2131                    mAudioManager.setParameters("A2dpSuspended=false");
2132                }
2133            }
2134            else
2135            {
2136                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2137                                        0, getByteAddress(device));
2138            }
2139        } else {
2140            Log.e(TAG, "Bad Voice Recognition state: " + state);
2141        }
2142    }
2143
2144    private void processLocalVrEvent(int state)
2145    {
2146        BluetoothDevice device = null;
2147        if (state == HeadsetHalConstants.VR_STATE_STARTED)
2148        {
2149            boolean needAudio = true;
2150            if (mVoiceRecognitionStarted || isInCall())
2151            {
2152                Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() +
2153                    " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
2154                return;
2155            }
2156            mVoiceRecognitionStarted = true;
2157
2158            if (mWaitingForVoiceRecognition)
2159            {
2160                device = getDeviceForMessage(START_VR_TIMEOUT);
2161                if (device == null)
2162                    return;
2163
2164                Log.d(TAG, "Voice recognition started successfully");
2165                mWaitingForVoiceRecognition = false;
2166                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2167                                        0, getByteAddress(device));
2168                removeMessages(START_VR_TIMEOUT);
2169            }
2170            else
2171            {
2172                Log.d(TAG, "Voice recognition started locally");
2173                needAudio = startVoiceRecognitionNative(getByteAddress(mCurrentDevice));
2174                if (mCurrentDevice != null)
2175                    device = mCurrentDevice;
2176            }
2177
2178            if (needAudio && !isAudioOn())
2179            {
2180                Log.d(TAG, "Initiating audio connection for Voice Recognition");
2181                // At this stage, we need to be sure that AVDTP is not streaming. This is needed
2182                // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
2183                // streaming state while a SCO connection is established.
2184                // This is needed for VoiceDial scenario alone and not for
2185                // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
2186                // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
2187                // Whereas for VoiceDial we want to activate the SCO connection but we are still
2188                // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
2189                mAudioManager.setParameters("A2dpSuspended=true");
2190                connectAudioNative(getByteAddress(device));
2191            }
2192
2193            if (mStartVoiceRecognitionWakeLock.isHeld()) {
2194                mStartVoiceRecognitionWakeLock.release();
2195            }
2196        }
2197        else
2198        {
2199            Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted +
2200                " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
2201            if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
2202            {
2203                mVoiceRecognitionStarted = false;
2204                mWaitingForVoiceRecognition = false;
2205
2206                if (stopVoiceRecognitionNative(getByteAddress(mCurrentDevice))
2207                                && !isInCall() && mActiveScoDevice != null) {
2208                    disconnectAudioNative(getByteAddress(mActiveScoDevice));
2209                    mAudioManager.setParameters("A2dpSuspended=false");
2210                }
2211            }
2212        }
2213    }
2214
2215    private synchronized void expectVoiceRecognition(BluetoothDevice device) {
2216        mWaitingForVoiceRecognition = true;
2217        Message m = obtainMessage(START_VR_TIMEOUT);
2218        m.obj = getMatchingDevice(device);
2219        sendMessageDelayed(m, START_VR_TIMEOUT_VALUE);
2220
2221        if (!mStartVoiceRecognitionWakeLock.isHeld()) {
2222            mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
2223        }
2224    }
2225
2226    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2227        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
2228        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
2229        int connectionState;
2230        synchronized (this) {
2231            for (BluetoothDevice device : bondedDevices) {
2232                ParcelUuid[] featureUuids = device.getUuids();
2233                if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
2234                    continue;
2235                }
2236                connectionState = getConnectionState(device);
2237                for(int i = 0; i < states.length; i++) {
2238                    if (connectionState == states[i]) {
2239                        deviceList.add(device);
2240                    }
2241                }
2242            }
2243        }
2244        return deviceList;
2245    }
2246
2247    private BluetoothDevice getDeviceForMessage(int what)
2248    {
2249        if (what == CONNECT_TIMEOUT) {
2250            log("getDeviceForMessage: returning mTargetDevice for what=" + what);
2251            return mTargetDevice;
2252        }
2253        if (mConnectedDevicesList.size() == 0) {
2254            log("getDeviceForMessage: No connected device. what=" + what);
2255            return null;
2256        }
2257        for (BluetoothDevice device : mConnectedDevicesList)
2258        {
2259            if (getHandler().hasMessages(what, device))
2260            {
2261                log("getDeviceForMessage: returning " + device);
2262                return device;
2263            }
2264        }
2265        log("getDeviceForMessage: No matching device for " + what + ". Returning null");
2266        return null;
2267    }
2268
2269    private BluetoothDevice getMatchingDevice(BluetoothDevice device)
2270    {
2271        for (BluetoothDevice matchingDevice : mConnectedDevicesList)
2272        {
2273            if (matchingDevice.equals(device))
2274            {
2275                return matchingDevice;
2276            }
2277        }
2278        return null;
2279    }
2280
2281    // This method does not check for error conditon (newState == prevState)
2282    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
2283        log("Connection state " + device + ": " + prevState + "->" + newState);
2284        if(prevState == BluetoothProfile.STATE_CONNECTED) {
2285            // Headset is disconnecting, stop Virtual call if active.
2286            terminateScoUsingVirtualVoiceCall();
2287        }
2288
2289        /* Notifying the connection state change of the profile before sending the intent for
2290           connection state change, as it was causing a race condition, with the UI not being
2291           updated with the correct connection state. */
2292        mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,
2293                                                     newState, prevState);
2294        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
2295        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2296        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2297        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2298        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
2299    }
2300
2301    private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
2302        if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2303            // When SCO gets disconnected during call transfer, Virtual call
2304            //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
2305            terminateScoUsingVirtualVoiceCall();
2306        }
2307        Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
2308        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
2309        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
2310        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2311        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
2312        log("Audio state " + device + ": " + prevState + "->" + newState);
2313    }
2314
2315    /*
2316     * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
2317     */
2318    private void broadcastVendorSpecificEventIntent(String command,
2319                                                    int companyId,
2320                                                    int commandType,
2321                                                    Object[] arguments,
2322                                                    BluetoothDevice device) {
2323        log("broadcastVendorSpecificEventIntent(" + command + ")");
2324        Intent intent =
2325                new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
2326        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
2327        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
2328                        commandType);
2329        // assert: all elements of args are Serializable
2330        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
2331        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2332
2333        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
2334            + "." + Integer.toString(companyId));
2335
2336        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
2337    }
2338
2339    private void configAudioParameters(BluetoothDevice device)
2340    {
2341        // Reset NREC on connect event. Headset will override later
2342        HashMap<String, Integer> AudioParamConfig = new HashMap<String, Integer>();
2343        AudioParamConfig.put("NREC", 1);
2344        mHeadsetAudioParam.put(device, AudioParamConfig);
2345        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device) + ";" +
2346                                    HEADSET_NREC + "=on");
2347        Log.d(TAG, "configAudioParameters for device:" + device + " are: nrec = " +
2348                      AudioParamConfig.get("NREC"));
2349    }
2350
2351    private void setAudioParameters(BluetoothDevice device)
2352    {
2353        // 1. update nrec value
2354        // 2. update headset name
2355        HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device);
2356        int mNrec = AudioParam.get("NREC");
2357
2358        if (mNrec == 1) {
2359            Log.d(TAG, "Set NREC: 1 for device:" + device);
2360            mAudioManager.setParameters(HEADSET_NREC + "=on");
2361        } else {
2362            Log.d(TAG, "Set NREC: 0 for device:" + device);
2363            mAudioManager.setParameters(HEADSET_NREC + "=off");
2364        }
2365        mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName(device));
2366    }
2367
2368    private String parseUnknownAt(String atString)
2369    {
2370        StringBuilder atCommand = new StringBuilder(atString.length());
2371        String result = null;
2372
2373        for (int i = 0; i < atString.length(); i++) {
2374            char c = atString.charAt(i);
2375            if (c == '"') {
2376                int j = atString.indexOf('"', i + 1 );  // search for closing "
2377                if (j == -1) {  // unmatched ", insert one.
2378                    atCommand.append(atString.substring(i, atString.length()));
2379                    atCommand.append('"');
2380                    break;
2381                }
2382                atCommand.append(atString.substring(i, j + 1));
2383                i = j;
2384            } else if (c != ' ') {
2385                atCommand.append(Character.toUpperCase(c));
2386            }
2387        }
2388        result = atCommand.toString();
2389        return result;
2390    }
2391
2392    private int getAtCommandType(String atCommand)
2393    {
2394        int commandType = mPhonebook.TYPE_UNKNOWN;
2395        String atString = null;
2396        atCommand = atCommand.trim();
2397        if (atCommand.length() > 5)
2398        {
2399            atString = atCommand.substring(5);
2400            if (atString.startsWith("?"))     // Read
2401                commandType = mPhonebook.TYPE_READ;
2402            else if (atString.startsWith("=?"))   // Test
2403                commandType = mPhonebook.TYPE_TEST;
2404            else if (atString.startsWith("="))   // Set
2405                commandType = mPhonebook.TYPE_SET;
2406            else
2407                commandType = mPhonebook.TYPE_UNKNOWN;
2408        }
2409        return commandType;
2410    }
2411
2412    /* Method to check if Virtual Call in Progress */
2413    private boolean isVirtualCallInProgress() {
2414        return mVirtualCallStarted;
2415    }
2416
2417    void setVirtualCallInProgress(boolean state) {
2418        mVirtualCallStarted = state;
2419    }
2420
2421    /* NOTE: Currently the VirtualCall API does not support handling of
2422    call transfers. If it is initiated from the handsfree device,
2423    HeadsetStateMachine will end the virtual call by calling
2424    terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
2425    synchronized boolean initiateScoUsingVirtualVoiceCall() {
2426        if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
2427        // 1. Check if the SCO state is idle
2428        if (isInCall() || mVoiceRecognitionStarted) {
2429            Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
2430            return false;
2431        }
2432
2433        // 2. Send virtual phone state changed to initialize SCO
2434        processCallState(new HeadsetCallState(0, 0,
2435            HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
2436        processCallState(new HeadsetCallState(0, 0,
2437            HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
2438        processCallState(new HeadsetCallState(1, 0,
2439            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2440        setVirtualCallInProgress(true);
2441        // Done
2442        if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
2443        return true;
2444    }
2445
2446    synchronized boolean terminateScoUsingVirtualVoiceCall() {
2447        if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
2448
2449        if (!isVirtualCallInProgress()) {
2450            Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+
2451                "No present call to terminate");
2452            return false;
2453        }
2454
2455        // 2. Send virtual phone state changed to close SCO
2456        processCallState(new HeadsetCallState(0, 0,
2457            HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
2458        setVirtualCallInProgress(false);
2459        // Done
2460        if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
2461        return true;
2462    }
2463
2464    private void processAnswerCall(BluetoothDevice device) {
2465        if(device == null) {
2466            Log.w(TAG, "processAnswerCall device is null");
2467            return;
2468        }
2469
2470        if (mPhoneProxy != null) {
2471            try {
2472                mPhoneProxy.answerCall();
2473            } catch (RemoteException e) {
2474                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2475            }
2476        } else {
2477            Log.e(TAG, "Handsfree phone proxy null for answering call");
2478        }
2479    }
2480
2481    private void processHangupCall(BluetoothDevice device) {
2482        if(device == null) {
2483            Log.w(TAG, "processHangupCall device is null");
2484            return;
2485        }
2486        // Close the virtual call if active. Virtual call should be
2487        // terminated for CHUP callback event
2488        if (isVirtualCallInProgress()) {
2489            terminateScoUsingVirtualVoiceCall();
2490        } else {
2491            if (mPhoneProxy != null) {
2492                try {
2493                    mPhoneProxy.hangupCall();
2494                } catch (RemoteException e) {
2495                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2496                }
2497            } else {
2498                Log.e(TAG, "Handsfree phone proxy null for hanging up call");
2499            }
2500        }
2501    }
2502
2503    private void processDialCall(String number, BluetoothDevice device) {
2504        if(device == null) {
2505            Log.w(TAG, "processDialCall device is null");
2506            return;
2507        }
2508
2509        String dialNumber;
2510        if ((number == null) || (number.length() == 0)) {
2511            dialNumber = mPhonebook.getLastDialledNumber();
2512            if (dialNumber == null) {
2513                if (DBG) log("processDialCall, last dial number null");
2514                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2515                                       getByteAddress(device));
2516                return;
2517            }
2518        } else if (number.charAt(0) == '>') {
2519            // Yuck - memory dialling requested.
2520            // Just dial last number for now
2521            if (number.startsWith(">9999")) {   // for PTS test
2522                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2523                                       getByteAddress(device));
2524                return;
2525            }
2526            if (DBG) log("processDialCall, memory dial do last dial for now");
2527            dialNumber = mPhonebook.getLastDialledNumber();
2528            if (dialNumber == null) {
2529                if (DBG) log("processDialCall, last dial number null");
2530                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
2531                                       getByteAddress(device));
2532                return;
2533            }
2534        } else {
2535            // Remove trailing ';'
2536            if (number.charAt(number.length() - 1) == ';') {
2537                number = number.substring(0, number.length() - 1);
2538            }
2539
2540            dialNumber = PhoneNumberUtils.convertPreDial(number);
2541        }
2542        // Check for virtual call to terminate before sending Call Intent
2543        terminateScoUsingVirtualVoiceCall();
2544
2545        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
2546                                   Uri.fromParts(SCHEME_TEL, dialNumber, null));
2547        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2548        mService.startActivity(intent);
2549        // TODO(BT) continue send OK reults code after call starts
2550        //          hold wait lock, start a timer, set wait call flag
2551        //          Get call started indication from bluetooth phone
2552        mDialingOut = true;
2553        Message m = obtainMessage(DIALING_OUT_TIMEOUT);
2554        m.obj = getMatchingDevice(device);
2555        sendMessageDelayed(m, DIALING_OUT_TIMEOUT_VALUE);
2556    }
2557
2558    private void processVolumeEvent(int volumeType, int volume, BluetoothDevice device) {
2559        if(device != null && !device.equals(mActiveScoDevice) && mPhoneState.isInCall()) {
2560            Log.w(TAG, "ignore processVolumeEvent");
2561            return;
2562        }
2563
2564        if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
2565            mPhoneState.setSpeakerVolume(volume);
2566            int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
2567            mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
2568        } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
2569            mPhoneState.setMicVolume(volume);
2570        } else {
2571            Log.e(TAG, "Bad voluem type: " + volumeType);
2572        }
2573    }
2574
2575    private void processSendDtmf(int dtmf, BluetoothDevice device) {
2576        if(device == null) {
2577            Log.w(TAG, "processSendDtmf device is null");
2578            return;
2579        }
2580
2581        if (mPhoneProxy != null) {
2582            try {
2583                mPhoneProxy.sendDtmf(dtmf);
2584            } catch (RemoteException e) {
2585                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2586            }
2587        } else {
2588            Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
2589        }
2590    }
2591
2592    private void processCallState(HeadsetCallState callState) {
2593        processCallState(callState, false);
2594    }
2595
2596    private void processCallState(HeadsetCallState callState,
2597        boolean isVirtualCall) {
2598        mPhoneState.setNumActiveCall(callState.mNumActive);
2599        mPhoneState.setNumHeldCall(callState.mNumHeld);
2600        mPhoneState.setCallState(callState.mCallState);
2601        if (mDialingOut) {
2602            if (callState.mCallState ==
2603                HeadsetHalConstants.CALL_STATE_DIALING) {
2604                BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT);
2605                if (device == null) {
2606                    return;
2607                }
2608                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2609                                                       0, getByteAddress(device));
2610                removeMessages(DIALING_OUT_TIMEOUT);
2611            } else if (callState.mCallState ==
2612                HeadsetHalConstants.CALL_STATE_ACTIVE || callState.mCallState
2613                == HeadsetHalConstants.CALL_STATE_IDLE) {
2614                mDialingOut = false;
2615            }
2616        }
2617
2618        /* Set ActiveScoDevice to null when call ends */
2619        if ((mActiveScoDevice != null) && !isInCall() &&
2620                callState.mCallState == HeadsetHalConstants.CALL_STATE_IDLE)
2621            mActiveScoDevice = null;
2622
2623        log("mNumActive: " + callState.mNumActive + " mNumHeld: " +
2624            callState.mNumHeld +" mCallState: " + callState.mCallState);
2625        log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
2626        if(!isVirtualCall) {
2627            /* Not a Virtual call request. End the virtual call, if running,
2628            before sending phoneStateChangeNative to BTIF */
2629            terminateScoUsingVirtualVoiceCall();
2630        }
2631        if (getCurrentState() != mDisconnected) {
2632            phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
2633                callState.mCallState, callState.mNumber, callState.mType);
2634        }
2635    }
2636
2637    // 1 enable noice reduction
2638    // 0 disable noice reduction
2639    private void processNoiceReductionEvent(int enable, BluetoothDevice device) {
2640        HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device);
2641        if (enable == 1)
2642            AudioParamNrec.put("NREC", 1);
2643        else
2644            AudioParamNrec.put("NREC", 0);
2645        Log.d(TAG, "NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC"));
2646    }
2647
2648    // 2 - WBS on
2649    // 1 - NBS on
2650    private void processWBSEvent(int enable, BluetoothDevice device) {
2651        if (enable == 2) {
2652            Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " +
2653                        device.getName() + " - " + device.getAddress());
2654            mAudioManager.setParameters(HEADSET_WBS + "=on");
2655        } else {
2656            Log.d(TAG, "AudioManager.setParameters bt_wbs=off for " +
2657                        device.getName() + " - " + device.getAddress());
2658            mAudioManager.setParameters(HEADSET_WBS + "=off");
2659        }
2660    }
2661
2662    private void processAtChld(int chld, BluetoothDevice device) {
2663        if(device == null) {
2664            Log.w(TAG, "processAtChld device is null");
2665            return;
2666        }
2667
2668        if (mPhoneProxy != null) {
2669            try {
2670                if (mPhoneProxy.processChld(chld)) {
2671                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2672                                               0, getByteAddress(device));
2673                } else {
2674                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2675                                               0, getByteAddress(device));
2676                }
2677            } catch (RemoteException e) {
2678                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2679                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2680                                               0, getByteAddress(device));
2681            }
2682        } else {
2683            Log.e(TAG, "Handsfree phone proxy null for At+Chld");
2684            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2685                                               0, getByteAddress(device));
2686        }
2687    }
2688
2689    private void processSubscriberNumberRequest(BluetoothDevice device) {
2690        if(device == null) {
2691            Log.w(TAG, "processSubscriberNumberRequest device is null");
2692            return;
2693        }
2694
2695        if (mPhoneProxy != null) {
2696            try {
2697                String number = mPhoneProxy.getSubscriberNumber();
2698                if (number != null) {
2699                    atResponseStringNative("+CNUM: ,\"" + number + "\"," +
2700                                           PhoneNumberUtils.toaFromString(number) +
2701                                             ",,4", getByteAddress(device));
2702                    atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
2703                                                 0, getByteAddress(device));
2704                }
2705            } catch (RemoteException e) {
2706                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2707                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR,
2708                                                 0, getByteAddress(device));
2709            }
2710        } else {
2711            Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
2712        }
2713    }
2714
2715    private void processAtCind(BluetoothDevice device) {
2716        int call, call_setup;
2717
2718        if(device == null) {
2719            Log.w(TAG, "processAtCind device is null");
2720            return;
2721        }
2722
2723        /* Handsfree carkits expect that +CIND is properly responded to
2724         Hence we ensure that a proper response is sent
2725         for the virtual call too.*/
2726        if (isVirtualCallInProgress()) {
2727            call = 1;
2728            call_setup = 0;
2729        } else {
2730            // regular phone call
2731            call = mPhoneState.getNumActiveCall();
2732            call_setup = mPhoneState.getNumHeldCall();
2733        }
2734
2735        cindResponseNative(mPhoneState.getService(), call,
2736                           call_setup, mPhoneState.getCallState(),
2737                           mPhoneState.getSignal(), mPhoneState.getRoam(),
2738                           mPhoneState.getBatteryCharge(), getByteAddress(device));
2739    }
2740
2741    private void processAtCops(BluetoothDevice device) {
2742        if(device == null) {
2743            Log.w(TAG, "processAtCops device is null");
2744            return;
2745        }
2746
2747        if (mPhoneProxy != null) {
2748            try {
2749                String operatorName = mPhoneProxy.getNetworkOperator();
2750                if (operatorName == null) {
2751                    operatorName = "";
2752                }
2753                copsResponseNative(operatorName, getByteAddress(device));
2754            } catch (RemoteException e) {
2755                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2756                copsResponseNative("", getByteAddress(device));
2757            }
2758        } else {
2759            Log.e(TAG, "Handsfree phone proxy null for At+COPS");
2760            copsResponseNative("", getByteAddress(device));
2761        }
2762    }
2763
2764    private void processAtClcc(BluetoothDevice device) {
2765        if(device == null) {
2766            Log.w(TAG, "processAtClcc device is null");
2767            return;
2768        }
2769
2770        if (mPhoneProxy != null) {
2771            try {
2772                if(isVirtualCallInProgress()) {
2773                    String phoneNumber = "";
2774                    int type = PhoneNumberUtils.TOA_Unknown;
2775                    try {
2776                        phoneNumber = mPhoneProxy.getSubscriberNumber();
2777                        type = PhoneNumberUtils.toaFromString(phoneNumber);
2778                    } catch (RemoteException ee) {
2779                        Log.e(TAG, "Unable to retrieve phone number"+
2780                            "using IBluetoothHeadsetPhone proxy");
2781                        phoneNumber = "";
2782                    }
2783                    clccResponseNative(1, 0, 0, 0, false, phoneNumber, type,
2784                                                       getByteAddress(device));
2785                    clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2786                }
2787                else if (!mPhoneProxy.listCurrentCalls()) {
2788                    clccResponseNative(0, 0, 0, 0, false, "", 0,
2789                                                       getByteAddress(device));
2790                }
2791                else
2792                {
2793                    Log.d(TAG, "Starting CLCC response timeout for device: "
2794                                                                     + device);
2795                    Message m = obtainMessage(CLCC_RSP_TIMEOUT);
2796                    m.obj = getMatchingDevice(device);
2797                    sendMessageDelayed(m, CLCC_RSP_TIMEOUT_VALUE);
2798                }
2799            } catch (RemoteException e) {
2800                Log.e(TAG, Log.getStackTraceString(new Throwable()));
2801                clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2802            }
2803        } else {
2804            Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
2805            clccResponseNative(0, 0, 0, 0, false, "", 0, getByteAddress(device));
2806        }
2807    }
2808
2809    private void processAtCscs(String atString, int type, BluetoothDevice device) {
2810        log("processAtCscs - atString = "+ atString);
2811        if(mPhonebook != null) {
2812            mPhonebook.handleCscsCommand(atString, type, device);
2813        }
2814        else {
2815            Log.e(TAG, "Phonebook handle null for At+CSCS");
2816            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2817        }
2818    }
2819
2820    private void processAtCpbs(String atString, int type, BluetoothDevice device) {
2821        log("processAtCpbs - atString = "+ atString);
2822        if(mPhonebook != null) {
2823            mPhonebook.handleCpbsCommand(atString, type, device);
2824        }
2825        else {
2826            Log.e(TAG, "Phonebook handle null for At+CPBS");
2827            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2828        }
2829    }
2830
2831    private void processAtCpbr(String atString, int type, BluetoothDevice device) {
2832        log("processAtCpbr - atString = "+ atString);
2833        if(mPhonebook != null) {
2834            mPhonebook.handleCpbrCommand(atString, type, device);
2835        }
2836        else {
2837            Log.e(TAG, "Phonebook handle null for At+CPBR");
2838            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2839        }
2840    }
2841
2842    /**
2843     * Find a character ch, ignoring quoted sections.
2844     * Return input.length() if not found.
2845     */
2846    static private int findChar(char ch, String input, int fromIndex) {
2847        for (int i = fromIndex; i < input.length(); i++) {
2848            char c = input.charAt(i);
2849            if (c == '"') {
2850                i = input.indexOf('"', i + 1);
2851                if (i == -1) {
2852                    return input.length();
2853                }
2854            } else if (c == ch) {
2855                return i;
2856            }
2857        }
2858        return input.length();
2859    }
2860
2861    /**
2862     * Break an argument string into individual arguments (comma delimited).
2863     * Integer arguments are turned into Integer objects. Otherwise a String
2864     * object is used.
2865     */
2866    static private Object[] generateArgs(String input) {
2867        int i = 0;
2868        int j;
2869        ArrayList<Object> out = new ArrayList<Object>();
2870        while (i <= input.length()) {
2871            j = findChar(',', input, i);
2872
2873            String arg = input.substring(i, j);
2874            try {
2875                out.add(new Integer(arg));
2876            } catch (NumberFormatException e) {
2877                out.add(arg);
2878            }
2879
2880            i = j + 1; // move past comma
2881        }
2882        return out.toArray();
2883    }
2884
2885    /**
2886     * @return {@code true} if the given string is a valid vendor-specific AT command.
2887     */
2888    private boolean processVendorSpecificAt(String atString) {
2889        log("processVendorSpecificAt - atString = " + atString);
2890
2891        // Currently we accept only SET type commands.
2892        int indexOfEqual = atString.indexOf("=");
2893        if (indexOfEqual == -1) {
2894            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2895            return false;
2896        }
2897
2898        String command = atString.substring(0, indexOfEqual);
2899        Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
2900        if (companyId == null) {
2901            Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
2902            return false;
2903        }
2904
2905        String arg = atString.substring(indexOfEqual + 1);
2906        if (arg.startsWith("?")) {
2907            Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
2908            return false;
2909        }
2910
2911        Object[] args = generateArgs(arg);
2912        broadcastVendorSpecificEventIntent(command,
2913                                           companyId,
2914                                           BluetoothHeadset.AT_CMD_TYPE_SET,
2915                                           args,
2916                                           mCurrentDevice);
2917        atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(mCurrentDevice));
2918        return true;
2919    }
2920
2921    private void processUnknownAt(String atString, BluetoothDevice device) {
2922        if(device == null) {
2923            Log.w(TAG, "processUnknownAt device is null");
2924            return;
2925        }
2926
2927        // TODO (BT)
2928        log("processUnknownAt - atString = "+ atString);
2929        String atCommand = parseUnknownAt(atString);
2930        int commandType = getAtCommandType(atCommand);
2931        if (atCommand.startsWith("+CSCS"))
2932            processAtCscs(atCommand.substring(5), commandType, device);
2933        else if (atCommand.startsWith("+CPBS"))
2934            processAtCpbs(atCommand.substring(5), commandType, device);
2935        else if (atCommand.startsWith("+CPBR"))
2936            processAtCpbr(atCommand.substring(5), commandType, device);
2937        else if (!processVendorSpecificAt(atCommand))
2938            atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0, getByteAddress(device));
2939    }
2940
2941    private void processKeyPressed(BluetoothDevice device) {
2942        if(device == null) {
2943            Log.w(TAG, "processKeyPressed device is null");
2944            return;
2945        }
2946
2947        if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
2948            if (mPhoneProxy != null) {
2949                try {
2950                    mPhoneProxy.answerCall();
2951                } catch (RemoteException e) {
2952                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
2953                }
2954            } else {
2955                Log.e(TAG, "Handsfree phone proxy null for answering call");
2956            }
2957        } else if (mPhoneState.getNumActiveCall() > 0) {
2958            if (!isAudioOn())
2959            {
2960                connectAudioNative(getByteAddress(mCurrentDevice));
2961            }
2962            else
2963            {
2964                if (mPhoneProxy != null) {
2965                    try {
2966                        mPhoneProxy.hangupCall();
2967                    } catch (RemoteException e) {
2968                        Log.e(TAG, Log.getStackTraceString(new Throwable()));
2969                    }
2970                } else {
2971                    Log.e(TAG, "Handsfree phone proxy null for hangup call");
2972                }
2973            }
2974        } else {
2975            String dialNumber = mPhonebook.getLastDialledNumber();
2976            if (dialNumber == null) {
2977                if (DBG) log("processKeyPressed, last dial number null");
2978                return;
2979            }
2980            Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
2981                                       Uri.fromParts(SCHEME_TEL, dialNumber, null));
2982            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2983            mService.startActivity(intent);
2984        }
2985    }
2986
2987    private void onConnectionStateChanged(int state, byte[] address) {
2988        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
2989        event.valueInt = state;
2990        event.device = getDevice(address);
2991        sendMessage(STACK_EVENT, event);
2992    }
2993
2994    private void onAudioStateChanged(int state, byte[] address) {
2995        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
2996        event.valueInt = state;
2997        event.device = getDevice(address);
2998        sendMessage(STACK_EVENT, event);
2999    }
3000
3001    private void onVrStateChanged(int state, byte[] address) {
3002        StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
3003        event.valueInt = state;
3004        event.device = getDevice(address);
3005        sendMessage(STACK_EVENT, event);
3006    }
3007
3008    private void onAnswerCall(byte[] address) {
3009        StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
3010        event.device = getDevice(address);
3011        sendMessage(STACK_EVENT, event);
3012    }
3013
3014    private void onHangupCall(byte[] address) {
3015        StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
3016        event.device = getDevice(address);
3017        sendMessage(STACK_EVENT, event);
3018    }
3019
3020    private void onVolumeChanged(int type, int volume, byte[] address) {
3021        StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
3022        event.valueInt = type;
3023        event.valueInt2 = volume;
3024        event.device = getDevice(address);
3025        sendMessage(STACK_EVENT, event);
3026    }
3027
3028    private void onDialCall(String number, byte[] address) {
3029        StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
3030        event.valueString = number;
3031        event.device = getDevice(address);
3032        sendMessage(STACK_EVENT, event);
3033    }
3034
3035    private void onSendDtmf(int dtmf, byte[] address) {
3036        StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
3037        event.valueInt = dtmf;
3038        event.device = getDevice(address);
3039        sendMessage(STACK_EVENT, event);
3040    }
3041
3042    private void onNoiceReductionEnable(boolean enable,  byte[] address) {
3043        StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
3044        event.valueInt = enable ? 1 : 0;
3045        event.device = getDevice(address);
3046        sendMessage(STACK_EVENT, event);
3047    }
3048
3049    private void onWBS(int codec, byte[] address) {
3050        StackEvent event = new StackEvent(EVENT_TYPE_WBS);
3051        event.valueInt = codec;
3052        event.device = getDevice(address);
3053        sendMessage(STACK_EVENT, event);
3054    }
3055
3056    private void onAtChld(int chld, byte[] address) {
3057        StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
3058        event.valueInt = chld;
3059        event.device = getDevice(address);
3060        sendMessage(STACK_EVENT, event);
3061    }
3062
3063    private void onAtCnum(byte[] address) {
3064        StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
3065        event.device = getDevice(address);
3066        sendMessage(STACK_EVENT, event);
3067    }
3068
3069    private void onAtCind(byte[] address) {
3070        StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
3071        event.device = getDevice(address);
3072        sendMessage(STACK_EVENT, event);
3073    }
3074
3075    private void onAtCops(byte[] address) {
3076        StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
3077        event.device = getDevice(address);
3078        sendMessage(STACK_EVENT, event);
3079    }
3080
3081    private void onAtClcc(byte[] address) {
3082        StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
3083        event.device = getDevice(address);
3084        sendMessage(STACK_EVENT, event);
3085    }
3086
3087    private void onUnknownAt(String atString, byte[] address) {
3088        StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
3089        event.valueString = atString;
3090        event.device = getDevice(address);
3091        sendMessage(STACK_EVENT, event);
3092    }
3093
3094    private void onKeyPressed(byte[] address) {
3095        StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
3096        event.device = getDevice(address);
3097        sendMessage(STACK_EVENT, event);
3098    }
3099
3100    private void processIntentBatteryChanged(Intent intent) {
3101        int batteryLevel = intent.getIntExtra("level", -1);
3102        int scale = intent.getIntExtra("scale", -1);
3103        if (batteryLevel == -1 || scale == -1 || scale == 0) {
3104            Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
3105            return;
3106        }
3107        batteryLevel = batteryLevel * 5 / scale;
3108        mPhoneState.setBatteryCharge(batteryLevel);
3109    }
3110
3111    private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
3112        notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
3113                                 deviceState.mBatteryCharge);
3114    }
3115
3116    private void processSendClccResponse(HeadsetClccResponse clcc) {
3117        BluetoothDevice device = getDeviceForMessage(CLCC_RSP_TIMEOUT);
3118        if (device == null) {
3119            return;
3120        }
3121        if (clcc.mIndex == 0) {
3122            removeMessages(CLCC_RSP_TIMEOUT);
3123        }
3124        clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
3125                           clcc.mNumber, clcc.mType, getByteAddress(device));
3126    }
3127
3128    private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
3129        String stringToSend = resultCode.mCommand + ": ";
3130        if (resultCode.mArg != null) {
3131            stringToSend += resultCode.mArg;
3132        }
3133        atResponseStringNative(stringToSend, getByteAddress(resultCode.mDevice));
3134    }
3135
3136    private String getCurrentDeviceName(BluetoothDevice device) {
3137        String defaultName = "<unknown>";
3138
3139        if(device == null) {
3140            return defaultName;
3141        }
3142
3143        String deviceName = device.getName();
3144        if (deviceName == null) {
3145            return defaultName;
3146        }
3147        return deviceName;
3148    }
3149
3150    private byte[] getByteAddress(BluetoothDevice device) {
3151        return Utils.getBytesFromAddress(device.getAddress());
3152    }
3153
3154    private BluetoothDevice getDevice(byte[] address) {
3155        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
3156    }
3157
3158    private boolean isInCall() {
3159        return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
3160                (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
3161    }
3162
3163    boolean isConnected() {
3164        IState currentState = getCurrentState();
3165        return (currentState == mConnected || currentState == mAudioOn);
3166    }
3167
3168    boolean okToConnect(BluetoothDevice device) {
3169        AdapterService adapterService = AdapterService.getAdapterService();
3170        int priority = mService.getPriority(device);
3171        boolean ret = false;
3172        //check if this is an incoming connection in Quiet mode.
3173        if((adapterService == null) ||
3174           ((adapterService.isQuietModeEnabled() == true) &&
3175           (mTargetDevice == null))){
3176            ret = false;
3177        }
3178        // check priority and accept or reject the connection. if priority is undefined
3179        // it is likely that our SDP has not completed and peer is initiating the
3180        // connection. Allow this connection, provided the device is bonded
3181        else if((BluetoothProfile.PRIORITY_OFF < priority) ||
3182                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
3183                (device.getBondState() != BluetoothDevice.BOND_NONE))){
3184            ret= true;
3185        }
3186        return ret;
3187    }
3188
3189    @Override
3190    protected void log(String msg) {
3191        if (DBG) {
3192            super.log(msg);
3193        }
3194    }
3195
3196    public void handleAccessPermissionResult(Intent intent) {
3197        log("handleAccessPermissionResult");
3198        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3199        if (mPhonebook != null) {
3200            if (!mPhonebook.getCheckingAccessPermission()) {
3201                return;
3202            }
3203            int atCommandResult = 0;
3204            int atCommandErrorCode = 0;
3205            //HeadsetBase headset = mHandsfree.getHeadset();
3206            // ASSERT: (headset != null) && headSet.isConnected()
3207            // REASON: mCheckingAccessPermission is true, otherwise resetAtState
3208            // has set mCheckingAccessPermission to false
3209            if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
3210                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
3211                                       BluetoothDevice.CONNECTION_ACCESS_NO)
3212                        == BluetoothDevice.CONNECTION_ACCESS_YES) {
3213                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3214                        mCurrentDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
3215                    }
3216                    atCommandResult = mPhonebook.processCpbrCommand(device);
3217                } else {
3218                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
3219                        mCurrentDevice.setPhonebookAccessPermission(
3220                                BluetoothDevice.ACCESS_REJECTED);
3221                    }
3222                }
3223            }
3224            mPhonebook.setCpbrIndex(-1);
3225            mPhonebook.setCheckingAccessPermission(false);
3226
3227            if (atCommandResult >= 0) {
3228                atResponseCodeNative(atCommandResult, atCommandErrorCode, getByteAddress(device));
3229            } else {
3230                log("handleAccessPermissionResult - RESULT_NONE");
3231            }
3232        } else {
3233            Log.e(TAG, "Phonebook handle null");
3234            if (device != null) {
3235                atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0,
3236                                     getByteAddress(device));
3237            }
3238        }
3239    }
3240
3241    private static final String SCHEME_TEL = "tel";
3242
3243    // Event types for STACK_EVENT message
3244    final private static int EVENT_TYPE_NONE = 0;
3245    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
3246    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
3247    final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
3248    final private static int EVENT_TYPE_ANSWER_CALL = 4;
3249    final private static int EVENT_TYPE_HANGUP_CALL = 5;
3250    final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
3251    final private static int EVENT_TYPE_DIAL_CALL = 7;
3252    final private static int EVENT_TYPE_SEND_DTMF = 8;
3253    final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
3254    final private static int EVENT_TYPE_AT_CHLD = 10;
3255    final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
3256    final private static int EVENT_TYPE_AT_CIND = 12;
3257    final private static int EVENT_TYPE_AT_COPS = 13;
3258    final private static int EVENT_TYPE_AT_CLCC = 14;
3259    final private static int EVENT_TYPE_UNKNOWN_AT = 15;
3260    final private static int EVENT_TYPE_KEY_PRESSED = 16;
3261    final private static int EVENT_TYPE_WBS = 17;
3262
3263    private class StackEvent {
3264        int type = EVENT_TYPE_NONE;
3265        int valueInt = 0;
3266        int valueInt2 = 0;
3267        String valueString = null;
3268        BluetoothDevice device = null;
3269
3270        private StackEvent(int type) {
3271            this.type = type;
3272        }
3273    }
3274
3275    /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode,
3276                                                                          byte[] address);
3277    /*package*/ native boolean atResponseStringNative(String responseString, byte[] address);
3278
3279    private native static void classInitNative();
3280    private native void initializeNative(int max_hf_clients);
3281    private native void cleanupNative();
3282    private native boolean connectHfpNative(byte[] address);
3283    private native boolean disconnectHfpNative(byte[] address);
3284    private native boolean connectAudioNative(byte[] address);
3285    private native boolean disconnectAudioNative(byte[] address);
3286    private native boolean startVoiceRecognitionNative(byte[] address);
3287    private native boolean stopVoiceRecognitionNative(byte[] address);
3288    private native boolean setVolumeNative(int volumeType, int volume, byte[] address);
3289    private native boolean cindResponseNative(int service, int numActive, int numHeld,
3290                                              int callState, int signal, int roam,
3291                                              int batteryCharge, byte[] address);
3292    private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
3293                                                    int batteryCharge);
3294
3295    private native boolean clccResponseNative(int index, int dir, int status, int mode,
3296                                              boolean mpty, String number, int type,
3297                                                                           byte[] address);
3298    private native boolean copsResponseNative(String operatorName, byte[] address);
3299
3300    private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
3301                                                  String number, int type);
3302    private native boolean configureWBSNative(byte[] address,int condec_config);
3303}
3304