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