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

Commit 13ae20f3 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioService: Pick head tracking sensor based on UUID

When the connected Bluetooth A2DP device reports a head tracker UUID,
use a head tracking sensor with this same UUID in priority over any
other head tracking sensor.

Bug: 210803914
Test: make
Change-Id: I8fa148e45fa36a01f1f44a79c7766595953fcc3f
parent ae36c6a8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;


@@ -1794,4 +1795,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
        }
        return null;
    }

    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.getDeviceSensorUuid(device);
        }
    }
}
+30 −3
Original line number Diff line number Diff line
@@ -48,7 +48,9 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/**
 * Class to manage the inventory of all connected devices.
@@ -185,12 +187,20 @@ public class AudioDeviceInventory {
        final @NonNull String mDeviceName;
        final @NonNull String mDeviceAddress;
        int mDeviceCodecFormat;
        final UUID mSensorUuid;

        DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) {
        DeviceInfo(int deviceType, String deviceName, String deviceAddress,
                   int deviceCodecFormat, UUID sensorUuid) {
            mDeviceType = deviceType;
            mDeviceName = deviceName == null ? "" : deviceName;
            mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
            mDeviceCodecFormat = deviceCodecFormat;
            mSensorUuid = sensorUuid;
        }

        DeviceInfo(int deviceType, String deviceName, String deviceAddress,
                   int deviceCodecFormat) {
            this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
        }

        @Override
@@ -199,7 +209,8 @@ public class AudioDeviceInventory {
                    + " (" + AudioSystem.getDeviceName(mDeviceType)
                    + ") name:" + mDeviceName
                    + " addr:" + mDeviceAddress
                    + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]";
                    + " codec: " + Integer.toHexString(mDeviceCodecFormat)
                    + " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
        }

        @NonNull String getKey() {
@@ -980,8 +991,13 @@ public class AudioDeviceInventory {
        // Reset A2DP suspend state each time a new sink is connected
        mAudioSystem.setParameters("A2dpSuspended=false");

        // The convention for head tracking sensors associated with A2DP devices is to
        // use a UUID derived from the MAC address as follows:
        //   time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
        final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                address, a2dpCodec);
                address, a2dpCodec, sensorUuid);
        final String diKey = di.getKey();
        mConnectedDevices.put(diKey, di);
        // on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1453,6 +1469,17 @@ public class AudioDeviceInventory {
        mDevRoleCapturePresetDispatchers.finishBroadcast();
    }

    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
        final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
                device.getAddress());
        synchronized (mDevicesLock) {
            DeviceInfo di = mConnectedDevices.get(key);
            if (di == null) {
                return null;
            }
            return di.mSensorUuid;
        }
    }
    //----------------------------------------------------------
    // For tests only

+5 −0
Original line number Diff line number Diff line
@@ -190,6 +190,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -11017,6 +11018,10 @@ public class AudioService extends IAudioService.Stub
        return delayMillis;
    }

    UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
        return mDeviceBroker.getDeviceSensorUuid(device);
    }

    //======================
    // misc
    //======================
+22 −6
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

/**
 * A helper class to manage Spatializer related functionality
@@ -168,6 +169,7 @@ public class SpatializerHelper {
            return;
        }
        mState = STATE_DISABLED_UNAVAILABLE;
        mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
        // note at this point mSpat is still not instantiated
    }

@@ -204,6 +206,11 @@ public class SpatializerHelper {
        logd("onRoutingUpdated: can spatialize media 5.1:" + able
                + " on device:" + ROUTING_DEVICES[0]);
        setDispatchAvailableState(able);

        if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED
                && mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
            postInitSensors();
        }
    }

    //------------------------------------------------------
@@ -909,13 +916,22 @@ public class SpatializerHelper {
                }
            }
            // initialize sensor handles
            // TODO-HT update to non-private sensor once head tracker sensor is defined
            for (Sensor sensor : mSensorManager.getDynamicSensorList(
                    Sensor.TYPE_DEVICE_PRIVATE_BASE)) {
                if (sensor.getStringType().equals(HEADTRACKER_SENSOR)) {
            UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
            List<Sensor> sensors = new ArrayList<Sensor>(0);
            sensors.addAll(mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER));
            sensors.addAll(mSensorManager.getDynamicSensorList(Sensor.TYPE_DEVICE_PRIVATE_BASE));
            for (Sensor sensor : sensors) {
                if (sensor.getType() == Sensor.TYPE_HEAD_TRACKER
                        || sensor.getStringType().equals(HEADTRACKER_SENSOR)) {
                    UUID uuid = sensor.getUuid();
                    if (uuid.equals(routingDeviceUuid)) {
                        headHandle = sensor.getHandle();
                        break;
                    }
                    if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
                        headHandle = sensor.getHandle();
                    }
                }
            }
            Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
            screenHandle = screenSensor.getHandle();
+70 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.audio;

import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
import android.util.Slog;

import java.util.UUID;

/**
 *  UuidUtils class implements helper functions to handle unique identifiers
 *  used to associate head tracking sensors to audio devices.
 */
class UuidUtils {
    private static final String TAG = "AudioService.UuidUtils";

    private static final long LSB_PREFIX_MASK = 0xFFFF000000000000L;
    private static final long LSB_SUFFIX_MASK = 0x0000FFFFFFFFFFFFL;
    // The sensor UUID for Bluetooth devices is defined as follows:
    // - 8 most significant bytes: All 0s
    // - 8 most significant bytes: Ascii B, Ascii T, Device MAC address on 6 bytes
    private static final long LSB_PREFIX_BT = 0x4254000000000000L;

    /**
     * Special UUID for a head tracking sensor not associated with an audio device.
     */
    public static final UUID STANDALONE_UUID = new UUID(0, 0);

    /**
     *  Generate a headtracking UUID from AudioDeviceAttributes
     */
    public static UUID uuidFromAudioDeviceAttributes(AudioDeviceAttributes device) {
        switch (device.getInternalType()) {
            case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP:
                String address = device.getAddress().replace(":", "");
                if (address.length() != 12) {
                    return null;
                }
                address = "0x" + address;
                long lsb = LSB_PREFIX_BT;
                try {
                    lsb |= Long.decode(address).longValue();
                } catch (NumberFormatException e) {
                    return null;
                }
                if (AudioService.DEBUG_DEVICES) {
                    Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
                }
                return new UUID(0, lsb);
            default:
                // Handle other device types here
                return null;
        }
    }
}