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