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