HFP: Indicate profile as connected after SLC is established

* HFP profile connection require RFCOMM connection first and then a
  Service Level Connection (SLC) on top of RFCOMM
* HFP profile is only considered fully connected when SLC is
  established
* Bluetooth stack currently sends
   BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED when RFCOMM is
  connected. Instead, it should send it when SLC is established.
* This CL fixes the above issue by sending connection state changed
  intent after SLC is established.
* This CL also removes instances where emitted connection state changed
  intents have same new state and previous state (i.e. no real state
  change)
* Moved processSlcConnected out from headset state machine to
  queryPhoneState()

Bug: 63775402
Test: HFP and HSP PTS tests, HFP regression
Change-Id: I0d4e1c17f502968e4f88f3c11c2b8001dc79f7b9
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index eb81c15..d1fc4a5 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -487,12 +487,11 @@
         // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (!mStateMachine.isConnected()) {
-            return false;
-        }
-        if (!mStateMachine.isSlcConnected()) {
+            Log.w(TAG, "connectAudio: profile not connected");
             return false;
         }
         if (mStateMachine.isAudioOn()) {
+            Log.w(TAG, "connectAudio: audio is already ON");
             return false;
         }
         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 155d5f0..5a9c3a0 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -115,7 +115,6 @@
     private static final int DIALING_OUT_TIMEOUT_VALUE = 10000;
     private static final int START_VR_TIMEOUT_VALUE = 5000;
     private static final int CLCC_RSP_TIMEOUT_VALUE = 5000;
-
     private static final int CONNECT_TIMEOUT_MILLIS = 30000;
 
     // Max number of HF connections at any time
@@ -169,9 +168,6 @@
     // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions
     private boolean mForceScoAudio = false;
 
-    // Indicate whether service level connection has been established for this device
-    private boolean mSlcConnected = false;
-
     // mCurrentDevice is the device connected before the state changes
     // mTargetDevice is the device to be connected
     // mIncomingDevice is the device connecting to us, valid only in Pending state
@@ -344,7 +340,6 @@
             mPhoneState.listenForPhoneState(false);
             mVoiceRecognitionStarted = false;
             mWaitingForVoiceRecognition = false;
-            mSlcConnected = false;
         }
 
         @Override
@@ -386,6 +381,9 @@
                 case CALL_STATE_CHANGED:
                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
                     break;
+                case DEVICE_STATE_CHANGED:
+                    log("Disconnected: ignoring DEVICE_STATE_CHANGED event");
+                    break;
                 case STACK_EVENT:
                     StackEvent event = (StackEvent) message.obj;
                     log("Disconnected: event type: " + event.type);
@@ -418,9 +416,12 @@
                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
                     Log.w(TAG, "Disconnected: ignore DISCONNECTED event, device=" + device);
                     break;
+                // Both events result in Pending state as SLC establishment is still required
+                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
                     if (okToConnect(device)) {
-                        Log.i(TAG, "Disconnected: connecting incoming HF, device=" + device);
+                        Log.i(TAG,
+                                "Disconnected: connected/connecting incoming HF, device=" + device);
                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
                                 BluetoothProfile.STATE_DISCONNECTED);
                         synchronized (HeadsetStateMachine.this) {
@@ -434,36 +435,6 @@
                                         + device.getBondState() + ", device=" + device);
                         // reject the connection and stay in Disconnected state itself
                         disconnectHfpNative(getByteAddress(device));
