Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12f9b9fa authored by Andy Hung's avatar Andy Hung
Browse files

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
Merged-In: I904a299556f1cccb9d8d69a23245e387f7a5d35e
Change-Id: I904a299556f1cccb9d8d69a23245e387f7a5d35e
parent a36a023d
Loading
Loading
Loading
Loading
+114 −140
Original line number Diff line number Diff line
@@ -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<SADeviceState> 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;
        if (deviceState != null) {
            if (forceEnable && !deviceState.mEnabled) {
                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;
            }
        }
        if (!isInList) {
            final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
            deviceState.mEnabled = true;
            mSADevices.add(deviceState);
            deviceUpdated = deviceState;
            deviceUpdated = new SADeviceState(canonicalDeviceType, ada.getAddress());
            mSADevices.add(deviceUpdated);
        }
        if (deviceUpdated != null) {
            onRoutingUpdated();
@@ -567,20 +566,50 @@ 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)) {
        final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
        if (deviceState != null && deviceState.mEnabled) {
            deviceState.mEnabled = false;
                deviceUpdated = deviceState;
                break;
            }
        }
        if (deviceUpdated != null) {
            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;
    }

    /**
@@ -589,12 +618,7 @@ public class SpatializerHelper {
     * @return a pair of boolean, 1/ enabled? 2/ available?
     */
    private synchronized Pair<Boolean, Boolean> 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)) {
        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);
@@ -607,50 +631,30 @@ public class SpatializerHelper {
            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);
        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);
        }
            if (!mBinauralSupported) {
                Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) 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;
            }
        }
        return new Pair<>(enabled, available);
        // 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,8 +1078,8 @@ public class SpatializerHelper {
            Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
                    + " for " + ada);
        }
        for (SADeviceState deviceState : mSADevices) {
            if (deviceState.matchesAudioDeviceAttributes(ada)) {
        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");
@@ -1090,9 +1089,7 @@ public class SpatializerHelper {
        deviceState.mHeadTrackerEnabled = enabled;
        mAudioService.persistSpatialAudioDeviceSettings();
        logDeviceState(deviceState, "setHeadTrackerEnabled");
                break;
            }
        }

        // 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,8 +1117,8 @@ public class SpatializerHelper {
            Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
            return false;
        }
        for (SADeviceState deviceState : mSADevices) {
            if (deviceState.matchesAudioDeviceAttributes(ada)) {
        final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
        if (deviceState != null) {
            if (!deviceState.mHasHeadTracker) {
                deviceState.mHasHeadTracker = true;
                mAudioService.persistSpatialAudioDeviceSettings();
@@ -1133,7 +1126,6 @@ public class SpatializerHelper {
            }
            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]);