Spatializer: Fix usb headset
Fixes the general transaural and binaural equivalence of different devices. Prior to this fix only speaker and true 3.5mm wired headphones could be enabled. Now all SADevices will have a canonical device type. Test: Plug in USB headset, verify spatializer works. Bug: 239081163 Change-Id: I904a299556f1cccb9d8d69a23245e387f7a5d35e
This commit is contained in:
@@ -46,6 +46,8 @@ import android.util.Log;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -103,10 +105,6 @@ public class SpatializerHelper {
|
|||||||
AudioDeviceInfo.TYPE_BLE_BROADCAST
|
AudioDeviceInfo.TYPE_BLE_BROADCAST
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int[] WIRELESS_SPEAKER_TYPES = {
|
|
||||||
AudioDeviceInfo.TYPE_BLE_SPEAKER,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Spatializer state machine
|
// Spatializer state machine
|
||||||
private static final int STATE_UNINITIALIZED = 0;
|
private static final int STATE_UNINITIALIZED = 0;
|
||||||
private static final int STATE_NOT_SUPPORTED = 1;
|
private static final int STATE_NOT_SUPPORTED = 1;
|
||||||
@@ -166,6 +164,7 @@ public class SpatializerHelper {
|
|||||||
* List of devices where Spatial Audio is possible. Each device can be enabled or disabled
|
* List of devices where Spatial Audio is possible. Each device can be enabled or disabled
|
||||||
* (== user choice to use or not)
|
* (== user choice to use or not)
|
||||||
*/
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
|
private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
@@ -520,30 +519,30 @@ public class SpatializerHelper {
|
|||||||
* set to true if the device is added to the list, otherwise, if already
|
* set to true if the device is added to the list, otherwise, if already
|
||||||
* present, the setting is left untouched.
|
* present, the setting is left untouched.
|
||||||
*/
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
|
private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
|
||||||
boolean forceEnable) {
|
boolean forceEnable) {
|
||||||
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
|
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loglogi("addCompatibleAudioDevice: dev=" + ada);
|
loglogi("addCompatibleAudioDevice: dev=" + ada);
|
||||||
boolean isInList = false;
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
SADeviceState deviceUpdated = null; // non-null on update.
|
SADeviceState deviceUpdated = null; // non-null on update.
|
||||||
|
if (deviceState != null) {
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
if (forceEnable && !deviceState.mEnabled) {
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
|
||||||
isInList = true;
|
|
||||||
if (forceEnable) {
|
|
||||||
deviceState.mEnabled = true;
|
|
||||||
deviceUpdated = deviceState;
|
deviceUpdated = deviceState;
|
||||||
|
deviceUpdated.mEnabled = true;
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
|
// When adding, force the device type to be a canonical one.
|
||||||
|
final int canonicalDeviceType = getCanonicalDeviceType(ada.getType());
|
||||||
|
if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
|
||||||
|
Log.e(TAG, "addCompatibleAudioDevice with incompatible AudioDeviceAttributes "
|
||||||
|
+ ada);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
deviceUpdated = new SADeviceState(canonicalDeviceType, ada.getAddress());
|
||||||
if (!isInList) {
|
mSADevices.add(deviceUpdated);
|
||||||
final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
|
|
||||||
deviceState.mEnabled = true;
|
|
||||||
mSADevices.add(deviceState);
|
|
||||||
deviceUpdated = deviceState;
|
|
||||||
}
|
}
|
||||||
if (deviceUpdated != null) {
|
if (deviceUpdated != null) {
|
||||||
onRoutingUpdated();
|
onRoutingUpdated();
|
||||||
@@ -574,34 +573,59 @@ public class SpatializerHelper {
|
|||||||
|
|
||||||
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
|
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
|
||||||
loglogi("removeCompatibleAudioDevice: dev=" + ada);
|
loglogi("removeCompatibleAudioDevice: dev=" + ada);
|
||||||
SADeviceState deviceUpdated = null; // non-null on update.
|
|
||||||
|
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
if (deviceState != null && deviceState.mEnabled) {
|
||||||
deviceState.mEnabled = false;
|
deviceState.mEnabled = false;
|
||||||
deviceUpdated = deviceState;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deviceUpdated != null) {
|
|
||||||
onRoutingUpdated();
|
onRoutingUpdated();
|
||||||
mAudioService.persistSpatialAudioDeviceSettings();
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
logDeviceState(deviceUpdated, "removeCompatibleAudioDevice");
|
logDeviceState(deviceState, "removeCompatibleAudioDevice");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a possibly aliased device type which is used
|
||||||
|
* for spatial audio settings (or TYPE_UNKNOWN if it doesn't exist).
|
||||||
|
*/
|
||||||
|
private static @AudioDeviceInfo.AudioDeviceType int getCanonicalDeviceType(int deviceType) {
|
||||||
|
if (isWireless(deviceType)) return deviceType;
|
||||||
|
|
||||||
|
final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
|
||||||
|
if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
|
||||||
|
return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
|
||||||
|
} else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) {
|
||||||
|
return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
|
||||||
|
}
|
||||||
|
return AudioDeviceInfo.TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Spatial Audio device state for an audio device attributes
|
||||||
|
* or null if it does not exist.
|
||||||
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
|
@Nullable
|
||||||
|
private SADeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
|
||||||
|
final int deviceType = ada.getType();
|
||||||
|
final boolean isWireless = isWireless(deviceType);
|
||||||
|
final int canonicalDeviceType = getCanonicalDeviceType(deviceType);
|
||||||
|
|
||||||
|
for (SADeviceState deviceState : mSADevices) {
|
||||||
|
if (deviceState.mDeviceType == canonicalDeviceType
|
||||||
|
&& (!isWireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
|
||||||
|
return deviceState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return if Spatial Audio is enabled and available for the given device
|
* Return if Spatial Audio is enabled and available for the given device
|
||||||
* @param ada
|
* @param ada
|
||||||
* @return a pair of boolean, 1/ enabled? 2/ available?
|
* @return a pair of boolean, 1/ enabled? 2/ available?
|
||||||
*/
|
*/
|
||||||
private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
|
private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
|
||||||
// if not a wireless device, this value will be overwritten to map the type
|
final @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
|
||||||
// to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
|
|
||||||
@AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
|
|
||||||
|
|
||||||
// if not a wireless device: find if media device is in the speaker, wired headphones
|
|
||||||
if (!isWireless(deviceType)) {
|
|
||||||
// is the device type capable of doing SA?
|
// is the device type capable of doing SA?
|
||||||
if (!mSACapableDeviceTypes.contains(deviceType)) {
|
if (!mSACapableDeviceTypes.contains(deviceType)) {
|
||||||
Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
|
Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
|
||||||
@@ -614,50 +638,30 @@ public class SpatializerHelper {
|
|||||||
Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
|
Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
|
||||||
return new Pair<>(false, false);
|
return new Pair<>(false, false);
|
||||||
}
|
}
|
||||||
// map the spatialization mode to the SPEAKER or HEADPHONES device
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
|
if (deviceState == null) {
|
||||||
deviceType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
|
// no matching device state?
|
||||||
} else {
|
Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada);
|
||||||
deviceType = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
|
|
||||||
}
|
|
||||||
} else { // wireless device
|
|
||||||
if (isWirelessSpeaker(deviceType) && !mTransauralSupported) {
|
|
||||||
Log.i(TAG, "Device incompatible with Spatial Audio (no transaural) dev:"
|
|
||||||
+ ada);
|
|
||||||
return new Pair<>(false, false);
|
return new Pair<>(false, false);
|
||||||
}
|
}
|
||||||
if (!mBinauralSupported) {
|
// found the matching device state.
|
||||||
Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) dev:"
|
return new Pair<>(deviceState.mEnabled, true /* available */);
|
||||||
+ ada);
|
|
||||||
return new Pair<>(false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean enabled = false;
|
|
||||||
boolean available = false;
|
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
|
||||||
available = true;
|
|
||||||
enabled = deviceState.mEnabled;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Pair<>(enabled, available);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
|
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
|
||||||
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
|
if (!isDeviceCompatibleWithSpatializationModes(ada)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean knownDevice = false;
|
if (findDeviceStateForAudioDeviceAttributes(ada) == null) {
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
// wireless device types should be canonical, but we translate to be sure.
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
final int canonicalDeviceType = getCanonicalDeviceType((ada.getType()));
|
||||||
knownDevice = true;
|
if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
|
||||||
break;
|
Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes "
|
||||||
|
+ ada);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
final SADeviceState deviceState =
|
||||||
if (!knownDevice) {
|
new SADeviceState(canonicalDeviceType, ada.getAddress());
|
||||||
final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
|
|
||||||
mSADevices.add(deviceState);
|
mSADevices.add(deviceState);
|
||||||
mAudioService.persistSpatialAudioDeviceSettings();
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
|
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
|
||||||
@@ -699,12 +703,7 @@ public class SpatializerHelper {
|
|||||||
if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
|
if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
return findDeviceStateForAudioDeviceAttributes(ada) != null;
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
|
private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
|
||||||
@@ -1086,8 +1085,8 @@ public class SpatializerHelper {
|
|||||||
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
|
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
|
||||||
+ " for " + ada);
|
+ " for " + ada);
|
||||||
}
|
}
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
if (deviceState == null) return;
|
||||||
if (!deviceState.mHasHeadTracker) {
|
if (!deviceState.mHasHeadTracker) {
|
||||||
Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
|
Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
|
||||||
+ " device:" + ada + " on a device without headtracker");
|
+ " device:" + ada + " on a device without headtracker");
|
||||||
@@ -1097,9 +1096,7 @@ public class SpatializerHelper {
|
|||||||
deviceState.mHeadTrackerEnabled = enabled;
|
deviceState.mHeadTrackerEnabled = enabled;
|
||||||
mAudioService.persistSpatialAudioDeviceSettings();
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
logDeviceState(deviceState, "setHeadTrackerEnabled");
|
logDeviceState(deviceState, "setHeadTrackerEnabled");
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check current routing to see if it affects the headtracking mode
|
// check current routing to see if it affects the headtracking mode
|
||||||
if (ROUTING_DEVICES[0].getType() == ada.getType()
|
if (ROUTING_DEVICES[0].getType() == ada.getType()
|
||||||
&& ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
|
&& ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
|
||||||
@@ -1113,12 +1110,8 @@ public class SpatializerHelper {
|
|||||||
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
|
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
return deviceState != null && deviceState.mHasHeadTracker;
|
||||||
return deviceState.mHasHeadTracker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1131,8 +1124,8 @@ public class SpatializerHelper {
|
|||||||
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
|
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
if (deviceState != null) {
|
||||||
if (!deviceState.mHasHeadTracker) {
|
if (!deviceState.mHasHeadTracker) {
|
||||||
deviceState.mHasHeadTracker = true;
|
deviceState.mHasHeadTracker = true;
|
||||||
mAudioService.persistSpatialAudioDeviceSettings();
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
@@ -1140,7 +1133,6 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
return deviceState.mHeadTrackerEnabled;
|
return deviceState.mHeadTrackerEnabled;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
|
Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1150,15 +1142,9 @@ public class SpatializerHelper {
|
|||||||
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
|
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SADeviceState deviceState : mSADevices) {
|
final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
|
||||||
if (deviceState.matchesAudioDeviceAttributes(ada)) {
|
return deviceState != null
|
||||||
if (!deviceState.mHasHeadTracker) {
|
&& deviceState.mHasHeadTracker && deviceState.mHeadTrackerEnabled;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return deviceState.mHeadTrackerEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized boolean isHeadTrackerAvailable() {
|
synchronized boolean isHeadTrackerAvailable() {
|
||||||
@@ -1582,12 +1568,6 @@ public class SpatializerHelper {
|
|||||||
mDeviceType, mDeviceAddress == null ? "" : mDeviceAddress);
|
mDeviceType, mDeviceAddress == null ? "" : mDeviceAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matchesAudioDeviceAttributes(AudioDeviceAttributes ada) {
|
|
||||||
final int deviceType = ada.getType();
|
|
||||||
final boolean wireless = isWireless(deviceType);
|
|
||||||
return (deviceType == mDeviceType)
|
|
||||||
&& (!wireless || ada.getAddress().equals(mDeviceAddress));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ synchronized String getSADeviceSettings() {
|
/*package*/ synchronized String getSADeviceSettings() {
|
||||||
@@ -1608,7 +1588,10 @@ public class SpatializerHelper {
|
|||||||
// small list, not worth overhead of Arrays.stream(devSettings)
|
// small list, not worth overhead of Arrays.stream(devSettings)
|
||||||
for (String setting : devSettings) {
|
for (String setting : devSettings) {
|
||||||
SADeviceState devState = SADeviceState.fromPersistedString(setting);
|
SADeviceState devState = SADeviceState.fromPersistedString(setting);
|
||||||
|
// Note if the device is not compatible with spatialization mode
|
||||||
|
// or the device type is not canonical, it is ignored.
|
||||||
if (devState != null
|
if (devState != null
|
||||||
|
&& devState.mDeviceType == getCanonicalDeviceType(devState.mDeviceType)
|
||||||
&& isDeviceCompatibleWithSpatializationModes(
|
&& isDeviceCompatibleWithSpatializationModes(
|
||||||
devState.getAudioDeviceAttributes())) {
|
devState.getAudioDeviceAttributes())) {
|
||||||
mSADevices.add(devState);
|
mSADevices.add(devState);
|
||||||
@@ -1645,15 +1628,6 @@ public class SpatializerHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isWirelessSpeaker(@AudioDeviceInfo.AudioDeviceType int deviceType) {
|
|
||||||
for (int type : WIRELESS_SPEAKER_TYPES) {
|
|
||||||
if (type == deviceType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getHeadSensorHandleUpdateTracker() {
|
private int getHeadSensorHandleUpdateTracker() {
|
||||||
int headHandle = -1;
|
int headHandle = -1;
|
||||||
UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
|
UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
|
||||||
|
|||||||
Reference in New Issue
Block a user