-                        // the other profile connection should be initiated
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
-                                BluetoothProfile.STATE_DISCONNECTED);
-                    }
-                    break;
-                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
-                    if (okToConnect(device)) {
-                        Log.i(TAG, "Incoming Hf accepted");
-                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                BluetoothProfile.STATE_DISCONNECTED);
-                        synchronized (HeadsetStateMachine.this) {
-                            if (!mConnectedDevicesList.contains(device)) {
-                                mConnectedDevicesList.add(device);
-                                Log.d(TAG, "device " + device.getAddress()
-                                                + " is adding in Disconnected state");
-                            }
-                            mCurrentDevice = device;
-                            transitionTo(mConnected);
-                        }
-                        configAudioParameters(device);
-                    } else {
-                        // reject the connection and stay in Disconnected state itself
-                        Log.i(TAG,
-                                "Disconnected: rejected incoming HF RFCOMM, priority="
-                                        + mService.getPriority(device) + " bondState="
-                                        + device.getBondState() + ", device=" + device);
-                        disconnectHfpNative(getByteAddress(device));
-                        // the other profile connection should be initiated
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
-                                BluetoothProfile.STATE_DISCONNECTED);
                     }
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTING:
@@ -476,6 +447,9 @@
         }
     }
 
+    // Per HFP 1.7.1 spec page 23/144, Pending state needs to handle
+    //      AT+BRSF, AT+CIND, AT+CMER, AT+BIND, +CHLD
+    // commands during SLC establishment
     private class Pending extends State {
         @Override
         public void enter() {
@@ -517,27 +491,33 @@
                 case CALL_STATE_CHANGED:
                     processCallState((HeadsetCallState) message.obj, message.arg1 == 1);
                     break;
+                case BIND_RESPONSE: {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    bindResponseNative(message.arg1, message.arg2 == 1, getByteAddress(device));
+                    break;
+                }
+                case DEVICE_STATE_CHANGED:
+                    log("Pending: ignoring DEVICE_STATE_CHANGED event");
+                    break;
                 case STACK_EVENT:
                     StackEvent event = (StackEvent) message.obj;
                     log("Pending: event type: " + event.type);
                     switch (event.type) {
                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
-                            if (device1 != null && device1.equals(event.device)) {
-                                Log.d(TAG, "remove connect timeout for device = " + device1);
-                                removeMessages(CONNECT_TIMEOUT);
-                            }
                             processConnectionEvent(event.valueInt, event.device);
                             break;
-
+                        case EVENT_TYPE_AT_CHLD:
+                            processAtChld(event.valueInt, event.device);
+                            break;
+                        case EVENT_TYPE_AT_CIND:
+                            processAtCind(event.device);
+                            break;
+                        case EVENT_TYPE_WBS:
+                            processWBSEvent(event.valueInt, event.device);
+                            break;
                         case EVENT_TYPE_BIND:
                             processAtBind(event.valueString, event.device);
                             break;
-
-                        case EVENT_TYPE_BIEV:
-                            processAtBiev(event.valueInt, event.valueInt2, event.device);
-                            break;
-
                         default:
                             Log.e(TAG, "Pending: Unexpected event: " + event.type);
                             break;
@@ -553,6 +533,7 @@
         // in Pending state
         private void processConnectionEvent(int state, BluetoothDevice device) {
             Log.d(TAG, "Pending: processConnectionEvent, state=" + state + ", device=" + device);
+            BluetoothDevice pendingDevice = getDeviceForMessage(CONNECT_TIMEOUT);
             switch (state) {
                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
                     if (mConnectedDevicesList.contains(device)) {
@@ -635,53 +616,51 @@
                             mTargetDevice = null;
                             transitionTo(mConnected);
                         }
-                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
-                        synchronized (HeadsetStateMachine.this) {
-                            mCurrentDevice = device;
-                            mConnectedDevicesList.add(device);
+                    } else if (!device.equals(mTargetDevice) && !device.equals(mIncomingDevice)) {
+                        Log.w(TAG,
+                                "Pending: unknown incoming HF connected on RFCOMM, device="
+                                        + device);
+                        if (!okToConnect(device)) {
+                            // reject the connection and stay in Pending state itself
+                            Log.i(TAG,
+                                    "Pending: unknown incoming HF rejected on RFCOMM, priority="
+                                            + mService.getPriority(device)
+                                            + " bondState=" + device.getBondState());
+                            disconnectHfpNative(getByteAddress(device));
+                        }
+                    } else {
+                        // Do nothing in normal case, wait for SLC connected event
+                        pendingDevice = null;
+                    }
+                    break;
+                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
+                    int previousConnectionState = BluetoothProfile.STATE_CONNECTING;
+                    synchronized (HeadsetStateMachine.this) {
+                        mCurrentDevice = device;
+                        mConnectedDevicesList.add(device);
+                        if (device.equals(mTargetDevice)) {
                             Log.d(TAG,
                                     "Pending: added " + device
                                             + " to mConnectedDevicesList, requested by us");
                             mTargetDevice = null;
                             transitionTo(mConnected);
-                        }
-                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                BluetoothProfile.STATE_CONNECTING);
-                        configAudioParameters(device);
-                    } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
-                        synchronized (HeadsetStateMachine.this) {
-                            mCurrentDevice = device;
-                            mConnectedDevicesList.add(device);
+                        } else if (device.equals(mIncomingDevice)) {
                             Log.d(TAG,
                                     "Pending: added " + device
                                             + " to mConnectedDevicesList, requested by remote");
                             mIncomingDevice = null;
                             transitionTo(mConnected);
-                        }
-                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                BluetoothProfile.STATE_CONNECTING);
-                        configAudioParameters(device);
-                    } else {
-                        Log.w(TAG, "Some other incoming HF connected in Pending state");
-                        if (okToConnect(device)) {
-                            Log.i(TAG, "Incoming Hf accepted");
-                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                    BluetoothProfile.STATE_DISCONNECTED);
-                            synchronized (HeadsetStateMachine.this) {
-                                mCurrentDevice = device;
-                                mConnectedDevicesList.add(device);
-                                Log.d(TAG, "device " + device.getAddress()
-                                                + " is added in Pending state");
-                            }
-                            configAudioParameters(device);
                         } else {
-                            // reject the connection and stay in Pending state itself
-                            Log.i(TAG, "Incoming Hf rejected. priority="
-                                            + mService.getPriority(device) + " bondState="
-                                            + device.getBondState());
-                            disconnectHfpNative(getByteAddress(device));
+                            Log.d(TAG,
+                                    "Pending: added " + device
+                                            + "to mConnectedDevicesList, unknown source");
+                            previousConnectionState = BluetoothProfile.STATE_DISCONNECTED;
                         }
                     }
+                    configAudioParameters(device);
+                    queryPhoneState();
+                    broadcastConnectionState(
+                            device, BluetoothProfile.STATE_CONNECTED, previousConnectionState);
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
                     if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
@@ -716,6 +695,10 @@
                     Log.e(TAG, "Incorrect state: " + state);
                     break;
             }
