From 70a053bf1ba331d727e2fbfca8f39d96b3b324b4 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 13 Oct 2010 15:54:30 -0700 Subject: [PATCH] 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 --- .../BluetoothDeviceProfileState.java | 29 ++++++++++++++++--- .../bluetooth/BluetoothProfileState.java | 13 ++++++--- .../android/server/BluetoothEventLoop.java | 9 +++++- .../java/android/server/BluetoothService.java | 10 +++++++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index ff82d39fc49f5..17b28d1a809d6 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -136,6 +136,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine 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 946dcaa01c867..dc0b32b7567ff 100644 --- a/core/java/android/bluetooth/BluetoothProfileState.java +++ b/core/java/android/bluetooth/BluetoothProfileState.java @@ -43,10 +43,10 @@ public class BluetoothProfileState extends HierarchicalStateMachine { 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 @@ public class BluetoothProfileState extends HierarchicalStateMachine { @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 @@ public class BluetoothProfileState extends HierarchicalStateMachine { 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 @@ public class BluetoothProfileState extends HierarchicalStateMachine { 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 94f80cce6d9bf..c877c5c84b534 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -512,7 +512,14 @@ class BluetoothEventLoop { 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 71b4ee244425a..4d4d30904932e 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -2227,6 +2227,16 @@ public class BluetoothService extends IBluetooth.Stub { 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); }