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 
     70 final class HeadsetStateMachine extends StateMachine {
     71     private static final String TAG = "HeadsetStateMachine";
     72     private static final boolean DBG = false;
     73     //For Debugging only
     74     private static int sRefCount=0;
     75 
     76     private static final String HEADSET_NAME = "bt_headset_name";
     77     private static final String HEADSET_NREC = "bt_headset_nrec";
     78 
     79     static final int CONNECT = 1;
     80     static final int DISCONNECT = 2;
     81     static final int CONNECT_AUDIO = 3;
     82     static final int DISCONNECT_AUDIO = 4;
     83     static final int VOICE_RECOGNITION_START = 5;
     84     static final int VOICE_RECOGNITION_STOP = 6;
     85 
     86     // message.obj is an intent AudioManager.VOLUME_CHANGED_ACTION
     87     // EXTRA_VOLUME_STREAM_TYPE is STREAM_BLUETOOTH_SCO
     88     static final int INTENT_SCO_VOLUME_CHANGED = 7;
     89     static final int SET_MIC_VOLUME = 8;
     90     static final int CALL_STATE_CHANGED = 9;
     91     static final int INTENT_BATTERY_CHANGED = 10;
     92     static final int DEVICE_STATE_CHANGED = 11;
     93     static final int SEND_CCLC_RESPONSE = 12;
     94     static final int SEND_VENDOR_SPECIFIC_RESULT_CODE = 13;
     95 
     96     static final int VIRTUAL_CALL_START = 14;
     97     static final int VIRTUAL_CALL_STOP = 15;
     98 
     99     private static final int STACK_EVENT = 101;
    100     private static final int DIALING_OUT_TIMEOUT = 102;
    101     private static final int START_VR_TIMEOUT = 103;
    102 
    103     private static final int CONNECT_TIMEOUT = 201;
    104 
    105     private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
    106     private static final int START_VR_TIMEOUT_VALUE = 5000;
    107 
    108     // Keys are AT commands, and values are the company IDs.
    109     private static final Map<String, Integer> VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID;
    110 
    111     private static final ParcelUuid[] HEADSET_UUIDS = {
    112         BluetoothUuid.HSP,
    113         BluetoothUuid.Handsfree,
    114     };
    115 
    116     private Disconnected mDisconnected;
    117     private Pending mPending;
    118     private Connected mConnected;
    119     private AudioOn mAudioOn;
    120 
    121     private HeadsetService mService;
    122     private PowerManager mPowerManager;
    123     private boolean mVirtualCallStarted = false;
    124     private boolean mVoiceRecognitionStarted = false;
    125     private boolean mWaitingForVoiceRecognition = false;
    126     private WakeLock mStartVoiceRecognitionWakeLock;  // held while waiting for voice recognition
    127 
    128     private boolean mDialingOut = false;
    129     private AudioManager mAudioManager;
    130     private AtPhonebook mPhonebook;
    131 
    132     private static Intent sVoiceCommandIntent;
    133 
    134     private HeadsetPhoneState mPhoneState;
    135     private int mAudioState;
    136     private BluetoothAdapter mAdapter;
    137     private IBluetoothHeadsetPhone mPhoneProxy;
    138     private boolean mNativeAvailable;
    139 
    140     // mCurrentDevice is the device connected before the state changes
    141     // mTargetDevice is the device to be connected
    142     // mIncomingDevice is the device connecting to us, valid only in Pending state
    143     //                when mIncomingDevice is not null, both mCurrentDevice
    144     //                  and mTargetDevice are null
    145     //                when either mCurrentDevice or mTargetDevice is not null,
    146     //                  mIncomingDevice is null
    147     // Stable states
    148     //   No connection, Disconnected state
    149     //                  both mCurrentDevice and mTargetDevice are null
    150     //   Connected, Connected state
    151     //              mCurrentDevice is not null, mTargetDevice is null
    152     // Interim states
    153     //   Connecting to a device, Pending
    154     //                           mCurrentDevice is null, mTargetDevice is not null
    155     //   Disconnecting device, Connecting to new device
    156     //     Pending
    157     //     Both mCurrentDevice and mTargetDevice are not null
    158     //   Disconnecting device Pending
    159     //                        mCurrentDevice is not null, mTargetDevice is null
    160     //   Incoming connections Pending
    161     //                        Both mCurrentDevice and mTargetDevice are null
    162     private BluetoothDevice mCurrentDevice = null;
    163     private BluetoothDevice mTargetDevice = null;
    164     private BluetoothDevice mIncomingDevice = null;
    165 
    166     static {
    167         classInitNative();
    168 
    169         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap<String, Integer>();
    170         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT", BluetoothAssignedNumbers.PLANTRONICS);
    171         VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID", BluetoothAssignedNumbers.GOOGLE);
    172     }
    173 
    174     private HeadsetStateMachine(HeadsetService context) {
    175         super(TAG);
    176         mService = context;
    177         mVoiceRecognitionStarted = false;
    178         mWaitingForVoiceRecognition = false;
    179 
    180         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    181         mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    182                                                        TAG + ":VoiceRecognition");
    183         mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
    184 
    185         mDialingOut = false;
    186         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    187         mPhonebook = new AtPhonebook(mService, this);
    188         mPhoneState = new HeadsetPhoneState(context, this);
    189         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    190         mAdapter = BluetoothAdapter.getDefaultAdapter();
    191         Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
    192         intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
    193         if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
    194             Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
    195         }
    196 
    197         initializeNative();
    198         mNativeAvailable=true;
    199 
    200         mDisconnected = new Disconnected();
    201         mPending = new Pending();
    202         mConnected = new Connected();
    203         mAudioOn = new AudioOn();
    204 
    205         if (sVoiceCommandIntent == null) {
    206             sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
    207             sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    208         }
    209 
    210         addState(mDisconnected);
    211         addState(mPending);
    212         addState(mConnected);
    213         addState(mAudioOn);
    214 
    215         setInitialState(mDisconnected);
    216     }
    217 
    218     static HeadsetStateMachine make(HeadsetService context) {
    219         Log.d(TAG, "make");
    220         HeadsetStateMachine hssm = new HeadsetStateMachine(context);
    221         hssm.start();
    222         return hssm;
    223     }
    224 
    225 
    226     public void doQuit() {
    227         quitNow();
    228     }
    229 
    230     public void cleanup() {
    231         if (mPhoneProxy != null) {
    232             if (DBG) Log.d(TAG,"Unbinding service...");
    233             synchronized (mConnection) {
    234                 try {
    235                     mPhoneProxy = null;
    236                     mService.unbindService(mConnection);
    237                 } catch (Exception re) {
    238                     Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re);
    239                 }
    240             }
    241         }
    242         if (mPhoneState != null) {
    243             mPhoneState.listenForPhoneState(false);
    244             mPhoneState.cleanup();
    245         }
    246         if (mPhonebook != null) {
    247             mPhonebook.cleanup();
    248         }
    249         if (mNativeAvailable) {
    250             cleanupNative();
    251             mNativeAvailable = false;
    252         }
    253     }
    254 
    255     private class Disconnected extends State {
    256         @Override
    257         public void enter() {
    258             log("Enter Disconnected: " + getCurrentMessage().what);
    259             mPhonebook.resetAtState();
    260             mPhoneState.listenForPhoneState(false);
    261         }
    262 
    263         @Override
    264         public boolean processMessage(Message message) {
    265             log("Disconnected process message: " + message.what);
    266             if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
    267                 Log.e(TAG, "ERROR: current, target, or mIncomingDevice not null in Disconnected");
    268                 return NOT_HANDLED;
    269             }
    270 
    271             boolean retValue = HANDLED;
    272             switch(message.what) {
    273                 case CONNECT:
    274                     BluetoothDevice device = (BluetoothDevice) message.obj;
    275                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    276                                    BluetoothProfile.STATE_DISCONNECTED);
    277 
    278                     if (!connectHfpNative(getByteAddress(device)) ) {
    279                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    280                                        BluetoothProfile.STATE_CONNECTING);
    281                         break;
    282                     }
    283 
    284                     synchronized (HeadsetStateMachine.this) {
    285                         mTargetDevice = device;
    286                         transitionTo(mPending);
    287                     }
    288                     // TODO(BT) remove CONNECT_TIMEOUT when the stack
    289                     //          sends back events consistently
    290                     sendMessageDelayed(CONNECT_TIMEOUT, 30000);
    291                     break;
    292                 case DISCONNECT:
    293                     // ignore
    294                     break;
    295                 case INTENT_BATTERY_CHANGED:
    296                     processIntentBatteryChanged((Intent) message.obj);
    297                     break;
    298                 case CALL_STATE_CHANGED:
    299                     processCallState((HeadsetCallState) message.obj,
    300                         ((message.arg1 == 1)?true:false));
    301                     break;
    302                 case STACK_EVENT:
    303                     StackEvent event = (StackEvent) message.obj;
    304                     if (DBG) {
    305                         log("event type: " + event.type);
    306                     }
    307                     switch (event.type) {
    308                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    309                             processConnectionEvent(event.valueInt, event.device);
    310                             break;
    311                         default:
    312                             Log.e(TAG, "Unexpected stack event: " + event.type);
    313                             break;
    314                     }
    315                     break;
    316                 default:
    317                     return NOT_HANDLED;
    318             }
    319             return retValue;
    320         }
    321 
    322         @Override
    323         public void exit() {
    324             log("Exit Disconnected: " + getCurrentMessage().what);
    325         }
    326 
    327         // in Disconnected state
    328         private void processConnectionEvent(int state, BluetoothDevice device) {
    329             switch (state) {
    330             case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
    331                 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
    332                 break;
    333             case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
    334                 if (okToConnect(device)){
    335                     Log.i(TAG,"Incoming Hf accepted");
    336                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    337                                              BluetoothProfile.STATE_DISCONNECTED);
    338                     synchronized (HeadsetStateMachine.this) {
    339                         mIncomingDevice = device;
    340                         transitionTo(mPending);
    341                     }
    342                 } else {
    343                     Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device)+
    344                               " bondState=" + device.getBondState());
    345                     //reject the connection and stay in Disconnected state itself
    346                     disconnectHfpNative(getByteAddress(device));
    347                     // the other profile connection should be initiated
    348                     AdapterService adapterService = AdapterService.getAdapterService();
    349                     if ( adapterService != null) {
    350                         adapterService.connectOtherProfile(device,
    351                                                            AdapterService.PROFILE_CONN_REJECTED);
    352                     }
    353                 }
    354                 break;
    355             case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
    356                 Log.w(TAG, "HFP Connected from Disconnected state");
    357                 if (okToConnect(device)){
    358                     Log.i(TAG,"Incoming Hf accepted");
    359                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    360                                              BluetoothProfile.STATE_DISCONNECTED);
    361                     synchronized (HeadsetStateMachine.this) {
    362                         mCurrentDevice = device;
    363                         transitionTo(mConnected);
    364                     }
    365                     configAudioParameters();
    366                 } else {
    367                     //reject the connection and stay in Disconnected state itself
    368                     Log.i(TAG,"Incoming Hf rejected. priority=" + mService.getPriority(device) +
    369                               " bondState=" + device.getBondState());
    370                     disconnectHfpNative(getByteAddress(device));
    371                     // the other profile connection should be initiated
    372                     AdapterService adapterService = AdapterService.getAdapterService();
    373                     if ( adapterService != null) {
    374                         adapterService.connectOtherProfile(device,
    375                                                            AdapterService.PROFILE_CONN_REJECTED);
    376                     }
    377                 }
    378                 break;
    379             case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
    380                 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
    381                 break;
    382             default:
    383                 Log.e(TAG, "Incorrect state: " + state);
    384                 break;
    385             }
    386         }
    387     }
    388 
    389     private class Pending extends State {
    390         @Override
    391         public void enter() {
    392             log("Enter Pending: " + getCurrentMessage().what);
    393         }
    394 
    395         @Override
    396         public boolean processMessage(Message message) {
    397             log("Pending process message: " + message.what);
    398 
    399             boolean retValue = HANDLED;
    400             switch(message.what) {
    401                 case CONNECT:
    402                 case CONNECT_AUDIO:
    403                     deferMessage(message);
    404                     break;
    405                 case CONNECT_TIMEOUT:
    406                     onConnectionStateChanged(HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
    407                                              getByteAddress(mTargetDevice));
    408                     break;
    409                 case DISCONNECT:
    410                     BluetoothDevice device = (BluetoothDevice) message.obj;
    411                     if (mCurrentDevice != null && mTargetDevice != null &&
    412                         mTargetDevice.equals(device) ) {
    413                         // cancel connection to the mTargetDevice
    414                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    415                                        BluetoothProfile.STATE_CONNECTING);
    416                         synchronized (HeadsetStateMachine.this) {
    417                             mTargetDevice = null;
    418                         }
    419                     } else {
    420                         deferMessage(message);
    421                     }
    422                     break;
    423                 case INTENT_BATTERY_CHANGED:
    424                     processIntentBatteryChanged((Intent) message.obj);
    425                     break;
    426                 case CALL_STATE_CHANGED:
    427                     processCallState((HeadsetCallState) message.obj,
    428                         ((message.arg1 == 1)?true:false));
    429                     break;
    430                 case STACK_EVENT:
    431                     StackEvent event = (StackEvent) message.obj;
    432                     if (DBG) {
    433                         log("event type: " + event.type);
    434                     }
    435                     switch (event.type) {
    436                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    437                             removeMessages(CONNECT_TIMEOUT);
    438                             processConnectionEvent(event.valueInt, event.device);
    439                             break;
    440                         default:
    441                             Log.e(TAG, "Unexpected event: " + event.type);
    442                             break;
    443                     }
    444                     break;
    445                 default:
    446                     return NOT_HANDLED;
    447             }
    448             return retValue;
    449         }
    450 
    451         // in Pending state
    452         private void processConnectionEvent(int state, BluetoothDevice device) {
    453             switch (state) {
    454                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
    455                     if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    456                         broadcastConnectionState(mCurrentDevice,
    457                                                  BluetoothProfile.STATE_DISCONNECTED,
    458                                                  BluetoothProfile.STATE_DISCONNECTING);
    459                         synchronized (HeadsetStateMachine.this) {
    460                             mCurrentDevice = null;
    461                         }
    462 
    463                         if (mTargetDevice != null) {
    464                             if (!connectHfpNative(getByteAddress(mTargetDevice))) {
    465                                 broadcastConnectionState(mTargetDevice,
    466                                                          BluetoothProfile.STATE_DISCONNECTED,
    467                                                          BluetoothProfile.STATE_CONNECTING);
    468                                 synchronized (HeadsetStateMachine.this) {
    469                                     mTargetDevice = null;
    470                                     transitionTo(mDisconnected);
    471                                 }
    472                             }
    473                         } else {
    474                             synchronized (HeadsetStateMachine.this) {
    475                                 mIncomingDevice = null;
    476                                 transitionTo(mDisconnected);
    477                             }
    478                         }
    479                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    480                         // outgoing connection failed
    481                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
    482                                                  BluetoothProfile.STATE_CONNECTING);
    483                         synchronized (HeadsetStateMachine.this) {
    484                             mTargetDevice = null;
    485                             transitionTo(mDisconnected);
    486                         }
    487                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    488                         broadcastConnectionState(mIncomingDevice,
    489                                                  BluetoothProfile.STATE_DISCONNECTED,
    490                                                  BluetoothProfile.STATE_CONNECTING);
    491                         synchronized (HeadsetStateMachine.this) {
    492                             mIncomingDevice = null;
    493                             transitionTo(mDisconnected);
    494                         }
    495                     } else {
    496                         Log.e(TAG, "Unknown device Disconnected: " + device);
    497                     }
    498                     break;
    499             case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
    500                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    501                     // disconnection failed
    502                     broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
    503                                              BluetoothProfile.STATE_DISCONNECTING);
    504                     if (mTargetDevice != null) {
    505                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
    506                                                  BluetoothProfile.STATE_CONNECTING);
    507                     }
    508                     synchronized (HeadsetStateMachine.this) {
    509                         mTargetDevice = null;
    510                         transitionTo(mConnected);
    511                     }
    512                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    513                     broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
    514                                              BluetoothProfile.STATE_CONNECTING);
    515                     synchronized (HeadsetStateMachine.this) {
    516                         mCurrentDevice = mTargetDevice;
    517                         mTargetDevice = null;
    518                         transitionTo(mConnected);
    519                     }
    520                 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    521                     broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
    522                                              BluetoothProfile.STATE_CONNECTING);
    523                     synchronized (HeadsetStateMachine.this) {
    524                         mCurrentDevice = mIncomingDevice;
    525                         mIncomingDevice = null;
    526                         transitionTo(mConnected);
    527                     }
    528                 } else {
    529                     Log.e(TAG, "Unknown device Connected: " + device);
    530                     // something is wrong here, but sync our state with stack
    531                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    532                                              BluetoothProfile.STATE_DISCONNECTED);
    533                     synchronized (HeadsetStateMachine.this) {
    534                         mCurrentDevice = device;
    535                         mTargetDevice = null;
    536                         mIncomingDevice = null;
    537                         transitionTo(mConnected);
    538                     }
    539                 }
    540                 configAudioParameters();
    541                 break;
    542             case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
    543                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    544                     log("current device tries to connect back");
    545                     // TODO(BT) ignore or reject
    546                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    547                     // The stack is connecting to target device or
    548                     // there is an incoming connection from the target device at the same time
    549                     // we already broadcasted the intent, doing nothing here
    550                     if (DBG) {
    551                         log("Stack and target device are connecting");
    552                     }
    553                 }
    554                 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    555                     Log.e(TAG, "Another connecting event on the incoming device");
    556                 } else {
    557                     // We get an incoming connecting request while Pending
    558                     // TODO(BT) is stack handing this case? let's ignore it for now
    559                     log("Incoming connection while pending, ignore");
    560                 }
    561                 break;
    562             case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
    563                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    564                     // we already broadcasted the intent, doing nothing here
    565                     if (DBG) {
    566                         log("stack is disconnecting mCurrentDevice");
    567                     }
    568                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    569                     Log.e(TAG, "TargetDevice is getting disconnected");
    570                 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    571                     Log.e(TAG, "IncomingDevice is getting disconnected");
    572                 } else {
    573                     Log.e(TAG, "Disconnecting unknow device: " + device);
    574                 }
    575                 break;
    576             default:
    577                 Log.e(TAG, "Incorrect state: " + state);
    578                 break;
    579             }
    580         }
    581 
    582     }
    583 
    584     private class Connected extends State {
    585         @Override
    586         public void enter() {
    587             log("Enter Connected: " + getCurrentMessage().what);
    588         }
    589 
    590         @Override
    591         public boolean processMessage(Message message) {
    592             log("Connected process message: " + message.what);
    593             if (DBG) {
    594                 if (mCurrentDevice == null) {
    595                     log("ERROR: mCurrentDevice is null in Connected");
    596                     return NOT_HANDLED;
    597                 }
    598             }
    599 
    600             boolean retValue = HANDLED;
    601             switch(message.what) {
    602                 case CONNECT:
    603                 {
    604                     BluetoothDevice device = (BluetoothDevice) message.obj;
    605                     if (mCurrentDevice.equals(device)) {
    606                         break;
    607                     }
    608 
    609                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    610                                    BluetoothProfile.STATE_DISCONNECTED);
    611                     if (!disconnectHfpNative(getByteAddress(mCurrentDevice))) {
    612                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    613                                        BluetoothProfile.STATE_CONNECTING);
    614                         break;
    615                     }
    616 
    617                     synchronized (HeadsetStateMachine.this) {
    618                         mTargetDevice = device;
    619                         transitionTo(mPending);
    620                     }
    621                 }
    622                     break;
    623                 case DISCONNECT:
    624                 {
    625                     BluetoothDevice device = (BluetoothDevice) message.obj;
    626                     if (!mCurrentDevice.equals(device)) {
    627                         break;
    628                     }
    629                     broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
    630                                    BluetoothProfile.STATE_CONNECTED);
    631                     if (!disconnectHfpNative(getByteAddress(device))) {
    632                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    633                                        BluetoothProfile.STATE_DISCONNECTED);
    634                         break;
    635                     }
    636                     transitionTo(mPending);
    637                 }
    638                     break;
    639                 case CONNECT_AUDIO:
    640                     // TODO(BT) when failure, broadcast audio connecting to disconnected intent
    641                     //          check if device matches mCurrentDevice
    642                     connectAudioNative(getByteAddress(mCurrentDevice));
    643                     break;
    644                 case VOICE_RECOGNITION_START:
    645                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
    646                     break;
    647                 case VOICE_RECOGNITION_STOP:
    648                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
    649                     break;
    650                 case CALL_STATE_CHANGED:
    651                     processCallState((HeadsetCallState) message.obj, ((message.arg1==1)?true:false));
    652                     break;
    653                 case INTENT_BATTERY_CHANGED:
    654                     processIntentBatteryChanged((Intent) message.obj);
    655                     break;
    656                 case DEVICE_STATE_CHANGED:
    657                     processDeviceStateChanged((HeadsetDeviceState) message.obj);
    658                     break;
    659                 case SEND_CCLC_RESPONSE:
    660                     processSendClccResponse((HeadsetClccResponse) message.obj);
    661                     break;
    662                 case SEND_VENDOR_SPECIFIC_RESULT_CODE:
    663                     processSendVendorSpecificResultCode(
    664                             (HeadsetVendorSpecificResultCode) message.obj);
    665                     break;
    666                 case DIALING_OUT_TIMEOUT:
    667                     if (mDialingOut) {
    668                         mDialingOut= false;
    669                         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
    670                     }
    671                     break;
    672                 case VIRTUAL_CALL_START:
    673                     initiateScoUsingVirtualVoiceCall();
    674                     break;
    675                 case VIRTUAL_CALL_STOP:
    676                     terminateScoUsingVirtualVoiceCall();
    677                     break;
    678                 case START_VR_TIMEOUT:
    679                     if (mWaitingForVoiceRecognition) {
    680                         mWaitingForVoiceRecognition = false;
    681                         Log.e(TAG, "Timeout waiting for voice recognition to start");
    682                         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
    683                     }
    684                     break;
    685                 case STACK_EVENT:
    686                     StackEvent event = (StackEvent) message.obj;
    687                     if (DBG) {
    688                         log("event type: " + event.type);
    689                     }
    690                     switch (event.type) {
    691                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    692                             processConnectionEvent(event.valueInt, event.device);
    693                             break;
    694                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
    695                             processAudioEvent(event.valueInt, event.device);
    696                             break;
    697                         case EVENT_TYPE_VR_STATE_CHANGED:
    698                             processVrEvent(event.valueInt);
    699                             break;
    700                         case EVENT_TYPE_ANSWER_CALL:
    701                             // TODO(BT) could answer call happen on Connected state?
    702                             processAnswerCall();
    703                             break;
    704                         case EVENT_TYPE_HANGUP_CALL:
    705                             // TODO(BT) could hangup call happen on Connected state?
    706                             processHangupCall();
    707                             break;
    708                         case EVENT_TYPE_VOLUME_CHANGED:
    709                             processVolumeEvent(event.valueInt, event.valueInt2);
    710                             break;
    711                         case EVENT_TYPE_DIAL_CALL:
    712                             processDialCall(event.valueString);
    713                             break;
    714                         case EVENT_TYPE_SEND_DTMF:
    715                             processSendDtmf(event.valueInt);
    716                             break;
    717                         case EVENT_TYPE_NOICE_REDUCTION:
    718                             processNoiceReductionEvent(event.valueInt);
    719                             break;
    720                         case EVENT_TYPE_AT_CHLD:
    721                             processAtChld(event.valueInt);
    722                             break;
    723                         case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
    724                             processSubscriberNumberRequest();
    725                             break;
    726                         case EVENT_TYPE_AT_CIND:
    727                             processAtCind();
    728                             break;
    729                         case EVENT_TYPE_AT_COPS:
    730                             processAtCops();
    731                             break;
    732                         case EVENT_TYPE_AT_CLCC:
    733                             processAtClcc();
    734                             break;
    735                         case EVENT_TYPE_UNKNOWN_AT:
    736                             processUnknownAt(event.valueString);
    737                             break;
    738                         case EVENT_TYPE_KEY_PRESSED:
    739                             processKeyPressed();
    740                             break;
    741                         default:
    742                             Log.e(TAG, "Unknown stack event: " + event.type);
    743                             break;
    744                     }
    745                     break;
    746                 default:
    747                     return NOT_HANDLED;
    748             }
    749             return retValue;
    750         }
    751 
    752         // in Connected state
    753         private void processConnectionEvent(int state, BluetoothDevice device) {
    754             switch (state) {
    755                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
    756                     if (mCurrentDevice.equals(device)) {
    757                         broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
    758                                                  BluetoothProfile.STATE_CONNECTED);
    759                         synchronized (HeadsetStateMachine.this) {
    760                             mCurrentDevice = null;
    761                             transitionTo(mDisconnected);
    762                         }
    763                     } else {
    764                         Log.e(TAG, "Disconnected from unknown device: " + device);
    765                     }
    766                     break;
    767                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
    768                     processSlcConnected();
    769                     break;
    770               default:
    771                   Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
    772                   break;
    773             }
    774         }
    775 
    776         // in Connected state
    777         private void processAudioEvent(int state, BluetoothDevice device) {
    778             if (!mCurrentDevice.equals(device)) {
    779                 Log.e(TAG, "Audio changed on disconnected device: " + device);
    780                 return;
    781             }
    782 
    783             switch (state) {
    784                 case HeadsetHalConstants.AUDIO_STATE_CONNECTED:
    785                     // TODO(BT) should I save the state for next broadcast as the prevState?
    786                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED;
    787                     mAudioManager.setBluetoothScoOn(true);
    788                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTED,
    789                                         BluetoothHeadset.STATE_AUDIO_CONNECTING);
    790                     transitionTo(mAudioOn);
    791                     break;
    792                 case HeadsetHalConstants.AUDIO_STATE_CONNECTING:
    793                     mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTING;
    794                     broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_CONNECTING,
    795                                         BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
    796                     break;
    797                     // TODO(BT) process other states
    798                 default:
    799                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
    800                     break;
    801             }
    802         }
    803 
    804         private void processSlcConnected() {
    805             if (mPhoneProxy != null) {
    806                 try {
    807                     // start phone state listener here, instead of on disconnected exit()
    808                     // On BT off, exitting SM sends a SM exit() call which incorrectly forces
    809                     // a listenForPhoneState(true).
    810                     // Additionally, no indicator updates should be sent prior to SLC setup
    811                     mPhoneState.listenForPhoneState(true);
    812                     mPhoneProxy.queryPhoneState();
    813                 } catch (RemoteException e) {
    814                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
    815                 }
    816             } else {
    817                 Log.e(TAG, "Handsfree phone proxy null for query phone state");
    818             }
    819 
    820         }
    821     }
    822 
    823     private class AudioOn extends State {
    824 
    825         @Override
    826         public void enter() {
    827             log("Enter AudioOn: " + getCurrentMessage().what);
    828         }
    829 
    830         @Override
    831         public boolean processMessage(Message message) {
    832             log("AudioOn process message: " + message.what);
    833             if (DBG) {
    834                 if (mCurrentDevice == null) {
    835                     log("ERROR: mCurrentDevice is null in AudioOn");
    836                     return NOT_HANDLED;
    837                 }
    838             }
    839 
    840             boolean retValue = HANDLED;
    841             switch(message.what) {
    842                 case DISCONNECT:
    843                 {
    844                     BluetoothDevice device = (BluetoothDevice) message.obj;
    845                     if (!mCurrentDevice.equals(device)) {
    846                         break;
    847                     }
    848                     deferMessage(obtainMessage(DISCONNECT, message.obj));
    849                 }
    850                 // fall through
    851                 case DISCONNECT_AUDIO:
    852                     if (disconnectAudioNative(getByteAddress(mCurrentDevice))) {
    853                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
    854                         mAudioManager.setBluetoothScoOn(false);
    855                         broadcastAudioState(mCurrentDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
    856                                             BluetoothHeadset.STATE_AUDIO_CONNECTED);
    857                     }
    858                     break;
    859                 case VOICE_RECOGNITION_START:
    860                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STARTED);
    861                     break;
    862                 case VOICE_RECOGNITION_STOP:
    863                     processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED);
    864                     break;
    865                 case INTENT_SCO_VOLUME_CHANGED:
    866                     processIntentScoVolume((Intent) message.obj);
    867                     break;
    868                 case CALL_STATE_CHANGED:
    869                     processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false));
    870                     break;
    871                 case INTENT_BATTERY_CHANGED:
    872                     processIntentBatteryChanged((Intent) message.obj);
    873                     break;
    874                 case DEVICE_STATE_CHANGED:
    875                     processDeviceStateChanged((HeadsetDeviceState) message.obj);
    876                     break;
    877                 case SEND_CCLC_RESPONSE:
    878                     processSendClccResponse((HeadsetClccResponse) message.obj);
    879                     break;
    880                 case SEND_VENDOR_SPECIFIC_RESULT_CODE:
    881                     processSendVendorSpecificResultCode(
    882                             (HeadsetVendorSpecificResultCode) message.obj);
    883                     break;
    884 
    885                 case VIRTUAL_CALL_START:
    886                     initiateScoUsingVirtualVoiceCall();
    887                     break;
    888                 case VIRTUAL_CALL_STOP:
    889                     terminateScoUsingVirtualVoiceCall();
    890                     break;
    891 
    892                 case DIALING_OUT_TIMEOUT:
    893                     if (mDialingOut) {
    894                         mDialingOut= false;
    895                         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
    896                     }
    897                     break;
    898                 case START_VR_TIMEOUT:
    899                     if (mWaitingForVoiceRecognition) {
    900                         mWaitingForVoiceRecognition = false;
    901                         Log.e(TAG, "Timeout waiting for voice recognition to start");
    902                         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
    903                     }
    904                     break;
    905                 case STACK_EVENT:
    906                     StackEvent event = (StackEvent) message.obj;
    907                     if (DBG) {
    908                         log("event type: " + event.type);
    909                     }
    910                     switch (event.type) {
    911                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    912                             processConnectionEvent(event.valueInt, event.device);
    913                             break;
    914                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
    915                             processAudioEvent(event.valueInt, event.device);
    916                             break;
    917                         case EVENT_TYPE_VR_STATE_CHANGED:
    918                             processVrEvent(event.valueInt);
    919                             break;
    920                         case EVENT_TYPE_ANSWER_CALL:
    921                             processAnswerCall();
    922                             break;
    923                         case EVENT_TYPE_HANGUP_CALL:
    924                             processHangupCall();
    925                             break;
    926                         case EVENT_TYPE_VOLUME_CHANGED:
    927                             processVolumeEvent(event.valueInt, event.valueInt2);
    928                             break;
    929                         case EVENT_TYPE_DIAL_CALL:
    930                             processDialCall(event.valueString);
    931                             break;
    932                         case EVENT_TYPE_SEND_DTMF:
    933                             processSendDtmf(event.valueInt);
    934                             break;
    935                         case EVENT_TYPE_NOICE_REDUCTION:
    936                             processNoiceReductionEvent(event.valueInt);
    937                             break;
    938                         case EVENT_TYPE_AT_CHLD:
    939                             processAtChld(event.valueInt);
    940                             break;
    941                         case EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST:
    942                             processSubscriberNumberRequest();
    943                             break;
    944                         case EVENT_TYPE_AT_CIND:
    945                             processAtCind();
    946                             break;
    947                         case EVENT_TYPE_AT_COPS:
    948                             processAtCops();
    949                             break;
    950                         case EVENT_TYPE_AT_CLCC:
    951                             processAtClcc();
    952                             break;
    953                         case EVENT_TYPE_UNKNOWN_AT:
    954                             processUnknownAt(event.valueString);
    955                             break;
    956                         case EVENT_TYPE_KEY_PRESSED:
    957                             processKeyPressed();
    958                             break;
    959                         default:
    960                             Log.e(TAG, "Unknown stack event: " + event.type);
    961                             break;
    962                     }
    963                     break;
    964                 default:
    965                     return NOT_HANDLED;
    966             }
    967             return retValue;
    968         }
    969 
    970         // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
    971         private void processConnectionEvent(int state, BluetoothDevice device) {
    972             switch (state) {
    973                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
    974                     if (mCurrentDevice.equals(device)) {
    975                         processAudioEvent (HeadsetHalConstants.AUDIO_STATE_DISCONNECTED, device);
    976                         broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
    977                                                  BluetoothProfile.STATE_CONNECTED);
    978                         synchronized (HeadsetStateMachine.this) {
    979                             mCurrentDevice = null;
    980                             transitionTo(mDisconnected);
    981                         }
    982                     } else {
    983                         Log.e(TAG, "Disconnected from unknown device: " + device);
    984                     }
    985                     break;
    986               default:
    987                   Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
    988                   break;
    989             }
    990         }
    991 
    992         // in AudioOn state
    993         private void processAudioEvent(int state, BluetoothDevice device) {
    994             if (!mCurrentDevice.equals(device)) {
    995                 Log.e(TAG, "Audio changed on disconnected device: " + device);
    996                 return;
    997             }
    998 
    999             switch (state) {
   1000                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
   1001                     if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1002                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
   1003                         mAudioManager.setBluetoothScoOn(false);
   1004                         broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
   1005                                             BluetoothHeadset.STATE_AUDIO_CONNECTED);
   1006                     }
   1007                     transitionTo(mConnected);
   1008                     break;
   1009                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTING:
   1010                     // TODO(BT) adding STATE_AUDIO_DISCONNECTING in BluetoothHeadset?
   1011                     //broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTING,
   1012                     //                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
   1013                     break;
   1014                 default:
   1015                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
   1016                     break;
   1017             }
   1018         }
   1019 
   1020         private void processIntentScoVolume(Intent intent) {
   1021             int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
   1022             if (mPhoneState.getSpeakerVolume() != volumeValue) {
   1023                 mPhoneState.setSpeakerVolume(volumeValue);
   1024                 setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, volumeValue);
   1025             }
   1026         }
   1027     }
   1028 
   1029     private ServiceConnection mConnection = new ServiceConnection() {
   1030         public void onServiceConnected(ComponentName className, IBinder service) {
   1031             if (DBG) Log.d(TAG, "Proxy object connected");
   1032             mPhoneProxy = IBluetoothHeadsetPhone.Stub.asInterface(service);
   1033         }
   1034 
   1035         public void onServiceDisconnected(ComponentName className) {
   1036             if (DBG) Log.d(TAG, "Proxy object disconnected");
   1037             mPhoneProxy = null;
   1038         }
   1039     };
   1040 
   1041     // HFP Connection state of the device could be changed by the state machine
   1042     // in separate thread while this method is executing.
   1043     int getConnectionState(BluetoothDevice device) {
   1044         if (getCurrentState() == mDisconnected) {
   1045             return BluetoothProfile.STATE_DISCONNECTED;
   1046         }
   1047 
   1048         synchronized (this) {
   1049             IState currentState = getCurrentState();
   1050             if (currentState == mPending) {
   1051                 if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
   1052                     return BluetoothProfile.STATE_CONNECTING;
   1053                 }
   1054                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
   1055                     return BluetoothProfile.STATE_DISCONNECTING;
   1056                 }
   1057                 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
   1058                     return BluetoothProfile.STATE_CONNECTING; // incoming connection
   1059                 }
   1060                 return BluetoothProfile.STATE_DISCONNECTED;
   1061             }
   1062 
   1063             if (currentState == mConnected || currentState == mAudioOn) {
   1064                 if (mCurrentDevice.equals(device)) {
   1065                     return BluetoothProfile.STATE_CONNECTED;
   1066                 }
   1067                 return BluetoothProfile.STATE_DISCONNECTED;
   1068             } else {
   1069                 Log.e(TAG, "Bad currentState: " + currentState);
   1070                 return BluetoothProfile.STATE_DISCONNECTED;
   1071             }
   1072         }
   1073     }
   1074 
   1075     List<BluetoothDevice> getConnectedDevices() {
   1076         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
   1077         synchronized(this) {
   1078             if (isConnected()) {
   1079                 devices.add(mCurrentDevice);
   1080             }
   1081         }
   1082         return devices;
   1083     }
   1084 
   1085     boolean isAudioOn() {
   1086         return (getCurrentState() == mAudioOn);
   1087     }
   1088 
   1089     boolean isAudioConnected(BluetoothDevice device) {
   1090         synchronized(this) {
   1091 
   1092             /*  Additional check for audio state included for the case when PhoneApp queries
   1093             Bluetooth Audio state, before we receive the close event from the stack for the
   1094             sco disconnect issued in AudioOn state. This was causing a mismatch in the
   1095             Incall screen UI. */
   1096 
   1097             if (getCurrentState() == mAudioOn && mCurrentDevice.equals(device)
   1098                 && mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
   1099             {
   1100                 return true;
   1101             }
   1102         }
   1103         return false;
   1104     }
   1105 
   1106     int getAudioState(BluetoothDevice device) {
   1107         synchronized(this) {
   1108             if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
   1109                 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
   1110             }
   1111         }
   1112         return mAudioState;
   1113     }
   1114 
   1115     private void processVrEvent(int state) {
   1116         Log.d(TAG, "processVrEvent: state=" + state + " mVoiceRecognitionStarted: " +
   1117             mVoiceRecognitionStarted + " mWaitingforVoiceRecognition: " + mWaitingForVoiceRecognition +
   1118             " isInCall: " + isInCall());
   1119         if (state == HeadsetHalConstants.VR_STATE_STARTED) {
   1120             if (!isVirtualCallInProgress() &&
   1121                 !isInCall())
   1122             {
   1123                 try {
   1124                     mService.startActivity(sVoiceCommandIntent);
   1125                 } catch (ActivityNotFoundException e) {
   1126                     atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1127                     return;
   1128                 }
   1129                 expectVoiceRecognition();
   1130             }
   1131         } else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
   1132             if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
   1133             {
   1134                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1135                 mVoiceRecognitionStarted = false;
   1136                 mWaitingForVoiceRecognition = false;
   1137                 if (!isInCall()) {
   1138                     disconnectAudioNative(getByteAddress(mCurrentDevice));
   1139                     mAudioManager.setParameters("A2dpSuspended=false");
   1140                 }
   1141             }
   1142             else
   1143             {
   1144                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1145             }
   1146         } else {
   1147             Log.e(TAG, "Bad Voice Recognition state: " + state);
   1148         }
   1149     }
   1150 
   1151     private void processLocalVrEvent(int state)
   1152     {
   1153         if (state == HeadsetHalConstants.VR_STATE_STARTED)
   1154         {
   1155             boolean needAudio = true;
   1156             if (mVoiceRecognitionStarted || isInCall())
   1157             {
   1158                 Log.e(TAG, "Voice recognition started when call is active. isInCall:" + isInCall() +
   1159                     " mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
   1160                 return;
   1161             }
   1162             mVoiceRecognitionStarted = true;
   1163 
   1164             if (mWaitingForVoiceRecognition)
   1165             {
   1166                 Log.d(TAG, "Voice recognition started successfully");
   1167                 mWaitingForVoiceRecognition = false;
   1168                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1169                 removeMessages(START_VR_TIMEOUT);
   1170             }
   1171             else
   1172             {
   1173                 Log.d(TAG, "Voice recognition started locally");
   1174                 needAudio = startVoiceRecognitionNative();
   1175             }
   1176 
   1177             if (needAudio && !isAudioOn())
   1178             {
   1179                 Log.d(TAG, "Initiating audio connection for Voice Recognition");
   1180                 // At this stage, we need to be sure that AVDTP is not streaming. This is needed
   1181                 // to be compliant with the AV+HFP Whitepaper as we cannot have A2DP in
   1182                 // streaming state while a SCO connection is established.
   1183                 // This is needed for VoiceDial scenario alone and not for
   1184                 // incoming call/outgoing call scenarios as the phone enters MODE_RINGTONE
   1185                 // or MODE_IN_CALL which shall automatically suspend the AVDTP stream if needed.
   1186                 // Whereas for VoiceDial we want to activate the SCO connection but we are still
   1187                 // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream
   1188                 mAudioManager.setParameters("A2dpSuspended=true");
   1189                 connectAudioNative(getByteAddress(mCurrentDevice));
   1190             }
   1191 
   1192             if (mStartVoiceRecognitionWakeLock.isHeld()) {
   1193                 mStartVoiceRecognitionWakeLock.release();
   1194             }
   1195         }
   1196         else
   1197         {
   1198             Log.d(TAG, "Voice Recognition stopped. mVoiceRecognitionStarted: " + mVoiceRecognitionStarted +
   1199                 " mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
   1200             if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
   1201             {
   1202                 mVoiceRecognitionStarted = false;
   1203                 mWaitingForVoiceRecognition = false;
   1204 
   1205                 if (stopVoiceRecognitionNative() && !isInCall()) {
   1206                     disconnectAudioNative(getByteAddress(mCurrentDevice));
   1207                     mAudioManager.setParameters("A2dpSuspended=false");
   1208                 }
   1209             }
   1210         }
   1211     }
   1212 
   1213     private synchronized void expectVoiceRecognition() {
   1214         mWaitingForVoiceRecognition = true;
   1215         sendMessageDelayed(START_VR_TIMEOUT, START_VR_TIMEOUT_VALUE);
   1216         if (!mStartVoiceRecognitionWakeLock.isHeld()) {
   1217             mStartVoiceRecognitionWakeLock.acquire(START_VR_TIMEOUT_VALUE);
   1218         }
   1219     }
   1220 
   1221     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
   1222         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
   1223         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
   1224         int connectionState;
   1225         synchronized (this) {
   1226             for (BluetoothDevice device : bondedDevices) {
   1227                 ParcelUuid[] featureUuids = device.getUuids();
   1228                 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
   1229                     continue;
   1230                 }
   1231                 connectionState = getConnectionState(device);
   1232                 for(int i = 0; i < states.length; i++) {
   1233                     if (connectionState == states[i]) {
   1234                         deviceList.add(device);
   1235                     }
   1236                 }
   1237             }
   1238         }
   1239         return deviceList;
   1240     }
   1241 
   1242     // This method does not check for error conditon (newState == prevState)
   1243     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
   1244         log("Connection state " + device + ": " + prevState + "->" + newState);
   1245         if(prevState == BluetoothProfile.STATE_CONNECTED) {
   1246             // Headset is disconnecting, stop Virtual call if active.
   1247             terminateScoUsingVirtualVoiceCall();
   1248         }
   1249 
   1250         /* Notifying the connection state change of the profile before sending the intent for
   1251            connection state change, as it was causing a race condition, with the UI not being
   1252            updated with the correct connection state. */
   1253         mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,
   1254                                                      newState, prevState);
   1255         Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
   1256         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
   1257         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
   1258         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1259         mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
   1260     }
   1261 
   1262     private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
   1263         if(prevState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
   1264             // When SCO gets disconnected during call transfer, Virtual call
   1265             //needs to be cleaned up.So call terminateScoUsingVirtualVoiceCall.
   1266             terminateScoUsingVirtualVoiceCall();
   1267         }
   1268         Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
   1269         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
   1270         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
   1271         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1272         mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
   1273         log("Audio state " + device + ": " + prevState + "->" + newState);
   1274     }
   1275 
   1276     /*
   1277      * Put the AT command, company ID, arguments, and device in an Intent and broadcast it.
   1278      */
   1279     private void broadcastVendorSpecificEventIntent(String command,
   1280                                                     int companyId,
   1281                                                     int commandType,
   1282                                                     Object[] arguments,
   1283                                                     BluetoothDevice device) {
   1284         log("broadcastVendorSpecificEventIntent(" + command + ")");
   1285         Intent intent =
   1286                 new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
   1287         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
   1288         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
   1289                         commandType);
   1290         // assert: all elements of args are Serializable
   1291         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
   1292         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1293 
   1294         intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
   1295             + "." + Integer.toString(companyId));
   1296 
   1297         mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
   1298     }
   1299 
   1300     private void configAudioParameters()
   1301     {
   1302         // Reset NREC on connect event. Headset will override later
   1303         mAudioManager.setParameters(HEADSET_NAME + "=" + getCurrentDeviceName() + ";" +
   1304                                     HEADSET_NREC + "=on");
   1305     }
   1306 
   1307     private String parseUnknownAt(String atString)
   1308     {
   1309         StringBuilder atCommand = new StringBuilder(atString.length());
   1310         String result = null;
   1311 
   1312         for (int i = 0; i < atString.length(); i++) {
   1313             char c = atString.charAt(i);
   1314             if (c == '"') {
   1315                 int j = atString.indexOf('"', i + 1 );  // search for closing "
   1316                 if (j == -1) {  // unmatched ", insert one.
   1317                     atCommand.append(atString.substring(i, atString.length()));
   1318                     atCommand.append('"');
   1319                     break;
   1320                 }
   1321                 atCommand.append(atString.substring(i, j + 1));
   1322                 i = j;
   1323             } else if (c != ' ') {
   1324                 atCommand.append(Character.toUpperCase(c));
   1325             }
   1326         }
   1327         result = atCommand.toString();
   1328         return result;
   1329     }
   1330 
   1331     private int getAtCommandType(String atCommand)
   1332     {
   1333         int commandType = mPhonebook.TYPE_UNKNOWN;
   1334         String atString = null;
   1335         atCommand = atCommand.trim();
   1336         if (atCommand.length() > 5)
   1337         {
   1338             atString = atCommand.substring(5);
   1339             if (atString.startsWith("?"))     // Read
   1340                 commandType = mPhonebook.TYPE_READ;
   1341             else if (atString.startsWith("=?"))   // Test
   1342                 commandType = mPhonebook.TYPE_TEST;
   1343             else if (atString.startsWith("="))   // Set
   1344                 commandType = mPhonebook.TYPE_SET;
   1345             else
   1346                 commandType = mPhonebook.TYPE_UNKNOWN;
   1347         }
   1348         return commandType;
   1349     }
   1350 
   1351     /* Method to check if Virtual Call in Progress */
   1352     private boolean isVirtualCallInProgress() {
   1353         return mVirtualCallStarted;
   1354     }
   1355 
   1356     void setVirtualCallInProgress(boolean state) {
   1357         mVirtualCallStarted = state;
   1358     }
   1359 
   1360     /* NOTE: Currently the VirtualCall API does not support handling of
   1361     call transfers. If it is initiated from the handsfree device,
   1362     HeadsetStateMachine will end the virtual call by calling
   1363     terminateScoUsingVirtualVoiceCall() in broadcastAudioState() */
   1364     synchronized boolean initiateScoUsingVirtualVoiceCall() {
   1365         if (DBG) log("initiateScoUsingVirtualVoiceCall: Received");
   1366         // 1. Check if the SCO state is idle
   1367         if (isInCall() || mVoiceRecognitionStarted) {
   1368             Log.e(TAG, "initiateScoUsingVirtualVoiceCall: Call in progress.");
   1369             return false;
   1370         }
   1371 
   1372         // 2. Send virtual phone state changed to initialize SCO
   1373         processCallState(new HeadsetCallState(0, 0,
   1374             HeadsetHalConstants.CALL_STATE_DIALING, "", 0), true);
   1375         processCallState(new HeadsetCallState(0, 0,
   1376             HeadsetHalConstants.CALL_STATE_ALERTING, "", 0), true);
   1377         processCallState(new HeadsetCallState(1, 0,
   1378             HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
   1379         setVirtualCallInProgress(true);
   1380         // Done
   1381         if (DBG) log("initiateScoUsingVirtualVoiceCall: Done");
   1382         return true;
   1383     }
   1384 
   1385     synchronized boolean terminateScoUsingVirtualVoiceCall() {
   1386         if (DBG) log("terminateScoUsingVirtualVoiceCall: Received");
   1387 
   1388         if (!isVirtualCallInProgress()) {
   1389             Log.e(TAG, "terminateScoUsingVirtualVoiceCall:"+
   1390                 "No present call to terminate");
   1391             return false;
   1392         }
   1393 
   1394         // 2. Send virtual phone state changed to close SCO
   1395         processCallState(new HeadsetCallState(0, 0,
   1396             HeadsetHalConstants.CALL_STATE_IDLE, "", 0), true);
   1397         setVirtualCallInProgress(false);
   1398         // Done
   1399         if (DBG) log("terminateScoUsingVirtualVoiceCall: Done");
   1400         return true;
   1401     }
   1402 
   1403     private void processAnswerCall() {
   1404         if (mPhoneProxy != null) {
   1405             try {
   1406                 mPhoneProxy.answerCall();
   1407             } catch (RemoteException e) {
   1408                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1409             }
   1410         } else {
   1411             Log.e(TAG, "Handsfree phone proxy null for answering call");
   1412         }
   1413     }
   1414 
   1415     private void processHangupCall() {
   1416         // Close the virtual call if active. Virtual call should be
   1417         // terminated for CHUP callback event
   1418         if (isVirtualCallInProgress()) {
   1419             terminateScoUsingVirtualVoiceCall();
   1420         } else {
   1421             if (mPhoneProxy != null) {
   1422                 try {
   1423                     mPhoneProxy.hangupCall();
   1424                 } catch (RemoteException e) {
   1425                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1426                 }
   1427             } else {
   1428                 Log.e(TAG, "Handsfree phone proxy null for hanging up call");
   1429             }
   1430         }
   1431     }
   1432 
   1433     private void processDialCall(String number) {
   1434         String dialNumber;
   1435         if ((number == null) || (number.length() == 0)) {
   1436             dialNumber = mPhonebook.getLastDialledNumber();
   1437             if (dialNumber == null) {
   1438                 if (DBG) log("processDialCall, last dial number null");
   1439                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1440                 return;
   1441             }
   1442         } else if (number.charAt(0) == '>') {
   1443             // Yuck - memory dialling requested.
   1444             // Just dial last number for now
   1445             if (number.startsWith(">9999")) {   // for PTS test
   1446                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1447                 return;
   1448             }
   1449             if (DBG) log("processDialCall, memory dial do last dial for now");
   1450             dialNumber = mPhonebook.getLastDialledNumber();
   1451             if (dialNumber == null) {
   1452                 if (DBG) log("processDialCall, last dial number null");
   1453                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1454                 return;
   1455             }
   1456         } else {
   1457             // Remove trailing ';'
   1458             if (number.charAt(number.length() - 1) == ';') {
   1459                 number = number.substring(0, number.length() - 1);
   1460             }
   1461 
   1462             dialNumber = PhoneNumberUtils.convertPreDial(number);
   1463         }
   1464         // Check for virtual call to terminate before sending Call Intent
   1465         terminateScoUsingVirtualVoiceCall();
   1466 
   1467         Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
   1468                                    Uri.fromParts(SCHEME_TEL, dialNumber, null));
   1469         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1470         mService.startActivity(intent);
   1471         // TODO(BT) continue send OK reults code after call starts
   1472         //          hold wait lock, start a timer, set wait call flag
   1473         //          Get call started indication from bluetooth phone
   1474         mDialingOut = true;
   1475         sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE);
   1476     }
   1477 
   1478     private void processVolumeEvent(int volumeType, int volume) {
   1479         if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {
   1480             mPhoneState.setSpeakerVolume(volume);
   1481             int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;
   1482             mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);
   1483         } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {
   1484             mPhoneState.setMicVolume(volume);
   1485         } else {
   1486             Log.e(TAG, "Bad voluem type: " + volumeType);
   1487         }
   1488     }
   1489 
   1490     private void processSendDtmf(int dtmf) {
   1491         if (mPhoneProxy != null) {
   1492             try {
   1493                 mPhoneProxy.sendDtmf(dtmf);
   1494             } catch (RemoteException e) {
   1495                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1496             }
   1497         } else {
   1498             Log.e(TAG, "Handsfree phone proxy null for sending DTMF");
   1499         }
   1500     }
   1501 
   1502     private void processCallState(HeadsetCallState callState) {
   1503         processCallState(callState, false);
   1504     }
   1505 
   1506     private void processCallState(HeadsetCallState callState,
   1507         boolean isVirtualCall) {
   1508         mPhoneState.setNumActiveCall(callState.mNumActive);
   1509         mPhoneState.setNumHeldCall(callState.mNumHeld);
   1510         mPhoneState.setCallState(callState.mCallState);
   1511         if (mDialingOut && callState.mCallState ==
   1512             HeadsetHalConstants.CALL_STATE_DIALING) {
   1513                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1514                 removeMessages(DIALING_OUT_TIMEOUT);
   1515                 mDialingOut = false;
   1516         }
   1517         log("mNumActive: " + callState.mNumActive + " mNumHeld: " +
   1518             callState.mNumHeld +" mCallState: " + callState.mCallState);
   1519         log("mNumber: " + callState.mNumber + " mType: " + callState.mType);
   1520         if(!isVirtualCall) {
   1521             /* Not a Virtual call request. End the virtual call, if running,
   1522             before sending phoneStateChangeNative to BTIF */
   1523             terminateScoUsingVirtualVoiceCall();
   1524         }
   1525         if (getCurrentState() != mDisconnected) {
   1526             phoneStateChangeNative(callState.mNumActive, callState.mNumHeld,
   1527                 callState.mCallState, callState.mNumber, callState.mType);
   1528         }
   1529     }
   1530 
   1531     // enable 1 enable noice reduction
   1532     //        0 disable noice reduction
   1533     private void processNoiceReductionEvent(int enable) {
   1534         if (enable == 1) {
   1535             mAudioManager.setParameters(HEADSET_NREC + "=on");
   1536         } else {
   1537             mAudioManager.setParameters(HEADSET_NREC + "=off");
   1538         }
   1539     }
   1540 
   1541     private void processAtChld(int chld) {
   1542         if (mPhoneProxy != null) {
   1543             try {
   1544                 if (mPhoneProxy.processChld(chld)) {
   1545                     atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1546                 } else {
   1547                     atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1548                 }
   1549             } catch (RemoteException e) {
   1550                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1551                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1552             }
   1553         } else {
   1554             Log.e(TAG, "Handsfree phone proxy null for At+Chld");
   1555             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1556         }
   1557     }
   1558 
   1559     private void processSubscriberNumberRequest() {
   1560         if (mPhoneProxy != null) {
   1561             try {
   1562                 String number = mPhoneProxy.getSubscriberNumber();
   1563                 if (number != null) {
   1564                     atResponseStringNative("+CNUM: ,\"" + number + "\"," +
   1565                                            PhoneNumberUtils.toaFromString(number) + ",,4");
   1566                     atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1567                 }
   1568             } catch (RemoteException e) {
   1569                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1570                 atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1571             }
   1572         } else {
   1573             Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
   1574         }
   1575     }
   1576 
   1577     private void processAtCind() {
   1578         int call, call_setup;
   1579 
   1580         /* Handsfree carkits expect that +CIND is properly responded to
   1581          Hence we ensure that a proper response is sent
   1582          for the virtual call too.*/
   1583         if (isVirtualCallInProgress()) {
   1584             call = 1;
   1585             call_setup = 0;
   1586         } else {
   1587             // regular phone call
   1588             call = mPhoneState.getNumActiveCall();
   1589             call_setup = mPhoneState.getNumHeldCall();
   1590         }
   1591 
   1592         cindResponseNative(mPhoneState.getService(), call,
   1593                            call_setup, mPhoneState.getCallState(),
   1594                            mPhoneState.getSignal(), mPhoneState.getRoam(),
   1595                            mPhoneState.getBatteryCharge());
   1596     }
   1597 
   1598     private void processAtCops() {
   1599         if (mPhoneProxy != null) {
   1600             try {
   1601                 String operatorName = mPhoneProxy.getNetworkOperator();
   1602                 if (operatorName == null) {
   1603                     operatorName = "";
   1604                 }
   1605                 copsResponseNative(operatorName);
   1606             } catch (RemoteException e) {
   1607                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1608                 copsResponseNative("");
   1609             }
   1610         } else {
   1611             Log.e(TAG, "Handsfree phone proxy null for At+COPS");
   1612             copsResponseNative("");
   1613         }
   1614     }
   1615 
   1616     private void processAtClcc() {
   1617         if (mPhoneProxy != null) {
   1618             try {
   1619                 if(isVirtualCallInProgress()) {
   1620                     String phoneNumber = "";
   1621                     int type = PhoneNumberUtils.TOA_Unknown;
   1622                     try {
   1623                         phoneNumber = mPhoneProxy.getSubscriberNumber();
   1624                         type = PhoneNumberUtils.toaFromString(phoneNumber);
   1625                     } catch (RemoteException ee) {
   1626                         Log.e(TAG, "Unable to retrieve phone number"+
   1627                             "using IBluetoothHeadsetPhone proxy");
   1628                         phoneNumber = "";
   1629                     }
   1630                     clccResponseNative(1, 0, 0, 0, false, phoneNumber, type);
   1631                 }
   1632                 else if (!mPhoneProxy.listCurrentCalls()) {
   1633                     clccResponseNative(0, 0, 0, 0, false, "", 0);
   1634                 }
   1635             } catch (RemoteException e) {
   1636                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1637                 clccResponseNative(0, 0, 0, 0, false, "", 0);
   1638             }
   1639         } else {
   1640             Log.e(TAG, "Handsfree phone proxy null for At+CLCC");
   1641             clccResponseNative(0, 0, 0, 0, false, "", 0);
   1642         }
   1643     }
   1644 
   1645     private void processAtCscs(String atString, int type) {
   1646         log("processAtCscs - atString = "+ atString);
   1647         if(mPhonebook != null) {
   1648             mPhonebook.handleCscsCommand(atString, type);
   1649         }
   1650         else {
   1651             Log.e(TAG, "Phonebook handle null for At+CSCS");
   1652             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1653         }
   1654     }
   1655 
   1656     private void processAtCpbs(String atString, int type) {
   1657         log("processAtCpbs - atString = "+ atString);
   1658         if(mPhonebook != null) {
   1659             mPhonebook.handleCpbsCommand(atString, type);
   1660         }
   1661         else {
   1662             Log.e(TAG, "Phonebook handle null for At+CPBS");
   1663             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1664         }
   1665     }
   1666 
   1667     private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) {
   1668         log("processAtCpbr - atString = "+ atString);
   1669         if(mPhonebook != null) {
   1670             mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice);
   1671         }
   1672         else {
   1673             Log.e(TAG, "Phonebook handle null for At+CPBR");
   1674             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1675         }
   1676     }
   1677 
   1678     /**
   1679      * Find a character ch, ignoring quoted sections.
   1680      * Return input.length() if not found.
   1681      */
   1682     static private int findChar(char ch, String input, int fromIndex) {
   1683         for (int i = fromIndex; i < input.length(); i++) {
   1684             char c = input.charAt(i);
   1685             if (c == '"') {
   1686                 i = input.indexOf('"', i + 1);
   1687                 if (i == -1) {
   1688                     return input.length();
   1689                 }
   1690             } else if (c == ch) {
   1691                 return i;
   1692             }
   1693         }
   1694         return input.length();
   1695     }
   1696 
   1697     /**
   1698      * Break an argument string into individual arguments (comma delimited).
   1699      * Integer arguments are turned into Integer objects. Otherwise a String
   1700      * object is used.
   1701      */
   1702     static private Object[] generateArgs(String input) {
   1703         int i = 0;
   1704         int j;
   1705         ArrayList<Object> out = new ArrayList<Object>();
   1706         while (i <= input.length()) {
   1707             j = findChar(',', input, i);
   1708 
   1709             String arg = input.substring(i, j);
   1710             try {
   1711                 out.add(new Integer(arg));
   1712             } catch (NumberFormatException e) {
   1713                 out.add(arg);
   1714             }
   1715 
   1716             i = j + 1; // move past comma
   1717         }
   1718         return out.toArray();
   1719     }
   1720 
   1721     /**
   1722      * @return {@code true} if the given string is a valid vendor-specific AT command.
   1723      */
   1724     private boolean processVendorSpecificAt(String atString) {
   1725         log("processVendorSpecificAt - atString = " + atString);
   1726 
   1727         // Currently we accept only SET type commands.
   1728         int indexOfEqual = atString.indexOf("=");
   1729         if (indexOfEqual == -1) {
   1730             Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
   1731             return false;
   1732         }
   1733 
   1734         String command = atString.substring(0, indexOfEqual);
   1735         Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
   1736         if (companyId == null) {
   1737             Log.e(TAG, "processVendorSpecificAt: unsupported command: " + atString);
   1738             return false;
   1739         }
   1740 
   1741         String arg = atString.substring(indexOfEqual + 1);
   1742         if (arg.startsWith("?")) {
   1743             Log.e(TAG, "processVendorSpecificAt: command type error in " + atString);
   1744             return false;
   1745         }
   1746 
   1747         Object[] args = generateArgs(arg);
   1748         broadcastVendorSpecificEventIntent(command,
   1749                                            companyId,
   1750                                            BluetoothHeadset.AT_CMD_TYPE_SET,
   1751                                            args,
   1752                                            mCurrentDevice);
   1753         atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
   1754         return true;
   1755     }
   1756 
   1757     private void processUnknownAt(String atString) {
   1758         // TODO (BT)
   1759         log("processUnknownAt - atString = "+ atString);
   1760         String atCommand = parseUnknownAt(atString);
   1761         int commandType = getAtCommandType(atCommand);
   1762         if (atCommand.startsWith("+CSCS"))
   1763             processAtCscs(atCommand.substring(5), commandType);
   1764         else if (atCommand.startsWith("+CPBS"))
   1765             processAtCpbs(atCommand.substring(5), commandType);
   1766         else if (atCommand.startsWith("+CPBR"))
   1767             processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice);
   1768         else if (!processVendorSpecificAt(atCommand))
   1769             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   1770     }
   1771 
   1772     private void processKeyPressed() {
   1773         if (mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) {
   1774             if (mPhoneProxy != null) {
   1775                 try {
   1776                     mPhoneProxy.answerCall();
   1777                 } catch (RemoteException e) {
   1778                     Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1779                 }
   1780             } else {
   1781                 Log.e(TAG, "Handsfree phone proxy null for answering call");
   1782             }
   1783         } else if (mPhoneState.getNumActiveCall() > 0) {
   1784             if (!isAudioOn())
   1785             {
   1786                 connectAudioNative(getByteAddress(mCurrentDevice));
   1787             }
   1788             else
   1789             {
   1790                 if (mPhoneProxy != null) {
   1791                     try {
   1792                         mPhoneProxy.hangupCall();
   1793                     } catch (RemoteException e) {
   1794                         Log.e(TAG, Log.getStackTraceString(new Throwable()));
   1795                     }
   1796                 } else {
   1797                     Log.e(TAG, "Handsfree phone proxy null for hangup call");
   1798                 }
   1799             }
   1800         } else {
   1801             String dialNumber = mPhonebook.getLastDialledNumber();
   1802             if (dialNumber == null) {
   1803                 if (DBG) log("processKeyPressed, last dial number null");
   1804                 return;
   1805             }
   1806             Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
   1807                                        Uri.fromParts(SCHEME_TEL, dialNumber, null));
   1808             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1809             mService.startActivity(intent);
   1810         }
   1811     }
   1812 
   1813     private void onConnectionStateChanged(int state, byte[] address) {
   1814         StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
   1815         event.valueInt = state;
   1816         event.device = getDevice(address);
   1817         sendMessage(STACK_EVENT, event);
   1818     }
   1819 
   1820     private void onAudioStateChanged(int state, byte[] address) {
   1821         StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
   1822         event.valueInt = state;
   1823         event.device = getDevice(address);
   1824         sendMessage(STACK_EVENT, event);
   1825     }
   1826 
   1827     private void onVrStateChanged(int state) {
   1828         StackEvent event = new StackEvent(EVENT_TYPE_VR_STATE_CHANGED);
   1829         event.valueInt = state;
   1830         sendMessage(STACK_EVENT, event);
   1831     }
   1832 
   1833     private void onAnswerCall() {
   1834         StackEvent event = new StackEvent(EVENT_TYPE_ANSWER_CALL);
   1835         sendMessage(STACK_EVENT, event);
   1836     }
   1837 
   1838     private void onHangupCall() {
   1839         StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);
   1840         sendMessage(STACK_EVENT, event);
   1841     }
   1842 
   1843     private void onVolumeChanged(int type, int volume) {
   1844         StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);
   1845         event.valueInt = type;
   1846         event.valueInt2 = volume;
   1847         sendMessage(STACK_EVENT, event);
   1848     }
   1849 
   1850     private void onDialCall(String number) {
   1851         StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);
   1852         event.valueString = number;
   1853         sendMessage(STACK_EVENT, event);
   1854     }
   1855 
   1856     private void onSendDtmf(int dtmf) {
   1857         StackEvent event = new StackEvent(EVENT_TYPE_SEND_DTMF);
   1858         event.valueInt = dtmf;
   1859         sendMessage(STACK_EVENT, event);
   1860     }
   1861 
   1862     private void onNoiceReductionEnable(boolean enable) {
   1863         StackEvent event = new StackEvent(EVENT_TYPE_NOICE_REDUCTION);
   1864         event.valueInt = enable ? 1 : 0;
   1865         sendMessage(STACK_EVENT, event);
   1866     }
   1867 
   1868     private void onAtChld(int chld) {
   1869         StackEvent event = new StackEvent(EVENT_TYPE_AT_CHLD);
   1870         event.valueInt = chld;
   1871         sendMessage(STACK_EVENT, event);
   1872     }
   1873 
   1874     private void onAtCnum() {
   1875         StackEvent event = new StackEvent(EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST);
   1876         sendMessage(STACK_EVENT, event);
   1877     }
   1878 
   1879     private void onAtCind() {
   1880         StackEvent event = new StackEvent(EVENT_TYPE_AT_CIND);
   1881         sendMessage(STACK_EVENT, event);
   1882     }
   1883 
   1884     private void onAtCops() {
   1885         StackEvent event = new StackEvent(EVENT_TYPE_AT_COPS);
   1886         sendMessage(STACK_EVENT, event);
   1887     }
   1888 
   1889     private void onAtClcc() {
   1890         StackEvent event = new StackEvent(EVENT_TYPE_AT_CLCC);
   1891         sendMessage(STACK_EVENT, event);
   1892     }
   1893 
   1894     private void onUnknownAt(String atString) {
   1895         StackEvent event = new StackEvent(EVENT_TYPE_UNKNOWN_AT);
   1896         event.valueString = atString;
   1897         sendMessage(STACK_EVENT, event);
   1898     }
   1899 
   1900     private void onKeyPressed() {
   1901         StackEvent event = new StackEvent(EVENT_TYPE_KEY_PRESSED);
   1902         sendMessage(STACK_EVENT, event);
   1903     }
   1904 
   1905     private void processIntentBatteryChanged(Intent intent) {
   1906         int batteryLevel = intent.getIntExtra("level", -1);
   1907         int scale = intent.getIntExtra("scale", -1);
   1908         if (batteryLevel == -1 || scale == -1 || scale == 0) {
   1909             Log.e(TAG, "Bad Battery Changed intent: " + batteryLevel + "," + scale);
   1910             return;
   1911         }
   1912         batteryLevel = batteryLevel * 5 / scale;
   1913         mPhoneState.setBatteryCharge(batteryLevel);
   1914     }
   1915 
   1916     private void processDeviceStateChanged(HeadsetDeviceState deviceState) {
   1917         notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, deviceState.mSignal,
   1918                                  deviceState.mBatteryCharge);
   1919     }
   1920 
   1921     private void processSendClccResponse(HeadsetClccResponse clcc) {
   1922         clccResponseNative(clcc.mIndex, clcc.mDirection, clcc.mStatus, clcc.mMode, clcc.mMpty,
   1923                            clcc.mNumber, clcc.mType);
   1924     }
   1925 
   1926     private void processSendVendorSpecificResultCode(HeadsetVendorSpecificResultCode resultCode) {
   1927         String stringToSend = resultCode.mCommand + ": ";
   1928         if (resultCode.mArg != null) {
   1929             stringToSend += resultCode.mArg;
   1930         }
   1931         atResponseStringNative(stringToSend);
   1932     }
   1933 
   1934     private String getCurrentDeviceName() {
   1935         String defaultName = "<unknown>";
   1936         if (mCurrentDevice == null) {
   1937             return defaultName;
   1938         }
   1939         String deviceName = mCurrentDevice.getName();
   1940         if (deviceName == null) {
   1941             return defaultName;
   1942         }
   1943         return deviceName;
   1944     }
   1945 
   1946     private byte[] getByteAddress(BluetoothDevice device) {
   1947         return Utils.getBytesFromAddress(device.getAddress());
   1948     }
   1949 
   1950     private BluetoothDevice getDevice(byte[] address) {
   1951         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
   1952     }
   1953 
   1954     private boolean isInCall() {
   1955         return ((mPhoneState.getNumActiveCall() > 0) || (mPhoneState.getNumHeldCall() > 0) ||
   1956                 (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE));
   1957     }
   1958 
   1959     boolean isConnected() {
   1960         IState currentState = getCurrentState();
   1961         return (currentState == mConnected || currentState == mAudioOn);
   1962     }
   1963 
   1964     boolean okToConnect(BluetoothDevice device) {
   1965         AdapterService adapterService = AdapterService.getAdapterService();
   1966         int priority = mService.getPriority(device);
   1967         boolean ret = false;
   1968         //check if this is an incoming connection in Quiet mode.
   1969         if((adapterService == null) ||
   1970            ((adapterService.isQuietModeEnabled() == true) &&
   1971            (mTargetDevice == null))){
   1972             ret = false;
   1973         }
   1974         // check priority and accept or reject the connection. if priority is undefined
   1975         // it is likely that our SDP has not completed and peer is initiating the
   1976         // connection. Allow this connection, provided the device is bonded
   1977         else if((BluetoothProfile.PRIORITY_OFF < priority) ||
   1978                 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
   1979                 (device.getBondState() != BluetoothDevice.BOND_NONE))){
   1980             ret= true;
   1981         }
   1982         return ret;
   1983     }
   1984 
   1985     @Override
   1986     protected void log(String msg) {
   1987         if (DBG) {
   1988             super.log(msg);
   1989         }
   1990     }
   1991 
   1992     public void handleAccessPermissionResult(Intent intent) {
   1993         log("handleAccessPermissionResult");
   1994         if(mPhonebook != null) {
   1995             if (!mPhonebook.getCheckingAccessPermission()) {
   1996                 return;
   1997             }
   1998             int atCommandResult = 0;
   1999             int atCommandErrorCode = 0;
   2000             //HeadsetBase headset = mHandsfree.getHeadset();
   2001             // ASSERT: (headset != null) && headSet.isConnected()
   2002             // REASON: mCheckingAccessPermission is true, otherwise resetAtState
   2003             // has set mCheckingAccessPermission to false
   2004             if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
   2005                 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
   2006                     BluetoothDevice.CONNECTION_ACCESS_NO) ==
   2007                     BluetoothDevice.CONNECTION_ACCESS_YES) {
   2008                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
   2009                         mCurrentDevice.setTrust(true);
   2010                     }
   2011                     atCommandResult = mPhonebook.processCpbrCommand();
   2012                 }
   2013             }
   2014             mPhonebook.setCpbrIndex(-1);
   2015             mPhonebook.setCheckingAccessPermission(false);
   2016 
   2017             if (atCommandResult >= 0) {
   2018                 atResponseCodeNative(atCommandResult, atCommandErrorCode);
   2019             }
   2020             else
   2021                 log("handleAccessPermissionResult - RESULT_NONE");
   2022         }
   2023         else {
   2024             Log.e(TAG, "Phonebook handle null");
   2025             atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
   2026         }
   2027     }
   2028 
   2029     private static final String SCHEME_TEL = "tel";
   2030 
   2031     // Event types for STACK_EVENT message
   2032     final private static int EVENT_TYPE_NONE = 0;
   2033     final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
   2034     final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
   2035     final private static int EVENT_TYPE_VR_STATE_CHANGED = 3;
   2036     final private static int EVENT_TYPE_ANSWER_CALL = 4;
   2037     final private static int EVENT_TYPE_HANGUP_CALL = 5;
   2038     final private static int EVENT_TYPE_VOLUME_CHANGED = 6;
   2039     final private static int EVENT_TYPE_DIAL_CALL = 7;
   2040     final private static int EVENT_TYPE_SEND_DTMF = 8;
   2041     final private static int EVENT_TYPE_NOICE_REDUCTION = 9;
   2042     final private static int EVENT_TYPE_AT_CHLD = 10;
   2043     final private static int EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST = 11;
   2044     final private static int EVENT_TYPE_AT_CIND = 12;
   2045     final private static int EVENT_TYPE_AT_COPS = 13;
   2046     final private static int EVENT_TYPE_AT_CLCC = 14;
   2047     final private static int EVENT_TYPE_UNKNOWN_AT = 15;
   2048     final private static int EVENT_TYPE_KEY_PRESSED = 16;
   2049 
   2050     private class StackEvent {
   2051         int type = EVENT_TYPE_NONE;
   2052         int valueInt = 0;
   2053         int valueInt2 = 0;
   2054         String valueString = null;
   2055         BluetoothDevice device = null;
   2056 
   2057         private StackEvent(int type) {
   2058             this.type = type;
   2059         }
   2060     }
   2061 
   2062     /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode);
   2063     /*package*/ native boolean atResponseStringNative(String responseString);
   2064 
   2065     private native static void classInitNative();
   2066     private native void initializeNative();
   2067     private native void cleanupNative();
   2068     private native boolean connectHfpNative(byte[] address);
   2069     private native boolean disconnectHfpNative(byte[] address);
   2070     private native boolean connectAudioNative(byte[] address);
   2071     private native boolean disconnectAudioNative(byte[] address);
   2072     private native boolean startVoiceRecognitionNative();
   2073     private native boolean stopVoiceRecognitionNative();
   2074     private native boolean setVolumeNative(int volumeType, int volume);
   2075     private native boolean cindResponseNative(int service, int numActive, int numHeld,
   2076                                               int callState, int signal, int roam,
   2077                                               int batteryCharge);
   2078     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
   2079                                                     int batteryCharge);
   2080 
   2081     private native boolean clccResponseNative(int index, int dir, int status, int mode,
   2082                                               boolean mpty, String number, int type);
   2083     private native boolean copsResponseNative(String operatorName);
   2084 
   2085     private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
   2086                                                   String number, int type);
   2087 }
   2088