Merge "Spatializer: Fix usb headset" into tm-qpr-dev
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index cd5960f..1def72b 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.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 @@
             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 @@
      * 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);
 
     //------------------------------------------------------
@@ -520,30 +519,30 @@
      *                    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();
@@ -574,90 +573,95 @@
 
     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<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)) {
-            // 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.
@@ -699,12 +703,7 @@
         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,
@@ -1086,20 +1085,18 @@
             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())) {
@@ -1113,12 +1110,8 @@
             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;
     }
 
     /**
@@ -1131,15 +1124,14 @@
             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;
@@ -1150,15 +1142,9 @@
             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() {
@@ -1582,12 +1568,6 @@
                     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() {
@@ -1608,7 +1588,10 @@
         // 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);
@@ -1645,15 +1628,6 @@
         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]);