Fix a race condition in BT device handling
When a headset or hearing aid disconnects in favor of the other, we have
a few milliseconds between when BT sends a "device disconnected" for the
first device and when it tells us that the second device is connected.
This causes us to go into a non-BT audio route for a while, but this
sends a "please disconnect audio" message to BT, which disrupts the
setup on the second device.
This change removes the "please disconnect audio" after a BT-initiated
disconnect, since it's redundant anyway.
Test: manual, unit
Bug: 122920967
Change-Id: I5f60b4b68c66c68a73d1fc0dd4e3122b35706d5e
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 69245da..dcf1b27 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -154,7 +154,7 @@
public static final int ACTIVE_FOCUS = 2;
public static final int RINGING_FOCUS = 3;
- /** Valid values for the argument for SWITCH_BASELINE_ROUTE */
+ /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */
public static final int NO_INCLUDE_BLUETOOTH_IN_BASELINE = 0;
public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1;
@@ -352,7 +352,6 @@
public void enter() {
super.enter();
setSpeakerphoneOn(false);
- setBluetoothOff();
CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
mAvailableRoutes, null,
mBluetoothRouteManager.getConnectedDevices());
@@ -547,7 +546,6 @@
public void enter() {
super.enter();
setSpeakerphoneOn(false);
- setBluetoothOff();
CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
setSystemAudioState(newState, true);
@@ -766,6 +764,32 @@
}
@Override
+ public void handleBtInitiatedDisconnect() {
+ // There's special-case state transitioning here -- if BT tells us that
+ // something got disconnected, we don't want to disconnect BT before
+ // transitioning, since BT might be trying to connect another device in the
+ // meantime.
+ int command = calculateBaselineRouteMessage(false, false);
+ switch (command) {
+ case SWITCH_EARPIECE:
+ transitionTo(mActiveEarpieceRoute);
+ break;
+ case SWITCH_HEADSET:
+ transitionTo(mActiveHeadsetRoute);
+ break;
+ case SWITCH_SPEAKER:
+ transitionTo(mActiveSpeakerRoute);
+ break;
+ default:
+ Log.w(this, "Got unexpected code " + command + " when processing a"
+ + " BT-initiated audio disconnect");
+ // Some fallback logic to make sure we make it off the bluetooth route.
+ super.handleBtInitiatedDisconnect();
+ break;
+ }
+ }
+
+ @Override
public boolean processMessage(Message msg) {
if (super.processMessage(msg) == HANDLED) {
return HANDLED;
@@ -776,6 +800,7 @@
// fall through
case SWITCH_EARPIECE:
if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+ setBluetoothOff();
transitionTo(mActiveEarpieceRoute);
} else {
Log.w(this, "Ignoring switch to earpiece command. Not available.");
@@ -799,6 +824,7 @@
// fall through
case SWITCH_HEADSET:
if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+ setBluetoothOff();
transitionTo(mActiveHeadsetRoute);
} else {
Log.w(this, "Ignoring switch to headset command. Not available.");
@@ -808,6 +834,7 @@
mHasUserExplicitlyLeftBluetooth = true;
// fall through
case SWITCH_SPEAKER:
+ setBluetoothOff();
transitionTo(mActiveSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
@@ -822,7 +849,7 @@
}
return HANDLED;
case BT_AUDIO_DISCONNECTED:
- sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
+ handleBtInitiatedDisconnect();
return HANDLED;
default:
return NOT_HANDLED;
@@ -1000,6 +1027,10 @@
return CallAudioState.ROUTE_BLUETOOTH;
}
+ public void handleBtInitiatedDisconnect() {
+ sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
+ }
+
@Override
public boolean processMessage(Message msg) {
if (super.processMessage(msg) == HANDLED) {
@@ -1014,7 +1045,7 @@
+ " have been null while we were in BT route.");
return HANDLED;
case BT_ACTIVE_DEVICE_GONE:
- sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
+ handleBtInitiatedDisconnect();
mWasOnSpeaker = false;
return HANDLED;
case DISCONNECT_WIRED_HEADSET:
@@ -1048,7 +1079,6 @@
super.enter();
mWasOnSpeaker = true;
setSpeakerphoneOn(true);
- setBluetoothOff();
CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
setSystemAudioState(newState, true);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 26d2419..e63fe9b 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -507,7 +507,7 @@
CallAudioState.ROUTE_BLUETOOTH, // initialRoute
CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
OPTIONAL, // speakerInteraction
- OFF, // bluetoothInteraction
+ NONE, // bluetoothInteraction
SPECIAL_DISCONNECT_BT_ACTION, // action
CallAudioState.ROUTE_EARPIECE, // expectedRoute
CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
@@ -519,7 +519,7 @@
CallAudioState.ROUTE_BLUETOOTH, // initialRoute
CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
OPTIONAL, // speakerInteraction
- OFF, // bluetoothInteraction
+ NONE, // bluetoothInteraction
SPECIAL_DISCONNECT_BT_ACTION, // action
CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
@@ -711,7 +711,7 @@
CallAudioState.ROUTE_BLUETOOTH, // initialRoute
CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
ON, // speakerInteraction
- OFF, // bluetoothInteraction
+ NONE, // bluetoothInteraction
SPECIAL_DISCONNECT_BT_ACTION, // action
CallAudioState.ROUTE_SPEAKER, // expectedRoute
CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
@@ -723,7 +723,7 @@
CallAudioState.ROUTE_BLUETOOTH, // initialRoute
CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
OPTIONAL, // speakerInteraction
- OFF, // bluetoothInteraction
+ NONE, // bluetoothInteraction
CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE, // action
CallAudioState.ROUTE_EARPIECE, // expectedRoute
CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl