Merge "Revise BT active device detection" am: 46d60dd323 am: 95639b3fc2
am: d97d321413
Change-Id: Ia9acd7a8cc740abf1ca5b47b3f5910a3b681b2c1
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index e53488c..df7896c 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -450,6 +450,7 @@
// Tracks the active devices in the BT stack (HFP or hearing aid).
private BluetoothDevice mHfpActiveDeviceCache = null;
private BluetoothDevice mHearingAidActiveDeviceCache = null;
+ private BluetoothDevice mMostRecentlyReportedActiveDevice = null;
public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
BluetoothDeviceManager deviceManager, Timeouts.Adapter timeoutsAdapter) {
@@ -588,6 +589,9 @@
} else {
mHfpActiveDeviceCache = device;
}
+
+ if (device != null) mMostRecentlyReportedActiveDevice = device;
+
boolean isActiveDevicePresent = mHearingAidActiveDeviceCache != null
|| mHfpActiveDeviceCache != null;
@@ -690,30 +694,38 @@
BluetoothHeadsetProxy bluetoothHeadset = mDeviceManager.getHeadsetService();
BluetoothHearingAid bluetoothHearingAid = mDeviceManager.getHearingAidService();
+ BluetoothDevice hfpActiveDevice = null;
+ BluetoothDevice hearingAidActiveDevice = null;
+
if (bluetoothHeadset == null && bluetoothHearingAid == null) {
Log.i(this, "getBluetoothAudioConnectedDevice: no service available.");
return null;
}
if (bluetoothHeadset != null) {
- for (BluetoothDevice device : bluetoothHeadset.getConnectedDevices()) {
- boolean isAudioOn = bluetoothHeadset.getAudioState(device)
- != BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
- Log.v(this, "isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn
- + "for headset: " + device);
- if (isAudioOn) {
- return device;
- }
- }
+ hfpActiveDevice = bluetoothHeadset.getActiveDevice();
}
+
if (bluetoothHearingAid != null) {
for (BluetoothDevice device : bluetoothHearingAid.getActiveDevices()) {
if (device != null) {
- return device;
+ hearingAidActiveDevice = device;
+ break;
}
}
}
- return null;
+
+ // Return the active device reported by either HFP or hearing aid. If both are reporting
+ // active devices, go with the most recent one as reported by the receiver.
+ if (hfpActiveDevice != null) {
+ if (hearingAidActiveDevice != null) {
+ Log.i(this, "Both HFP and hearing aid are reporting active devices. Going with"
+ + " the most recently reported active device: %s");
+ return mMostRecentlyReportedActiveDevice;
+ }
+ return hfpActiveDevice;
+ }
+ return hearingAidActiveDevice;
}
/**
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 0176a43..da2b8f1 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -136,7 +136,7 @@
boolean isHearingAid =
BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction());
Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device,
- isHearingAid ? "heading aid" : "HFP");
+ isHearingAid ? "hearing aid" : "HFP");
mBluetoothRouteManager.onActiveDeviceChanged(device, isHearingAid);
if (isHearingAid) {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 42626d9..060031d 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -38,7 +38,9 @@
import org.mockito.Mock;
import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -55,6 +57,7 @@
static final BluetoothDevice DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
static final BluetoothDevice DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
static final BluetoothDevice DEVICE3 = makeBluetoothDevice("00:00:00:00:00:03");
+ static final BluetoothDevice HEARING_AID_DEVICE = makeBluetoothDevice("00:00:00:00:00:04");
@Mock private BluetoothDeviceManager mDeviceManager;
@Mock private BluetoothHeadsetProxy mHeadsetProxy;
@@ -73,7 +76,7 @@
public void testConnectHfpRetryWhileNotConnected() {
BluetoothRouteManager sm = setupStateMachine(
BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
- setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null);
+ setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
nullable(ContentResolver.class))).thenReturn(0L);
when(mHeadsetProxy.connectAudio()).thenReturn(false);
@@ -92,10 +95,29 @@
@SmallTest
@Test
+ public void testAmbiguousActiveDevice() {
+ BluetoothRouteManager sm = setupStateMachine(
+ BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
+ setupConnectedDevices(new BluetoothDevice[]{DEVICE1},
+ new BluetoothDevice[]{HEARING_AID_DEVICE}, DEVICE1, HEARING_AID_DEVICE);
+ sm.onActiveDeviceChanged(DEVICE1, false);
+ sm.onActiveDeviceChanged(HEARING_AID_DEVICE, true);
+ executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST, DEVICE1.getAddress());
+
+ verifyConnectionAttempt(HEARING_AID_DEVICE, 0);
+ verifyConnectionAttempt(DEVICE1, 0);
+ assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+ + ":" + HEARING_AID_DEVICE.getAddress(),
+ sm.getCurrentState().getName());
+ sm.quitNow();
+ }
+
+ @SmallTest
+ @Test
public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
BluetoothRouteManager sm = setupStateMachine(
BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
- setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null);
+ setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null);
when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
nullable(ContentResolver.class))).thenReturn(0L);
when(mHeadsetProxy.connectAudio()).thenReturn(false);
@@ -127,18 +149,26 @@
return sm;
}
- private void setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice activeDevice) {
- when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
- when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
- when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
- when(mHeadsetProxy.getAudioState(any(BluetoothDevice.class)))
- .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
- when(mBluetoothHearingAid.getConnectedDevices()).thenReturn(Collections.emptyList());
- when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(null, null));
- if (activeDevice != null) {
- when(mHeadsetProxy.getAudioState(eq(activeDevice)))
- .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
- }
+ private void setupConnectedDevices(BluetoothDevice[] hfpDevices,
+ BluetoothDevice[] hearingAidDevices,
+ BluetoothDevice hfpActiveDevice, BluetoothDevice hearingAidActiveDevice) {
+ if (hfpDevices == null) hfpDevices = new BluetoothDevice[]{};
+ if (hearingAidDevices == null) hearingAidDevices = new BluetoothDevice[]{};
+
+ when(mDeviceManager.getNumConnectedDevices()).thenReturn(
+ hfpDevices.length + hearingAidDevices.length);
+ List<BluetoothDevice> allDevices = Stream.concat(
+ Arrays.stream(hfpDevices), Arrays.stream(hearingAidDevices))
+ .collect(Collectors.toList());
+
+ when(mDeviceManager.getConnectedDevices()).thenReturn(allDevices);
+ when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(hfpDevices));
+ when(mHeadsetProxy.getActiveDevice()).thenReturn(hfpActiveDevice);
+
+ when(mBluetoothHearingAid.getConnectedDevices())
+ .thenReturn(Arrays.asList(hearingAidDevices));
+ when(mBluetoothHearingAid.getActiveDevices())
+ .thenReturn(Arrays.asList(hearingAidActiveDevice, null));
}
static void executeRoutingAction(BluetoothRouteManager brm, int message, String
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index f87da3c..2f68ac2 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -265,9 +265,8 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = Log.createSubsession();
args.arg2 = mParams.initialDevice.getAddress();
+ when(mHeadsetProxy.getActiveDevice()).thenReturn(null);
sm.sendMessage(BluetoothRouteManager.BT_AUDIO_LOST, args);
- when(mHeadsetProxy.getAudioState(eq(mParams.initialDevice)))
- .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
return true;
}).when(mDeviceManager).disconnectAudio();
}
@@ -278,9 +277,14 @@
sm.onActiveDeviceChanged(mParams.messageDevice,
mParams.hearingAidBtDevices.contains(mParams.messageDevice));
} else if (mParams.messageType == BluetoothRouteManager.LOST_DEVICE) {
- sm.onDeviceLost(mParams.messageDevice.getAddress());
sm.onActiveDeviceChanged(null,
mParams.hearingAidBtDevices.contains(mParams.messageDevice));
+ if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) {
+ when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(null, null));
+ } else {
+ when(mHeadsetProxy.getActiveDevice()).thenReturn(null);
+ }
+ sm.onDeviceLost(mParams.messageDevice.getAddress());
} else {
executeRoutingAction(sm, mParams.messageType,
mParams.messageDevice == null ? null : mParams.messageDevice.getAddress());
@@ -335,11 +339,8 @@
when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
when(mHeadsetProxy.getActiveDevice()).thenReturn(activeDevice);
- when(mHeadsetProxy.getAudioState(any(BluetoothDevice.class)))
- .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
if (audioOnDevice != null) {
- when(mHeadsetProxy.getAudioState(eq(audioOnDevice)))
- .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
+ when(mHeadsetProxy.getActiveDevice()).thenReturn(audioOnDevice);
}
}