+            if (pendingDevice != null && pendingDevice.equals(device)) {
+                removeMessages(CONNECT_TIMEOUT);
+                Log.d(TAG, "Pending: removed CONNECT_TIMEOUT for device=" + pendingDevice);
+            }
         }
 
         private void processMultiHFConnected(BluetoothDevice device) {
@@ -799,7 +782,6 @@
                                 mMultiDisconnectDevice = DisconnectConnectedDevice;
                                 transitionTo(mMultiHFPending);
                             }
-                            DisconnectConnectedDevice = null;
                         }
                     } else if (mConnectedDevicesList.size() < max_hf_connections) {
                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
@@ -831,7 +813,7 @@
                     if (!disconnectHfpNative(getByteAddress(device))) {
                         // Failed disconnection request
                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                BluetoothProfile.STATE_DISCONNECTED);
+                                BluetoothProfile.STATE_DISCONNECTING);
                         break;
                     }
                     // Pending disconnection confirmation
@@ -1027,15 +1009,7 @@
                     }
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
-                    processSlcConnected();
-                    break;
-                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
-                    if (mConnectedDevicesList.contains(device)) {
-                        mIncomingDevice = null;
-                        mTargetDevice = null;
-                        break;
-                    }
-                    Log.w(TAG, "HFP to be Connected in Connected state");
+                    // Should have been rejected in CONNECTION_STATE_CONNECTED
                     if (okToConnect(device)
                             && (mConnectedDevicesList.size() < max_hf_connections)) {
                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
@@ -1051,7 +1025,18 @@
                             transitionTo(mConnected);
                         }
                         configAudioParameters(device);
