diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index dd44af1b68ee..e66c0c68ddc8 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -46,6 +46,8 @@ import android.util.Log; import android.util.Pair; import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -103,10 +105,6 @@ public class SpatializerHelper { AudioDeviceInfo.TYPE_BLE_BROADCAST }; - private static final int[] WIRELESS_SPEAKER_TYPES = { - AudioDeviceInfo.TYPE_BLE_SPEAKER, - }; - // Spatializer state machine private static final int STATE_UNINITIALIZED = 0; 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 * (== user choice to use or not) */ + @GuardedBy("this") private final ArrayList mSADevices = new ArrayList<>(0); //------------------------------------------------------ @@ -513,30 +512,30 @@ public class SpatializerHelper { * set to true if the device is added to the list, otherwise, if already * present, the setting is left untouched. */ + @GuardedBy("this") private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada, boolean forceEnable) { if (!isDeviceCompatibleWithSpatializationModes(ada)) { return; } loglogi("addCompatibleAudioDevice: dev=" + ada); - boolean isInList = false; + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); SADeviceState deviceUpdated = null; // non-null on update. - - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - isInList = true; - if (forceEnable) { - deviceState.mEnabled = true; - deviceUpdated = deviceState; - } - break; + if (deviceState != null) { + if (forceEnable && !deviceState.mEnabled) { + deviceUpdated = deviceState; + deviceUpdated.mEnabled = true; } - } - if (!isInList) { - final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress()); - deviceState.mEnabled = true; - mSADevices.add(deviceState); - deviceUpdated = deviceState; + } 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()); + mSADevices.add(deviceUpdated); } if (deviceUpdated != null) { onRoutingUpdated(); @@ -567,90 +566,95 @@ public class SpatializerHelper { synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) { loglogi("removeCompatibleAudioDevice: dev=" + ada); - SADeviceState deviceUpdated = null; // non-null on update. - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - deviceState.mEnabled = false; - deviceUpdated = deviceState; - break; - } - } - if (deviceUpdated != null) { + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + if (deviceState != null && deviceState.mEnabled) { + deviceState.mEnabled = false; onRoutingUpdated(); 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 * @param ada * @return a pair of boolean, 1/ enabled? 2/ available? */ private synchronized Pair evaluateState(AudioDeviceAttributes ada) { - // if not a wireless device, this value will be overwritten to map the type - // 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? - if (!mSACapableDeviceTypes.contains(deviceType)) { - Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada); - return new Pair<>(false, false); - } - // what spatialization mode to use for this device? - final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); - if (spatMode == Integer.MIN_VALUE) { - // error case, device not found - Log.e(TAG, "no spatialization mode found for device type:" + deviceType); - return new Pair<>(false, false); - } - // map the spatialization mode to the SPEAKER or HEADPHONES device - if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) { - deviceType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; - } else { - 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); - } - if (!mBinauralSupported) { - Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) dev:" - + ada); - return new Pair<>(false, false); - } + final @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType(); + // is the device type capable of doing SA? + if (!mSACapableDeviceTypes.contains(deviceType)) { + Log.i(TAG, "Device incompatible with Spatial Audio dev:" + 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; - } + // what spatialization mode to use for this device? + final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); + if (spatMode == Integer.MIN_VALUE) { + // error case, device not found + Log.e(TAG, "no spatialization mode found for device type:" + deviceType); + return new Pair<>(false, false); } - return new Pair<>(enabled, available); + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + if (deviceState == null) { + // no matching device state? + Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada); + return new Pair<>(false, false); + } + // found the matching device state. + return new Pair<>(deviceState.mEnabled, true /* available */); } private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) { if (!isDeviceCompatibleWithSpatializationModes(ada)) { return; } - boolean knownDevice = false; - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - knownDevice = true; - break; + if (findDeviceStateForAudioDeviceAttributes(ada) == null) { + // wireless device types should be canonical, but we translate to be sure. + final int canonicalDeviceType = getCanonicalDeviceType((ada.getType())); + if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) { + Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes " + + ada); + return; } - } - if (!knownDevice) { - final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress()); + final SADeviceState deviceState = + new SADeviceState(canonicalDeviceType, ada.getAddress()); mSADevices.add(deviceState); mAudioService.persistSpatialAudioDeviceSettings(); logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later. @@ -692,12 +696,7 @@ public class SpatializerHelper { if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) { return false; } - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - return true; - } - } - return false; + return findDeviceStateForAudioDeviceAttributes(ada) != null; } private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes, @@ -1079,20 +1078,18 @@ public class SpatializerHelper { Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled + " for " + ada); } - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - if (!deviceState.mHasHeadTracker) { - Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled - + " device:" + ada + " on a device without headtracker"); - return; - } - Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada); - deviceState.mHeadTrackerEnabled = enabled; - mAudioService.persistSpatialAudioDeviceSettings(); - logDeviceState(deviceState, "setHeadTrackerEnabled"); - break; - } + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + if (deviceState == null) return; + if (!deviceState.mHasHeadTracker) { + Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled + + " device:" + ada + " on a device without headtracker"); + return; } + Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada); + deviceState.mHeadTrackerEnabled = enabled; + mAudioService.persistSpatialAudioDeviceSettings(); + logDeviceState(deviceState, "setHeadTrackerEnabled"); + // check current routing to see if it affects the headtracking mode if (ROUTING_DEVICES[0].getType() == ada.getType() && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) { @@ -1106,12 +1103,8 @@ public class SpatializerHelper { Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada); return false; } - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - return deviceState.mHasHeadTracker; - } - } - return false; + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + return deviceState != null && deviceState.mHasHeadTracker; } /** @@ -1124,15 +1117,14 @@ public class SpatializerHelper { Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada); return false; } - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - if (!deviceState.mHasHeadTracker) { - deviceState.mHasHeadTracker = true; - mAudioService.persistSpatialAudioDeviceSettings(); - logDeviceState(deviceState, "setHasHeadTracker"); - } - return deviceState.mHeadTrackerEnabled; + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + if (deviceState != null) { + if (!deviceState.mHasHeadTracker) { + deviceState.mHasHeadTracker = true; + mAudioService.persistSpatialAudioDeviceSettings(); + logDeviceState(deviceState, "setHasHeadTracker"); } + return deviceState.mHeadTrackerEnabled; } Log.e(TAG, "setHasHeadTracker: device not found for:" + ada); return false; @@ -1143,15 +1135,9 @@ public class SpatializerHelper { Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada); return false; } - for (SADeviceState deviceState : mSADevices) { - if (deviceState.matchesAudioDeviceAttributes(ada)) { - if (!deviceState.mHasHeadTracker) { - return false; - } - return deviceState.mHeadTrackerEnabled; - } - } - return false; + final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada); + return deviceState != null + && deviceState.mHasHeadTracker && deviceState.mHeadTrackerEnabled; } synchronized boolean isHeadTrackerAvailable() { @@ -1575,12 +1561,6 @@ public class SpatializerHelper { 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() { @@ -1601,7 +1581,10 @@ public class SpatializerHelper { // small list, not worth overhead of Arrays.stream(devSettings) for (String setting : devSettings) { 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 + && devState.mDeviceType == getCanonicalDeviceType(devState.mDeviceType) && isDeviceCompatibleWithSpatializationModes( devState.getAudioDeviceAttributes())) { mSADevices.add(devState); @@ -1638,15 +1621,6 @@ public class SpatializerHelper { 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() { int headHandle = -1; UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);