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