-                    } else {
+                    }
+                    queryPhoneState();
+                    break;
+                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
+                    if (mConnectedDevicesList.contains(device)) {
+                        mIncomingDevice = null;
+                        mTargetDevice = null;
+                        break;
+                    }
+                    Log.w(TAG, "HFP to be Connected in Connected state");
+                    if (!okToConnect(device)
+                            || (mConnectedDevicesList.size() >= max_hf_connections)) {
                         // reject the connection and stay in Connected state itself
                         Log.i(TAG, "Incoming Hf rejected. priority=" + mService.getPriority(device)
                                         + " bondState=" + device.getBondState());
@@ -1100,19 +1085,6 @@
             }
         }
 
-        private void processSlcConnected() {
-            mSlcConnected = true;
-            if (mPhoneProxy != null) {
-                try {
-                    mPhoneProxy.queryPhoneState();
-                } catch (RemoteException e) {
-                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
-                }
-            } else {
-                Log.e(TAG, "Handsfree phone proxy null for query phone state");
-            }
-        }
-
         private void processMultiHFConnected(BluetoothDevice device) {
             log("Connect state: processMultiHFConnected");
             if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
@@ -1336,11 +1308,6 @@
                     log("AudioOn: event type: " + event.type);
                     switch (event.type) {
                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
-                            if (device1 != null && device1.equals(event.device)) {
-                                Log.d(TAG, "remove connect timeout for device = " + device1);
-                                removeMessages(CONNECT_TIMEOUT);
-                            }
                             processConnectionEvent(event.valueInt, event.device);
                             break;
                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
@@ -1409,6 +1376,7 @@
         // in AudioOn state. Some headsets disconnect RFCOMM prior to SCO down. Handle this
         private void processConnectionEvent(int state, BluetoothDevice device) {
             Log.d(TAG, "AudioOn: processConnectionEvent, state=" + state + ", device=" + device);
+            BluetoothDevice pendingDevice = getDeviceForMessage(CONNECT_TIMEOUT);
             switch (state) {
                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
                     if (mConnectedDevicesList.contains(device)) {
@@ -1437,15 +1405,7 @@
                     }
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
-                    processSlcConnected();
-                    break;
-                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
-                    if (mConnectedDevicesList.contains(device)) {
-                        mIncomingDevice = null;
-                        mTargetDevice = null;
-                        break;
-                    }
-                    Log.w(TAG, "HFP to be Connected in AudioOn state");
+                    // Should have been rejected in CONNECTION_STATE_CONNECTED
                     if (okToConnect(device)
                             && (mConnectedDevicesList.size() < max_hf_connections)) {
                         Log.i(TAG, "AudioOn: accepted incoming HF");
@@ -1461,19 +1421,37 @@
                             }
                         }
                         configAudioParameters(device);
-                    } else {
+                    }
+                    queryPhoneState();
+                    break;
+                case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
+                    if (mConnectedDevicesList.contains(device)) {
+                        mIncomingDevice = null;
+                        mTargetDevice = null;
+                        break;
+                    }
+                    Log.w(TAG, "AudioOn: HFP to be connected device=" + device);
+                    if (!okToConnect(device)
+                            || (mConnectedDevicesList.size() >= max_hf_connections)) {
                         // reject the connection and stay in Connected state itself
                         Log.i(TAG,
                                 "AudioOn: rejected incoming HF, priority="
                                         + mService.getPriority(device)
                                         + " bondState=" + device.getBondState());
                         disconnectHfpNative(getByteAddress(device));
+                    } else {
+                        // Do nothing in normal case, wait for SLC connected event
+                        pendingDevice = null;
                     }
                     break;
                 default:
                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
                     break;
             }
+            if (pendingDevice != null && pendingDevice.equals(device)) {
+                removeMessages(CONNECT_TIMEOUT);
+                Log.d(TAG, "AudioOn: removed CONNECT_TIMEOUT for device=" + pendingDevice);
+            }
         }
 
         // in AudioOn state
