Merge "Resolve READ_PRIVILEGED_PHONE_STATE bypass" into main
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 98adae9..727d300 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -800,7 +800,6 @@
if (bluetoothRoute != null) {
Log.i(this, "request to route to bluetooth route: %s (active=%b)", bluetoothRoute,
mIsActive);
- updateActiveBluetoothDevice(new Pair<>(type, deviceAddress));
routeTo(mIsActive, bluetoothRoute);
} else {
Log.i(this, "request to route to unavailable bluetooth route - type (%s), address (%s)",
@@ -844,10 +843,6 @@
// Fallback to an available route excluding the previously active device.
routeTo(mIsActive, getBaseRoute(true, previouslyActiveDeviceAddress));
}
- // Clear out the active device for the BT audio type.
- if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
- updateActiveBluetoothDevice(new Pair(type, null));
- }
}
private void handleMuteChanged(boolean mute) {
@@ -1023,15 +1018,28 @@
// If SCO is once again connected or there's a pending message for BT_AUDIO_CONNECTED, then
// we know that the device has reconnected or is in the middle of connecting. Ignore routing
// out of this BT device.
- if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue() && areExcludedBtAndDestBtSame
+ boolean isExcludedDeviceConnectingOrConnected = areExcludedBtAndDestBtSame
&& (mIsScoAudioConnected || mPendingAudioRoute.getPendingMessages()
- .contains(btDevicePendingMsg))) {
- Log.i(this, "BT device with address (%s) is currently connecting/connected. "
- + "Ignore route switch.");
- } else {
- routeTo(mIsActive, calculateBaselineRoute(isExplicitUserRequest, includeBluetooth,
- btAddressToExclude));
+ .contains(btDevicePendingMsg));
+ // Check if the pending audio route or current route is already different from the route
+ // including the BT device that should be excluded from route selection.
+ boolean isCurrentOrDestRouteDifferent = btAddressToExclude != null
+ && ((mIsPending && !btAddressToExclude.equals(mPendingAudioRoute.getDestRoute()
+ .getBluetoothAddress())) || (!mIsPending && !btAddressToExclude.equals(
+ mCurrentRoute.getBluetoothAddress())));
+ if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
+ if (isExcludedDeviceConnectingOrConnected) {
+ Log.i(this, "BT device with address (%s) is currently connecting/connected. "
+ + "Ignoring route switch.", btAddressToExclude);
+ return;
+ } else if (isCurrentOrDestRouteDifferent) {
+ Log.i(this, "Current or pending audio route isn't routed to device with address "
+ + "(%s). Ignoring route switch.", btAddressToExclude);
+ return;
+ }
}
+ routeTo(mIsActive, calculateBaselineRoute(isExplicitUserRequest, includeBluetooth,
+ btAddressToExclude));
}
private void handleSpeakerOn() {
@@ -1441,8 +1449,11 @@
continue;
}
// Check if the most recently active device is a watch device.
- if (i == (bluetoothRoutes.size() - 1) && device.equals(mCallAudioState
- .getActiveBluetoothDevice()) && mBluetoothRouteManager.isWatch(device)) {
+ boolean isActiveDevice = mActiveBluetoothDevice != null
+ && device.getAddress().equals(mActiveBluetoothDevice.second);
+ if (i == (bluetoothRoutes.size() - 1) && mBluetoothRouteManager.isWatch(device)
+ && (device.equals(mCallAudioState.getActiveBluetoothDevice())
+ || isActiveDevice)) {
Log.i(this, "getActiveWatchOrNonWatchDeviceRoute: Routing to active watch - %s",
bluetoothRoutes.get(0));
return bluetoothRoutes.get(0);
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 7667ebc..7fe8246 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -252,17 +252,14 @@
CallAudioRouteController audioRouteController = (CallAudioRouteController)
mCallAudioRouteAdapter;
if (device == null) {
- if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
- audioRouteController.updateActiveBluetoothDevice(
- new Pair(audioRouteType, null));
- }
+ // Update the active device cache immediately.
+ audioRouteController.updateActiveBluetoothDevice(new Pair(audioRouteType, null));
mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_GONE,
audioRouteType);
} else {
- if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
- audioRouteController.updateActiveBluetoothDevice(
- new Pair(audioRouteType, device.getAddress()));
- }
+ // Update the active device cache immediately.
+ audioRouteController.updateActiveBluetoothDevice(
+ new Pair(audioRouteType, device.getAddress()));
mCallAudioRouteAdapter.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
audioRouteType, device.getAddress());
if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
index ba48c64..809abb4 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
@@ -74,6 +74,7 @@
import android.os.UserHandle;
import android.telecom.CallAudioState;
import android.telecom.VideoProfile;
+import android.util.Pair;
import androidx.test.filters.SmallTest;
@@ -1101,6 +1102,63 @@
@Test
@SmallTest
+ public void testRouteToWatchWhenCallAnsweredOnWatch_MultipleBtDevices() {
+ when(mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()).thenReturn(true);
+ // Connect first BT device.
+ verifyConnectBluetoothDevice(AudioRoute.TYPE_BLUETOOTH_SCO);
+ // Connect another BT device.
+ String scoDeviceAddress = "00:00:00:00:00:03";
+ BluetoothDevice watchDevice =
+ BluetoothRouteManagerTest.makeBluetoothDevice(scoDeviceAddress);
+ when(mBluetoothRouteManager.isWatch(eq(watchDevice))).thenReturn(true);
+ BLUETOOTH_DEVICES.add(watchDevice);
+
+ mController.sendMessageWithSessionInfo(BT_DEVICE_ADDED, AudioRoute.TYPE_BLUETOOTH_SCO,
+ watchDevice);
+ CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
+ | CallAudioState.ROUTE_BLUETOOTH, BLUETOOTH_DEVICE_1, BLUETOOTH_DEVICES);
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // Signal that watch is now the active device. This is done in BluetoothStateReceiver and
+ // then BT_ACTIVE_DEVICE_PRESENT will be sent to the controller to be processed.
+ mController.updateActiveBluetoothDevice(
+ new Pair<>(AudioRoute.TYPE_BLUETOOTH_SCO, watchDevice.getAddress()));
+ // Emulate scenario with call answered on watch. Ensure at this point that audio was routed
+ // into watch
+ mController.sendMessageWithSessionInfo(SWITCH_FOCUS, ACTIVE_FOCUS, 0);
+ mController.sendMessageWithSessionInfo(BT_AUDIO_CONNECTED,
+ 0, watchDevice);
+ mController.sendMessageWithSessionInfo(BT_AUDIO_DISCONNECTED,
+ 0, BLUETOOTH_DEVICE_1);
+ expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
+ | CallAudioState.ROUTE_BLUETOOTH, watchDevice, BLUETOOTH_DEVICES);
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // Hardcode signal from BT stack signaling to Telecom that watch is now the active device.
+ // This should just be a no-op since audio was already routed when processing active focus.
+ mController.sendMessageWithSessionInfo(BT_ACTIVE_DEVICE_PRESENT,
+ AudioRoute.TYPE_BLUETOOTH_SCO, scoDeviceAddress);
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // Mimic behavior of controller processing BT_AUDIO_DISCONNECTED for BLUETOOTH_DEVICE_1 and
+ // verify that audio remains routed to the watch and not routed to earpiece (this should
+ // be taking into account what the BT active device is as reported to us by the BT stack).
+ mController.sendMessageWithSessionInfo(SWITCH_BASELINE_ROUTE,
+ INCLUDE_BLUETOOTH_IN_BASELINE, BLUETOOTH_DEVICE_1.getAddress());
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ BLUETOOTH_DEVICES.remove(watchDevice);
+ }
+
+
+ @Test
+ @SmallTest
public void testAbandonCallAudioFocusAfterCallEnd() {
// Make sure in-band ringing is disabled so that route never becomes active
when(mBluetoothRouteManager.isInbandRingEnabled(eq(BLUETOOTH_DEVICE_1))).thenReturn(false);