Merge "CallAudioCommunicationDevice: treat TYPE_BUS as TYPE_BUILTIN_SPEAKER" into main
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index b63385f..ae146f8 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -173,3 +173,14 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+# OWNER=tgunn TARGET=25Q2
+flag {
+  name: "bus_device_is_a_speaker"
+  namespace: "telecom"
+  description: "Treat TYPE_BUS devices like TYPE_SPEAKER"
+  bug: "395647782"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
index 84f7d8f..7bd4dca 100644
--- a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
+++ b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
@@ -144,7 +144,8 @@
         boolean handleLeAudioDeviceSwitch = btDevice != null
                 && !btDevice.getAddress().equals(mBtAudioDevice);
         if ((audioDeviceType == mAudioDeviceType
-                || isUsbHeadsetType(audioDeviceType, mAudioDeviceType))
+                || isUsbHeadsetType(audioDeviceType, mAudioDeviceType)
+                || isSpeakerType(audioDeviceType, mAudioDeviceType))
                 && !handleLeAudioDeviceSwitch) {
             Log.i(this, "Communication device is already set for this audio type");
             return false;
@@ -161,7 +162,8 @@
             Log.i(this, "Available device type: " + device.getType());
             // Ensure that we do not select the same BT LE audio device for communication.
             if ((audioDeviceType == device.getType()
-                    || isUsbHeadsetType(audioDeviceType, device.getType()))
+                    || isUsbHeadsetType(audioDeviceType, device.getType())
+                    || isSpeakerType(audioDeviceType, device.getType()))
                     && !device.getAddress().equals(mBtAudioDevice)) {
                 activeDevice = device;
                 break;
@@ -234,13 +236,15 @@
                 audioDeviceType, isBtDevice);
 
         if (audioDeviceType != mAudioDeviceType
-                && !isUsbHeadsetType(audioDeviceType, mAudioDeviceType)) {
-            Log.i(this, "Unable to clear communication device of type(s), %s. "
-                            + "Device does not correspond to the locally requested device type.",
+                && !isUsbHeadsetType(audioDeviceType, mAudioDeviceType)
+                && !isSpeakerType(audioDeviceType, mAudioDeviceType)) {
+            Log.i(this, "Unable to clear communication device of type(s) %s. "
+                            + "Device does not correspond to the locally requested device type %s.",
                     audioDeviceType == AudioDeviceInfo.TYPE_WIRED_HEADSET
                             ? Arrays.asList(AudioDeviceInfo.TYPE_WIRED_HEADSET,
                             AudioDeviceInfo.TYPE_USB_HEADSET)
-                            : audioDeviceType
+                            : audioDeviceType,
+                    mAudioDeviceType
             );
             return;
         }
@@ -267,4 +271,11 @@
         return audioDeviceType == AudioDeviceInfo.TYPE_WIRED_HEADSET
                 && sourceType == AudioDeviceInfo.TYPE_USB_HEADSET;
     }
+
+    private boolean isSpeakerType(@AudioDeviceInfo.AudioDeviceType int audioDeviceType,
+        @AudioDeviceInfo.AudioDeviceType int sourceType) {
+        if (!Flags.busDeviceIsASpeaker()) return false;
+        return audioDeviceType == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
+                && sourceType == AudioDeviceInfo.TYPE_BUS;
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index c403c50..1b7b0ef 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -695,7 +695,10 @@
                 ArgumentCaptor.forClass(AudioDeviceInfo.class);
         verify(audioManager, timeout(TEST_TIMEOUT).atLeast(1))
                 .setCommunicationDevice(infoArgumentCaptor.capture());
-        assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, infoArgumentCaptor.getValue().getType());
+        var deviceType = infoArgumentCaptor.getValue().getType();
+        if (deviceType != AudioDeviceInfo.TYPE_BUS) { // on automotive, we expect BUS
+            assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, deviceType);
+        }
         mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE, null);
         waitForHandlerAction(mTelecomSystem.getCallsManager().getCallAudioManager()
                 .getCallAudioRouteAdapter().getAdapterHandler(), TEST_TIMEOUT);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index e97de2e..ac90fd1 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -113,7 +113,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        MockitoAnnotations.initMocks(this);
         mThreadHandler = new HandlerThread("CallAudioRouteStateMachineTest");
         mThreadHandler.start();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -144,7 +143,6 @@
         doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
                 any(CallAudioState.class));
         when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(false);
-        when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(false);
     }
 
     @Override
@@ -838,7 +836,10 @@
         ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass(
                 AudioDeviceInfo.class);
         verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture());
-        assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, infoArgumentCaptor.getValue().getType());
+        var deviceType = infoArgumentCaptor.getValue().getType();
+        if (deviceType != AudioDeviceInfo.TYPE_BUS) { // on automotive, we expect BUS
+            assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, deviceType);
+        }
     }
 
     @SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 6b9b5c8..c288d17 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -172,7 +172,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        MockitoAnnotations.initMocks(this);
         mHandlerThread = new HandlerThread("CallAudioRouteTransitionTests");
         mHandlerThread.start();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -344,8 +343,10 @@
                 ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass(
                         AudioDeviceInfo.class);
                 verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture());
-                assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
-                        infoArgumentCaptor.getValue().getType());
+                var deviceType = infoArgumentCaptor.getValue().getType();
+                if (deviceType != AudioDeviceInfo.TYPE_BUS) { // on automotive, we expect BUS
+                    assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, deviceType);
+                }
                 break;
             case OFF:
                 verify(mockAudioManager).clearCommunicationDevice();
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 0b4cb2d..4aceae4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -523,6 +523,7 @@
         when(mRoleManagerAdapter.getDefaultCallScreeningApp(any(UserHandle.class)))
                 .thenReturn(null);
         when(mRoleManagerAdapter.getBTInCallService()).thenReturn(new String[] {"bt_pkg"});
+        when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true);
         when(mFeatureFlags.useRefactoredAudioRouteSwitching()).thenReturn(false);
         mTelecomSystem = new TelecomSystem(
                 mComponentContextFixture.getTestDouble(),
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index bfe18f6..0b1c17c 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -49,6 +49,7 @@
         mMockitoHelper.setUp(InstrumentationRegistry.getContext(), getClass());
         MockitoAnnotations.initMocks(this);
 
+        Mockito.when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true);
         mComponentContextFixture = new ComponentContextFixture(mFeatureFlags);
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         Log.setSessionManager(mComponentContextFixture.getTestDouble().getApplicationContext(),