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