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

Commit e1dae84f authored by Vlad Popa's avatar Vlad Popa
Browse files

Use Bluetooth metadata to categorize the audio device type

The AdiDeviceState values are updated every time a BT device connects
and whenever a client is requesting the values through the get/set API

Test: adb shell device_config put media_audio android.media.audio.automatic_bt_device_type true
Bug: 302323921
Change-Id: I29122a11e26589d7dcc63df93784f0d7d525466c
parent 9512dc28
Loading
Loading
Loading
Loading
+67 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;

import android.Manifest;
@@ -7170,10 +7171,52 @@ public class AudioManager {
     * Sets the audio device type of a Bluetooth device given its MAC address
     */
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public void setBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle,
    public void setBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle,
            @AudioDeviceCategory int btAudioDeviceType) {
        if (automaticBtDeviceType()) {
            // do nothing
            return;
        }
        try {
            getService().setBluetoothAudioDeviceCategory_legacy(address, isBle, btAudioDeviceType);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Gets the audio device type of a Bluetooth device given its MAC address
     */
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    @AudioDeviceCategory
    public int getBluetoothAudioDeviceCategory_legacy(@NonNull String address, boolean isBle) {
        if (automaticBtDeviceType()) {
            return AUDIO_DEVICE_CATEGORY_UNKNOWN;
        }
        try {
            return getService().getBluetoothAudioDeviceCategory_legacy(address, isBle);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Sets the audio device type of a Bluetooth device given its MAC address
     *
     * @return {@code true} if the device type was set successfully. If the
     *         audio device type was automatically identified this method will
     *         return {@code false}.
     */
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public boolean setBluetoothAudioDeviceCategory(@NonNull String address,
            @AudioDeviceCategory int btAudioDeviceCategory) {
        if (!automaticBtDeviceType()) {
            return false;
        }
        try {
            getService().setBluetoothAudioDeviceCategory(address, isBle, btAudioDeviceType);
            return getService().setBluetoothAudioDeviceCategory(address, btAudioDeviceCategory);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -7185,9 +7228,29 @@ public class AudioManager {
     */
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    @AudioDeviceCategory
    public int getBluetoothAudioDeviceCategory(@NonNull String address, boolean isBle) {
    public int getBluetoothAudioDeviceCategory(@NonNull String address) {
        if (!automaticBtDeviceType()) {
            return AUDIO_DEVICE_CATEGORY_UNKNOWN;
        }
        try {
            return getService().getBluetoothAudioDeviceCategory(address);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Returns {@code true} if the audio device type of a Bluetooth device can
     * be automatically identified
     */
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
        if (!automaticBtDeviceType()) {
            return false;
        }
        try {
            return getService().getBluetoothAudioDeviceCategory(address, isBle);
            return getService().isBluetoothAudioDeviceCategoryFixed(address);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+12 −2
Original line number Diff line number Diff line
@@ -338,10 +338,20 @@ interface IAudioService {
    oneway void setCsdAsAFeatureEnabled(boolean csdToggleValue);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    oneway void setBluetoothAudioDeviceCategory(in String address, boolean isBle, int deviceType);
    oneway void setBluetoothAudioDeviceCategory_legacy(in String address, boolean isBle,
            int deviceCategory);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    int getBluetoothAudioDeviceCategory(in String address, boolean isBle);
    int getBluetoothAudioDeviceCategory_legacy(in String address, boolean isBle);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    boolean setBluetoothAudioDeviceCategory(in String address, int deviceCategory);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    int getBluetoothAudioDeviceCategory(in String address);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    boolean isBluetoothAudioDeviceCategoryFixed(in String address);

    int setHdmiSystemAudioSupported(boolean on);

+55 −14
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.audio;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
import static android.media.AudioSystem.DEVICE_NONE;
import static android.media.AudioSystem.isBluetoothDevice;
import static android.media.audio.Flags.automaticBtDeviceType;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +56,8 @@ import java.util.Objects;
    @AudioManager.AudioDeviceCategory
    private int mAudioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN;

    private boolean mAutoBtCategorySet = false;

    private boolean mSAEnabled;
    private boolean mHasHeadTracker = false;
    private boolean mHeadTrackerEnabled;
@@ -84,58 +87,94 @@ import java.util.Objects;
        mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
    }

    public Pair<Integer, String> getDeviceId() {
    public synchronized Pair<Integer, String> getDeviceId() {
        return mDeviceId;
    }

    @AudioDeviceInfo.AudioDeviceType
    public int getDeviceType() {
    public synchronized int getDeviceType() {
        return mDeviceType;
    }

    public int getInternalDeviceType() {
    public synchronized int getInternalDeviceType() {
        return mInternalDeviceType;
    }

    @NonNull
    public String getDeviceAddress() {
    public synchronized String getDeviceAddress() {
        return mDeviceAddress;
    }

    public void setSAEnabled(boolean sAEnabled) {
    public synchronized void setSAEnabled(boolean sAEnabled) {
        mSAEnabled = sAEnabled;
    }

    public boolean isSAEnabled() {
    public synchronized boolean isSAEnabled() {
        return mSAEnabled;
    }

    public void setHeadTrackerEnabled(boolean headTrackerEnabled) {
    public synchronized void setHeadTrackerEnabled(boolean headTrackerEnabled) {
        mHeadTrackerEnabled = headTrackerEnabled;
    }

    public boolean isHeadTrackerEnabled() {
    public synchronized boolean isHeadTrackerEnabled() {
        return mHeadTrackerEnabled;
    }

    public void setHasHeadTracker(boolean hasHeadTracker) {
    public synchronized void setHasHeadTracker(boolean hasHeadTracker) {
        mHasHeadTracker = hasHeadTracker;
    }


    public boolean hasHeadTracker() {
    public synchronized boolean hasHeadTracker() {
        return mHasHeadTracker;
    }

    @AudioDeviceInfo.AudioDeviceType
    public int getAudioDeviceCategory() {
    public synchronized int getAudioDeviceCategory() {
        return mAudioDeviceCategory;
    }

    public void setAudioDeviceCategory(@AudioDeviceInfo.AudioDeviceType int audioDeviceCategory) {
    public synchronized void setAudioDeviceCategory(
            @AudioDeviceInfo.AudioDeviceType int audioDeviceCategory) {
        mAudioDeviceCategory = audioDeviceCategory;
    }

    public synchronized boolean isBtDeviceCategoryFixed() {
        if (!automaticBtDeviceType()) {
            // do nothing
            return false;
        }

        updateAudioDeviceCategory();
        return mAutoBtCategorySet;
    }

    public synchronized boolean updateAudioDeviceCategory() {
        if (!automaticBtDeviceType()) {
            // do nothing
            return false;
        }
        if (!isBluetoothDevice(mInternalDeviceType)) {
            return false;
        }
        if (mAutoBtCategorySet) {
            // no need to update. The auto value is already set.
            return false;
        }

        int newAudioDeviceCategory = BtHelper.getBtDeviceCategory(mDeviceAddress);
        if (newAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_UNKNOWN) {
            // no info provided by the BtDevice metadata
            return false;
        }

        mAudioDeviceCategory = newAudioDeviceCategory;
        mAutoBtCategorySet = true;
        return true;

    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
@@ -175,7 +214,7 @@ import java.util.Objects;
                + " HTenabled: " + mHeadTrackerEnabled;
    }

    public String toPersistableString() {
    public synchronized String toPersistableString() {
        return (new StringBuilder().append(mDeviceType)
                .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
                .append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0")
@@ -228,6 +267,8 @@ import java.util.Objects;
            deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
            deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
            deviceState.setAudioDeviceCategory(audioDeviceCategory);
            // update in case we can automatically determine the category
            deviceState.updateAudioDeviceCategory();
            return deviceState;
        } catch (NumberFormatException e) {
            Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
@@ -235,7 +276,7 @@ import java.util.Objects;
        }
    }

    public AudioDeviceAttributes getAudioDeviceAttributes() {
    public synchronized AudioDeviceAttributes getAudioDeviceAttributes() {
        return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
                mDeviceType, mDeviceAddress);
    }
+31 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioManager.AudioDeviceCategory;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
@@ -1483,8 +1484,12 @@ public class AudioDeviceBroker {
                MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES, SENDMSG_QUEUE, groupId);
    }

    /*package*/ void postSynchronizeLeDevicesInInventory(AdiDeviceState deviceState) {
        sendLMsgNoDelay(MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
    /*package*/ void postSynchronizeAdiDevicesInInventory(AdiDeviceState deviceState) {
        sendLMsgNoDelay(MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
    }

    /*package*/ void postUpdatedAdiDeviceState(AdiDeviceState deviceState) {
        sendLMsgNoDelay(MSG_L_UPDATED_ADI_DEVICE_STATE, SENDMSG_QUEUE, deviceState);
    }

    /*package*/ static final class CommunicationDeviceInfo {
@@ -2004,14 +2009,19 @@ public class AudioDeviceBroker {
                        }
                    } break;

                case MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY:
                case MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY:
                    synchronized (mSetModeLock) {
                        synchronized (mDeviceStateLock) {
                            mDeviceInventory.onSynchronizeLeDevicesInInventory(
                            mDeviceInventory.onSynchronizeAdiDevicesInInventory(
                                    (AdiDeviceState) msg.obj);
                        }
                    } break;

                case MSG_L_UPDATED_ADI_DEVICE_STATE:
                    synchronized (mDeviceStateLock) {
                        mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj);
                    } break;

                default:
                    Log.wtf(TAG, "Invalid message " + msg.what);
            }
@@ -2095,7 +2105,8 @@ public class AudioDeviceBroker {

    private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
    private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57;
    private static final int MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY = 58;
    private static final int MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY = 58;
    private static final int MSG_L_UPDATED_ADI_DEVICE_STATE = 59;



@@ -2742,6 +2753,21 @@ public class AudioDeviceBroker {
        return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType);
    }

    void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
            @AudioDeviceCategory int btAudioDeviceCategory) {
        mDeviceInventory.addAudioDeviceWithCategoryInInventoryIfNeeded(address,
                btAudioDeviceCategory);
    }

    @AudioDeviceCategory
    int getAndUpdateBtAdiDeviceStateCategoryForAddress(@NonNull String address) {
        return mDeviceInventory.getAndUpdateBtAdiDeviceStateCategoryForAddress(address);
    }

    boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
        return mDeviceInventory.isBluetoothAudioDeviceCategoryFixed(address);
    }

    //------------------------------------------------
    // for testing purposes only
    void clearDeviceInventory() {
+175 −58

File changed.

Preview size limit exceeded, changes collapsed.

Loading