Work around for errant headsets.

1. Some headsets send an AVCTP connection before an AVDTP connection
When that AVCTP connection fails, we get stuck in IncomingA2DP state
because we don't handle AVCTP signals for now.
We need to handle the signals and fix it better.

2. Also when ACL gets disconnected, reset state i.e
when the low level connection disconnected reset our state.
This is like a fail safe in case we get stuck.

Bug: 3051490
Change-Id: Ibcf188103999ffb1e08c36c2554504c639fb7f50
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index ff82d39..17b28d1 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -136,6 +136,10 @@
                 Message msg = new Message();
                 msg.what = AUTO_CONNECT_PROFILES;
                 sendMessageDelayed(msg, AUTO_CONNECT_DELAY);
+            } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+                // This is technically not needed, but we can get stuck sometimes.
+                // For example, if incoming A2DP fails, we are not informed by Bluez
+                sendMessage(TRANSITION_TO_STABLE);
             }
       }
     };
@@ -175,6 +179,7 @@
         filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
         filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
 
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
@@ -302,7 +307,11 @@
                 Log.e(TAG, "Error: OutgoingHandsfree state with command:" + mCommand);
             }
             mStatus = processCommand(mCommand);
-            if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+            if (!mStatus) {
+                sendMessage(TRANSITION_TO_STABLE);
+                mService.sendProfileStateMessage(BluetoothProfileState.HFP,
+                                                 BluetoothProfileState.TRANSITION_TO_STABLE);
+            }
         }
 
         @Override
@@ -393,7 +402,11 @@
                 Log.e(TAG, "Error: IncomingHandsfree state with command:" + mCommand);
             }
             mStatus = processCommand(mCommand);
-            if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+            if (!mStatus) {
+                sendMessage(TRANSITION_TO_STABLE);
+                mService.sendProfileStateMessage(BluetoothProfileState.HFP,
+                                                 BluetoothProfileState.TRANSITION_TO_STABLE);
+            }
         }
 
         @Override
@@ -461,7 +474,11 @@
                 Log.e(TAG, "Error: OutgoingA2DP state with command:" + mCommand);
             }
             mStatus = processCommand(mCommand);
-            if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+            if (!mStatus) {
+                sendMessage(TRANSITION_TO_STABLE);
+                mService.sendProfileStateMessage(BluetoothProfileState.A2DP,
+                                                 BluetoothProfileState.TRANSITION_TO_STABLE);
+            }
         }
 
         @Override
@@ -549,7 +566,11 @@
                 Log.e(TAG, "Error: IncomingA2DP state with command:" + mCommand);
             }
             mStatus = processCommand(mCommand);
-            if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+            if (!mStatus) {
+                sendMessage(TRANSITION_TO_STABLE);
+                mService.sendProfileStateMessage(BluetoothProfileState.A2DP,
+                                                 BluetoothProfileState.TRANSITION_TO_STABLE);
+            }
         }
 
         @Override
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index 946dcaa..dc0b32b 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -43,10 +43,10 @@
     private static final boolean DBG = true; // STOPSHIP - change to false.
     private static final String TAG = "BluetoothProfileState";
 
-    public static int HFP = 0;
-    public static int A2DP = 1;
+    public static final int HFP = 0;
+    public static final int A2DP = 1;
 
-    private static int TRANSITION_TO_STABLE = 100;
+    static final int TRANSITION_TO_STABLE = 100;
 
     private int mProfile;
     private BluetoothDevice mPendingDevice;
@@ -57,7 +57,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
                 int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
                 if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
@@ -70,6 +70,10 @@
                     newState == BluetoothA2dp.STATE_DISCONNECTED)) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
+            } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+                if (device.equals(mPendingDevice)) {
+                    sendMessage(TRANSITION_TO_STABLE);
+                }
             }
         }
     };
@@ -84,6 +88,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
         filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         context.registerReceiver(mBroadcastReceiver, filter);
     }
 
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 94f80cc..c877c5c 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -512,7 +512,14 @@
             authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
             if (authorized) {
                 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
-                mBluetoothService.notifyIncomingA2dpConnection(address);
+                // Some headsets try to connect AVCTP before AVDTP - against the recommendation
+                // If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
+                // machine.  We don't handle AVCTP signals currently. We only send
+                // intents for AVDTP state changes. We need to handle both of them in
+                // some cases. For now, just don't move to incoming state in this case.
+                if (!BluetoothUuid.isAvrcpTarget(uuid)) {
+                    mBluetoothService.notifyIncomingA2dpConnection(address);
+                }
             } else {
                 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
             }
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 71b4ee2..4d4d309 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -2227,6 +2227,16 @@
         mA2dpService = a2dpService;
     }
 
+    public void sendProfileStateMessage(int profile, int cmd) {
+        Message msg = new Message();
+        msg.what = cmd;
+        if (profile == BluetoothProfileState.HFP) {
+            mHfpProfileState.sendMessage(msg);
+        } else if (profile == BluetoothProfileState.A2DP) {
+            mA2dpProfileState.sendMessage(msg);
+        }
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }