Merge "Support TYPE_BUS for call audio routing." into main
diff --git a/src/com/android/server/telecom/AudioRoute.java b/src/com/android/server/telecom/AudioRoute.java
index 7ce9fcc..4f563a1 100644
--- a/src/com/android/server/telecom/AudioRoute.java
+++ b/src/com/android/server/telecom/AudioRoute.java
@@ -117,6 +117,8 @@
public static final int TYPE_BLUETOOTH_HA = 6;
public static final int TYPE_BLUETOOTH_LE = 7;
public static final int TYPE_STREAMING = 8;
+ // Used by auto
+ public static final int TYPE_BUS = 9;
@IntDef(prefix = "TYPE", value = {
TYPE_INVALID,
TYPE_EARPIECE,
@@ -126,7 +128,8 @@
TYPE_BLUETOOTH_SCO,
TYPE_BLUETOOTH_HA,
TYPE_BLUETOOTH_LE,
- TYPE_STREAMING
+ TYPE_STREAMING,
+ TYPE_BUS
})
@Retention(RetentionPolicy.SOURCE)
public @interface AudioRouteType {}
@@ -155,35 +158,38 @@
DEVICE_TYPE_STRINGS.put(TYPE_WIRED, "TYPE_WIRED_HEADSET");
DEVICE_TYPE_STRINGS.put(TYPE_SPEAKER, "TYPE_SPEAKER");
DEVICE_TYPE_STRINGS.put(TYPE_DOCK, "TYPE_DOCK");
+ DEVICE_TYPE_STRINGS.put(TYPE_BUS, "TYPE_BUS");
DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO");
DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_HA, "TYPE_BLUETOOTH_HA");
DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_LE, "TYPE_BLUETOOTH_LE");
DEVICE_TYPE_STRINGS.put(TYPE_STREAMING, "TYPE_STREAMING");
}
- public static final HashMap<Integer, Integer> DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE;
+ public static final HashMap<Integer, Integer> DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE;
static {
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE = new HashMap<>();
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE = new HashMap<>();
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
TYPE_EARPIECE);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, TYPE_SPEAKER);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ TYPE_SPEAKER);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
TYPE_BLUETOOTH_SCO);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID,
TYPE_BLUETOOTH_HA);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET,
TYPE_BLUETOOTH_LE);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER,
TYPE_BLUETOOTH_LE);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST,
TYPE_BLUETOOTH_LE);
- DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK);
+ DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUS, TYPE_BUS);
}
private static final HashMap<Integer, List<Integer>> AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE;
@@ -210,6 +216,10 @@
dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK_ANALOG);
AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_DOCK, dockDeviceInfoTypes);
+ List<Integer> busDeviceInfoTypes = new ArrayList<>();
+ busDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUS);
+ AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BUS, busDeviceInfoTypes);
+
List<Integer> bluetoothScoDeviceInfoTypes = new ArrayList<>();
bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP);
bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
@@ -241,6 +251,10 @@
BluetoothRouteManager bluetoothRouteManager, boolean isScoAudioConnected) {
Log.i(this, "onDestRouteAsPendingRoute: active (%b), type (%s)", active,
DEVICE_TYPE_STRINGS.get(mAudioRouteType));
+ if (mAudioRouteType == TYPE_BUS) {
+ Log.i(this, "onDestRouteAsPendingRoute: Ignore processing dest route for TYPE_BUS");
+ return;
+ }
if (pendingAudioRoute.isActive() && !active) {
clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager);
} else if (active) {
@@ -302,6 +316,10 @@
void onOrigRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute,
AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager) {
Log.i(this, "onOrigRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType);
+ if (mAudioRouteType == TYPE_BUS) {
+ Log.i(this, "onOrigRouteAsPendingRoute: Ignore processing dest route for TYPE_BUS");
+ return;
+ }
if (active) {
if (mAudioRouteType == TYPE_SPEAKER) {
pendingAudioRoute.addMessage(SPEAKER_OFF, null);
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 3dcd58e..0ed4308 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -63,7 +63,6 @@
import java.util.Set;
public class CallAudioRouteController implements CallAudioRouteAdapter {
- private static final long TIMEOUT_LIMIT = 2000L;
private static final AudioRoute DUMMY_ROUTE = new AudioRoute(TYPE_INVALID, null, null);
private static final Map<Integer, Integer> ROUTE_MAP;
static {
@@ -73,6 +72,7 @@
ROUTE_MAP.put(AudioRoute.TYPE_WIRED, CallAudioState.ROUTE_WIRED_HEADSET);
ROUTE_MAP.put(AudioRoute.TYPE_SPEAKER, CallAudioState.ROUTE_SPEAKER);
ROUTE_MAP.put(AudioRoute.TYPE_DOCK, CallAudioState.ROUTE_SPEAKER);
+ ROUTE_MAP.put(AudioRoute.TYPE_BUS, CallAudioState.ROUTE_SPEAKER);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_SCO, CallAudioState.ROUTE_BLUETOOTH);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_HA, CallAudioState.ROUTE_BLUETOOTH);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_LE, CallAudioState.ROUTE_BLUETOOTH);
@@ -353,14 +353,22 @@
int supportMask = calculateSupportedRouteMaskInit();
if ((supportMask & CallAudioState.ROUTE_SPEAKER) != 0) {
+ int audioRouteType = AudioRoute.TYPE_SPEAKER;
// Create speaker routes
mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_SPEAKER, null,
mAudioManager);
- if (mSpeakerDockRoute == null) {
- Log.w(this, "Can't find available audio device info for route TYPE_SPEAKER");
- } else {
- mTypeRoutes.put(AudioRoute.TYPE_SPEAKER, mSpeakerDockRoute);
+ // Todo(b/364562758): Represent TYPE_BUS as CallAudioState.ROUTE_SPEAKER (moving
+ // forward, we may rework this if audio fwk team decides to allow list this as a
+ // valid communication device.
+ if (mSpeakerDockRoute != null || hasBusAudioDevice()) {
+ if (mSpeakerDockRoute == null){
+ mSpeakerDockRoute = new AudioRoute(AudioRoute.TYPE_BUS, null, null);
+ audioRouteType = AudioRoute.TYPE_BUS;
+ }
+ mTypeRoutes.put(audioRouteType, mSpeakerDockRoute);
updateAvailableRoutes(mSpeakerDockRoute, true);
+ } else {
+ Log.w(this, "Can't find available audio device info for route TYPE_SPEAKER");
}
}
@@ -905,7 +913,8 @@
}
private void handleSwitchSpeaker() {
- if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)) {
+ if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)
+ && mSpeakerDockRoute.getType() == AudioRoute.TYPE_SPEAKER) {
routeTo(mIsActive, mSpeakerDockRoute);
} else {
Log.i(this, "ignore switch speaker request");
@@ -923,8 +932,8 @@
// Update status bar notification if we are in a call.
mStatusBarNotifier.notifySpeakerphone(mCallsManager.hasAnyCalls());
} else {
- if (mSpeakerDockRoute != null && getCallSupportedRoutes()
- .contains(mSpeakerDockRoute)) {
+ if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)
+ && mSpeakerDockRoute.getType() == AudioRoute.TYPE_SPEAKER) {
routeTo(mIsActive, mSpeakerDockRoute);
// Since the route switching triggered by this message, we need to manually send it
// again so that we won't stuck in the pending route
@@ -1084,6 +1093,24 @@
}
private AudioRoute getPreferredAudioRouteFromStrategy() {
+ // Get preferred device
+ AudioDeviceAttributes deviceAttr = getPreferredDeviceForStrategy();
+ Log.i(this, "getPreferredAudioRouteFromStrategy: preferred device is %s", deviceAttr);
+ if (deviceAttr == null) {
+ return null;
+ }
+
+ // Get corresponding audio route
+ @AudioRoute.AudioRouteType int type = AudioRoute.DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.get(
+ deviceAttr.getType());
+ if (BT_AUDIO_ROUTE_TYPES.contains(type)) {
+ return getBluetoothRoute(type, deviceAttr.getAddress());
+ } else {
+ return mTypeRoutes.get(type);
+ }
+ }
+
+ private AudioDeviceAttributes getPreferredDeviceForStrategy() {
// Get audio produce strategy
AudioProductStrategy strategy = null;
final AudioAttributes attr = new AudioAttributes.Builder()
@@ -1099,21 +1126,26 @@
return null;
}
- // Get preferred device
- AudioDeviceAttributes deviceAttr = mAudioManager.getPreferredDeviceForStrategy(strategy);
- Log.i(this, "getPreferredAudioRouteFromStrategy: preferred device is %s", deviceAttr);
- if (deviceAttr == null) {
- return null;
- }
+ return mAudioManager.getPreferredDeviceForStrategy(strategy);
+ }
- // Get corresponding audio route
- @AudioRoute.AudioRouteType int type = AudioRoute.DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.get(
- deviceAttr.getType());
- if (BT_AUDIO_ROUTE_TYPES.contains(type)) {
- return getBluetoothRoute(type, deviceAttr.getAddress());
- } else {
- return mTypeRoutes.get(type);
+ /**
+ * For auto, there is no earpiece or speakerphone routes available. The audio is routed to the
+ * bus but because this isn't a valid communication device,
+ * {@link AudioManager#getCommunicationDevice()} will not provide this device even if audio fwk
+ * reports it as the active communication device (refer to
+ * AudioDeviceBroker#getCommunicationDeviceInt()}. Check if the device is the preferred device
+ * for strategy instead.
+ */
+ private boolean hasBusAudioDevice() {
+ AudioDeviceAttributes deviceAttr = getPreferredDeviceForStrategy();
+ if (deviceAttr == null) {
+ return false;
}
+ // Get corresponding audio route mapping
+ @AudioRoute.AudioRouteType int type = AudioRoute.DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.get(
+ deviceAttr.getType());
+ return type == AudioRoute.TYPE_BUS;
}
private AudioRoute getPreferredAudioRouteFromDefault(boolean includeBluetooth,
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
index 72e2111..1254b8a 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
@@ -43,6 +43,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
@@ -63,7 +64,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
-import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.audiopolicy.AudioProductStrategy;
@@ -86,7 +86,6 @@
import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
-import dalvik.annotation.TestTarget;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -119,6 +118,7 @@
@Mock private TelecomSystem.SyncRoot mLock;
private AudioRoute mEarpieceRoute;
private AudioRoute mSpeakerRoute;
+ private boolean mOverrideSpeakerToBus;
private static final String BT_ADDRESS_1 = "00:00:00:00:00:01";
private static final BluetoothDevice BLUETOOTH_DEVICE_1 =
BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:01");
@@ -132,6 +132,9 @@
@Override
public AudioRoute create(@AudioRoute.AudioRouteType int type, String bluetoothAddress,
AudioManager audioManager) {
+ if (mOverrideSpeakerToBus && type == AudioRoute.TYPE_SPEAKER) {
+ type = AudioRoute.TYPE_BUS;
+ }
return new AudioRoute(type, bluetoothAddress, mAudioDeviceInfo);
}
};
@@ -177,6 +180,7 @@
mController.setAudioManager(mAudioManager);
mEarpieceRoute = new AudioRoute(AudioRoute.TYPE_EARPIECE, null, null);
mSpeakerRoute = new AudioRoute(AudioRoute.TYPE_SPEAKER, null, null);
+ mOverrideSpeakerToBus = false;
mController.setCallAudioManager(mCallAudioManager);
when(mCallAudioManager.getForegroundCall()).thenReturn(mCall);
when(mCall.getVideoState()).thenReturn(VideoProfile.STATE_AUDIO_ONLY);
@@ -877,6 +881,31 @@
assertEquals(1, mController.getCallSupportedRoutes().size());
}
+ @SmallTest
+ @Test
+ public void testRouteToBusForAuto() {
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
+ .thenReturn(new AudioDeviceInfo[0]);
+ mOverrideSpeakerToBus = true;
+ mController.initialize();
+
+ mController.sendMessageWithSessionInfo(SWITCH_FOCUS, ACTIVE_FOCUS, 0);
+ waitForHandlerAction(mController.getAdapterHandler(), TEST_TIMEOUT);
+ PendingAudioRoute pendingRoute = mController.getPendingAudioRoute();
+ assertEquals(AudioRoute.TYPE_BUS, pendingRoute.getDestRoute().getType());
+
+ CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+ CallAudioState.ROUTE_SPEAKER, null, new HashSet<>());
+ verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ any(CallAudioState.class), eq(expectedState));
+
+ // Ensure that turning speaker phone on doesn't get triggered when speaker isn't available.
+ mController.sendMessageWithSessionInfo(USER_SWITCH_SPEAKER);
+ mController.sendMessageWithSessionInfo(SPEAKER_ON);
+ verify(mockStatusBarNotifier, times(0)).notifySpeakerphone(anyBoolean());
+
+ }
+
private void verifyConnectBluetoothDevice(int audioType) {
mController.initialize();
mController.setActive(true);