Resolve user switch baseline route for video calls
Ensure that when there's an explicit user request to switch baseline
route that we don't skip earpiece (i.e. during a video call). This stems
from the user not being able to turn speaker off during a carrier video
call.
Bug: 374037591
Flag: com.android.server.telecom.flags.fix_user_request_baseline_route_video_call
Test: atest CallAudioRouteControllerTest
Change-Id: Iae9d1ae7720f0ddde565a05bdd2d5b341b9f3dd7
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index 3dd5c9d..a60c0f1 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -118,3 +118,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+# OWNER=pmadapurmath TARGET=25Q2
+flag {
+ name: "fix_user_request_baseline_route_video_call"
+ namespace: "telecom"
+ description: "Ensure that audio is routed out of speaker in a video call when we receive USER_SWITCH_BASELINE_ROUTE."
+ bug: "374037591"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 5c5f3f0..04229b6 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -310,12 +310,12 @@
break;
case SWITCH_BASELINE_ROUTE:
address = (String) ((SomeArgs) msg.obj).arg2;
- handleSwitchBaselineRoute(msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE,
- address);
+ handleSwitchBaselineRoute(false,
+ msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE, address);
break;
case USER_SWITCH_BASELINE_ROUTE:
- handleSwitchBaselineRoute(msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE,
- null);
+ handleSwitchBaselineRoute(true,
+ msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE, null);
break;
case SPEAKER_ON:
handleSpeakerOn();
@@ -888,7 +888,7 @@
// speaker, route back to earpiece). If we're on BT, remain on BT if it's still
// connected.
AudioRoute route = mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()
- ? calculateBaselineRoute(true, null)
+ ? calculateBaselineRoute(false, true, null)
: mCurrentRoute;
routeTo(false, route);
// Clear pending messages
@@ -1009,7 +1009,8 @@
}
}
- private void handleSwitchBaselineRoute(boolean includeBluetooth, String btAddressToExclude) {
+ private void handleSwitchBaselineRoute(boolean isExplicitUserRequest, boolean includeBluetooth,
+ String btAddressToExclude) {
Log.i(this, "handleSwitchBaselineRoute: includeBluetooth: %b, "
+ "btAddressToExclude: %s", includeBluetooth, btAddressToExclude);
boolean areExcludedBtAndDestBtSame = btAddressToExclude != null
@@ -1027,7 +1028,8 @@
Log.i(this, "BT device with address (%s) is currently connecting/connected. "
+ "Ignore route switch.");
} else {
- routeTo(mIsActive, calculateBaselineRoute(includeBluetooth, btAddressToExclude));
+ routeTo(mIsActive, calculateBaselineRoute(isExplicitUserRequest, includeBluetooth,
+ btAddressToExclude));
}
}
@@ -1238,13 +1240,18 @@
return mAudioManager.getPreferredDeviceForStrategy(strategy);
}
- private AudioRoute getPreferredAudioRouteFromDefault(boolean includeBluetooth,
- String btAddressToExclude) {
- boolean skipEarpiece;
+ private AudioRoute getPreferredAudioRouteFromDefault(boolean isExplicitUserRequest,
+ boolean includeBluetooth, String btAddressToExclude) {
+ boolean skipEarpiece = false;
Call foregroundCall = mCallAudioManager.getForegroundCall();
- synchronized (mTelecomLock) {
- skipEarpiece = foregroundCall != null
- && VideoProfile.isVideo(foregroundCall.getVideoState());
+ if (!mFeatureFlags.fixUserRequestBaselineRouteVideoCall()) {
+ isExplicitUserRequest = false;
+ }
+ if (!isExplicitUserRequest) {
+ synchronized (mTelecomLock) {
+ skipEarpiece = foregroundCall != null
+ && VideoProfile.isVideo(foregroundCall.getVideoState());
+ }
}
// Route to earpiece, wired, or speaker route if there are not bluetooth routes or if there
// are only wearables available.
@@ -1344,7 +1351,7 @@
Log.i(this, "getBaseRoute: preferred audio route is %s", destRoute);
if (destRoute == null || (destRoute.getBluetoothAddress() != null && (!includeBluetooth
|| destRoute.getBluetoothAddress().equals(btAddressToExclude)))) {
- destRoute = getPreferredAudioRouteFromDefault(includeBluetooth, btAddressToExclude);
+ destRoute = getPreferredAudioRouteFromDefault(false, includeBluetooth, btAddressToExclude);
}
if (destRoute != null && !getCallSupportedRoutes().contains(destRoute)) {
destRoute = null;
@@ -1353,8 +1360,9 @@
return destRoute;
}
- private AudioRoute calculateBaselineRoute(boolean includeBluetooth, String btAddressToExclude) {
- AudioRoute destRoute = getPreferredAudioRouteFromDefault(
+ private AudioRoute calculateBaselineRoute(boolean isExplicitUserRequest,
+ boolean includeBluetooth, String btAddressToExclude) {
+ AudioRoute destRoute = getPreferredAudioRouteFromDefault(isExplicitUserRequest,
includeBluetooth, btAddressToExclude);
if (destRoute != null && !getCallSupportedRoutes().contains(destRoute)) {
destRoute = null;
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index bb1a745..7667ebc 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -270,12 +270,16 @@
if (!mBluetoothDeviceManager.setCommunicationDeviceForAddress(
device.getAddress())) {
Log.i(this, "handleActiveDeviceChanged: Failed to set "
- + "communication device for %s. Sending PENDING_ROUTE_FAILED to "
- + "pending audio route.", device);
+ + "communication device for %s.", device);
if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
+ Log.i(this, "Sending PENDING_ROUTE_FAILED "
+ + "to pending audio route.");
mCallAudioRouteAdapter.getPendingAudioRoute()
.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED,
device.getAddress()), device.getAddress());
+ } else {
+ Log.i(this, "Refrain from sending PENDING_ROUTE_FAILED"
+ + " to pending audio route.");
}
} else {
// Track the currently set communication device.
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
index 528585b..330e84c 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
@@ -37,6 +37,7 @@
import static com.android.server.telecom.CallAudioRouteAdapter.STREAMING_FORCE_ENABLED;
import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_ROUTE;
import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_FOCUS;
+import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_BASELINE_ROUTE;
import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_BLUETOOTH;
import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_EARPIECE;
import static com.android.server.telecom.CallAudioRouteAdapter.USER_SWITCH_HEADSET;
@@ -194,6 +195,7 @@
when(mFeatureFlags.useRefactoredAudioRouteSwitching()).thenReturn(true);
when(mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()).thenReturn(false);
when(mFeatureFlags.newAudioPathSpeakerBroadcastAndUnfocusedRouting()).thenReturn(false);
+ when(mFeatureFlags.fixUserRequestBaselineRouteVideoCall()).thenReturn(false);
}
@After
@@ -1058,6 +1060,45 @@
any(CallAudioState.class), eq(expectedState));
}
+ @Test
+ @SmallTest
+ public void testUserSwitchBaselineRouteVideoCall() {
+ when(mFeatureFlags.fixUserRequestBaselineRouteVideoCall()).thenReturn(true);
+ mController.initialize();
+ mController.setActive(true);
+ // Set capabilities for video call.
+ when(mCall.getVideoState()).thenReturn(VideoProfile.STATE_BIDIRECTIONAL);
+
+ // Turn on speaker
+ mController.sendMessageWithSessionInfo(SPEAKER_ON);
+ CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, null,
+ new HashSet<>());
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // USER_SWITCH_BASELINE_ROUTE (explicit user request). Verify that audio is routed back to
+ // earpiece.
+ mController.sendMessageWithSessionInfo(USER_SWITCH_BASELINE_ROUTE,
+ CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE);
+ mController.sendMessageWithSessionInfo(SPEAKER_OFF);
+ expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, null,
+ new HashSet<>());
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // SWITCH_BASELINE_ROUTE. Verify that audio is routed to speaker for non-user requests.
+ mController.sendMessageWithSessionInfo(SWITCH_BASELINE_ROUTE,
+ CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE);
+ mController.sendMessageWithSessionInfo(SPEAKER_ON);
+ expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, null,
+ new HashSet<>());
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+ }
+
private void verifyConnectBluetoothDevice(int audioType) {
mController.initialize();
mController.setActive(true);