@@ -1487,6 +1465,9 @@
                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
                     if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+                        if (device.equals(mActiveScoDevice)) {
+                            mActiveScoDevice = null;
+                        }
                         mAudioManager.setBluetoothScoOn(false);
                         broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
                                 BluetoothHeadset.STATE_AUDIO_CONNECTED);
@@ -1504,19 +1485,6 @@
             }
         }
 
-        private void processSlcConnected() {
-            mSlcConnected = true;
-            if (mPhoneProxy != null) {
-                try {
-                    mPhoneProxy.queryPhoneState();
-                } catch (RemoteException e) {
-                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
-                }
-            } else {
-                Log.e(TAG, "Handsfree phone proxy null for query phone state");
-            }
-        }
-
         private void processIntentScoVolume(Intent intent, BluetoothDevice device) {
             int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
             if (mPhoneState.getSpeakerVolume() != volumeValue) {
@@ -1667,11 +1635,6 @@
                     log("MultiHFPending: event type: " + event.type);
                     switch (event.type) {
                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                            BluetoothDevice device1 = getDeviceForMessage(CONNECT_TIMEOUT);
-                            if (device1 != null && device1.equals(event.device)) {
-                                Log.d(TAG, "remove connect timeout for device = " + device1);
-                                removeMessages(CONNECT_TIMEOUT);
-                            }
                             processConnectionEvent(event.valueInt, event.device);
                             break;
                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
@@ -1743,6 +1706,7 @@
         private void processConnectionEvent(int state, BluetoothDevice device) {
             Log.d(TAG,
                     "MultiPending: processConnectionEvent, state=" + state + ", device=" + device);
+            BluetoothDevice pendingDevice = getDeviceForMessage(CONNECT_TIMEOUT);
             switch (state) {
                 case HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED:
                     if (mConnectedDevicesList.contains(device)) {
@@ -1818,8 +1782,9 @@
                     }
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED:
-                    /* Outgoing disconnection for device failed */
                     if (mConnectedDevicesList.contains(device)) {
+                        // Disconnection failure does not go through SLC establishment
+                        Log.w(TAG, "MultiPending: disconnection failed for device " + device);
                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
                                 BluetoothProfile.STATE_DISCONNECTING);
                         if (mTargetDevice != null) {
@@ -1834,50 +1799,52 @@
                             else
                                 transitionTo(mConnected);
                         }
-                    } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
-                        synchronized (HeadsetStateMachine.this) {
-                            mCurrentDevice = device;
-                            mConnectedDevicesList.add(device);
-                            Log.d(TAG, "device " + device.getAddress()
-                                            + " is added in MultiHFPending state");
+                    } else if (!device.equals(mTargetDevice)) {
+                        Log.w(TAG,
+                                "MultiPending: unknown incoming HF connected on RFCOMM"
+                                        + ", device=" + device);
+                        if (!okToConnect(device)
+                                || (mConnectedDevicesList.size() >= max_hf_connections)) {
+                            // reject the connection and stay in Pending state itself
+                            Log.i(TAG,
+                                    "MultiPending: unknown incoming HF rejected on RFCOMM"
+                                            + ", priority=" + mService.getPriority(device)
+                                            + ", bondState=" + device.getBondState());
+                            disconnectHfpNative(getByteAddress(device));
+                        } else {
+                            // Ok to connect, keep waiting for SLC connected event
+                            pendingDevice = null;
+                        }
+                    } else {
+                        // Do nothing in normal case, keep waiting for SLC connected event
+                        pendingDevice = null;
+                    }
+                    break;
+                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
+                    int previousConnectionState = BluetoothProfile.STATE_CONNECTING;
+                    synchronized (HeadsetStateMachine.this) {
+                        mCurrentDevice = device;
+                        mConnectedDevicesList.add(device);
+                        if (device.equals(mTargetDevice)) {
+                            Log.d(TAG,
+                                    "MultiPending: added " + device
+                                            + " to mConnectedDevicesList, requested by us");
                             mTargetDevice = null;
                             if (mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED)
                                 transitionTo(mAudioOn);
                             else
                                 transitionTo(mConnected);
-                        }
-
-                        broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                BluetoothProfile.STATE_CONNECTING);
-                        configAudioParameters(device);
-                    } else {
-                        Log.w(TAG, "Some other incoming HF connected"
-                                        + "in Multi Pending state");
-                        if (okToConnect(device)
-                                && (mConnectedDevicesList.size() < max_hf_connections)) {
-                            Log.i(TAG, "Incoming Hf accepted");
-                            broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
-                                    BluetoothProfile.STATE_DISCONNECTED);
-                            synchronized (HeadsetStateMachine.this) {
-                                if (!mConnectedDevicesList.contains(device)) {
-                                    mCurrentDevice = device;
-                                    mConnectedDevicesList.add(device);
-                                    Log.d(TAG, "device " + device.getAddress()
-                                                    + " is added in MultiHFPending state");
-                                }
-                            }
-                            configAudioParameters(device);
                         } else {
-                            // reject the connection and stay in Pending state itself
-                            Log.i(TAG, "Incoming Hf rejected. priority="
-                                            + mService.getPriority(device) + " bondState="
-                                            + device.getBondState());
-                            disconnectHfpNative(getByteAddress(device));
+                            Log.d(TAG,
+                                    "MultiPending: added " + device
+                                            + "to mConnectedDevicesList, unknown source");
+                            previousConnectionState = BluetoothProfile.STATE_DISCONNECTED;
                         }
                     }
-                    break;
-                case HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED:
-                    processSlcConnected();
+                    configAudioParameters(device);
+                    queryPhoneState();
+                    broadcastConnectionState(
+                            device, BluetoothProfile.STATE_CONNECTED, previousConnectionState);
                     break;
                 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING:
                     if (mConnectedDevicesList.contains(device)) {
@@ -1903,6 +1870,10 @@
                     Log.e(TAG, "MultiPending: Incorrect state: " + state);
                     break;
             }
+            if (pendingDevice != null && pendingDevice.equals(device)) {
+                removeMessages(CONNECT_TIMEOUT);
+                Log.d(TAG, "MultiPending: removed CONNECT_TIMEOUT for device=" + pendingDevice);
+            }
         }
 
         private void processAudioEvent(int state, BluetoothDevice device) {
@@ -1936,6 +1907,9 @@
                 case HeadsetHalConstants.AUDIO_STATE_DISCONNECTED:
                     if (mAudioState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                         mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+                        if (device.equals(mActiveScoDevice)) {
+                            mActiveScoDevice = null;
+                        }
                         mAudioManager.setBluetoothScoOn(false);
                         broadcastAudioState(device, BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
                                 BluetoothHeadset.STATE_AUDIO_CONNECTED);
@@ -1952,19 +1926,6 @@
             }
         }
 
-        private void processSlcConnected() {
-            mSlcConnected = true;
-            if (mPhoneProxy != null) {
-                try {
-                    mPhoneProxy.queryPhoneState();
-                } catch (RemoteException e) {
-                    Log.e(TAG, Log.getStackTraceString(new Throwable()));
-                }
-            } else {
-                Log.e(TAG, "MultiPending: Handsfree phone proxy null for query phone state");
-            }
-        }
-
         private void processMultiHFConnected(BluetoothDevice device) {
             log("MultiHFPending: processMultiHFConnected, device=" + device);
             if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) {
@@ -2092,10 +2053,6 @@
         return false;
     }
 
-    boolean isSlcConnected() {
-        return mSlcConnected;
-    }
-
     public void setAudioRouteAllowed(boolean allowed) {
         mAudioRouteAllowed = allowed;
         setScoAllowedNative(allowed);
@@ -2871,6 +2828,18 @@
         }
     }
 
+    private void queryPhoneState() {
+        if (mPhoneProxy != null) {
+            try {
+                mPhoneProxy.queryPhoneState();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        } else {
+            Log.e(TAG, "Handsfree phone proxy null for query phone state");
+        }
+    }
+
     /**
      * Find a character ch, ignoring quoted sections.
      * Return input.length() if not found.