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

Commit 6ac3d53b authored by Vlad Popa's avatar Vlad Popa Committed by Android (Google) Code Review
Browse files

Merge "CSD: Add possibility to select the BT audio device type" into main

parents bffc5f4b 2ea196af
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
@@ -6927,6 +6927,114 @@ public class AudioManager {
        }
    }

    /**
     * @hide
     * Describes an audio device that has not been categorized with a specific
     * audio type.
     */
    public static final int AUDIO_DEVICE_CATEGORY_UNKNOWN = 0;

    /**
     * @hide
     * Describes an audio device which is categorized as something different.
     */
    public static final int AUDIO_DEVICE_CATEGORY_OTHER = 1;

    /**
     * @hide
     * Describes an audio device which was categorized as speakers.
     */
    public static final int AUDIO_DEVICE_CATEGORY_SPEAKER = 2;

    /**
     * @hide
     * Describes an audio device which was categorized as headphones.
     */
    public static final int AUDIO_DEVICE_CATEGORY_HEADPHONES = 3;

    /**
     * @hide
     * Describes an audio device which was categorized as car-kit.
     */
    public static final int AUDIO_DEVICE_CATEGORY_CARKIT = 4;

    /**
     * @hide
     * Describes an audio device which was categorized as watch.
     */
    public static final int AUDIO_DEVICE_CATEGORY_WATCH = 5;

    /**
     * @hide
     * Describes an audio device which was categorized as hearing aid.
     */
    public static final int AUDIO_DEVICE_CATEGORY_HEARING_AID = 6;

    /**
     * @hide
     * Describes an audio device which was categorized as receiver.
     */
    public static final int AUDIO_DEVICE_CATEGORY_RECEIVER = 7;

    /** @hide */
    @IntDef(flag = false, prefix = "AUDIO_DEVICE_CATEGORY", value = {
            AUDIO_DEVICE_CATEGORY_UNKNOWN,
            AUDIO_DEVICE_CATEGORY_OTHER,
            AUDIO_DEVICE_CATEGORY_SPEAKER,
            AUDIO_DEVICE_CATEGORY_HEADPHONES,
            AUDIO_DEVICE_CATEGORY_CARKIT,
            AUDIO_DEVICE_CATEGORY_WATCH,
            AUDIO_DEVICE_CATEGORY_HEARING_AID,
            AUDIO_DEVICE_CATEGORY_RECEIVER }
    )
    @Retention(RetentionPolicy.SOURCE)
    public @interface AudioDeviceCategory {}

    /** @hide */
    public static String audioDeviceCategoryToString(int audioDeviceCategory) {
        switch (audioDeviceCategory) {
            case AUDIO_DEVICE_CATEGORY_UNKNOWN: return "AUDIO_DEVICE_CATEGORY_UNKNOWN";
            case AUDIO_DEVICE_CATEGORY_OTHER: return "AUDIO_DEVICE_CATEGORY_OTHER";
            case AUDIO_DEVICE_CATEGORY_SPEAKER: return "AUDIO_DEVICE_CATEGORY_SPEAKER";
            case AUDIO_DEVICE_CATEGORY_HEADPHONES: return "AUDIO_DEVICE_CATEGORY_HEADPHONES";
            case AUDIO_DEVICE_CATEGORY_CARKIT: return "AUDIO_DEVICE_CATEGORY_CARKIT";
            case AUDIO_DEVICE_CATEGORY_WATCH: return "AUDIO_DEVICE_CATEGORY_WATCH";
            case AUDIO_DEVICE_CATEGORY_HEARING_AID: return "AUDIO_DEVICE_CATEGORY_HEARING_AID";
            case AUDIO_DEVICE_CATEGORY_RECEIVER: return "AUDIO_DEVICE_CATEGORY_RECEIVER";
            default:
                return new StringBuilder("unknown AudioDeviceCategory ").append(
                        audioDeviceCategory).toString();
        }
    }

    /**
     * @hide
     * 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,
            @AudioDeviceCategory int btAudioDeviceType) {
        try {
            getService().setBluetoothAudioDeviceCategory(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(@NonNull String address, boolean isBle) {
        try {
            return getService().getBluetoothAudioDeviceCategory(address, isBle);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Sound dose warning at every 100% of dose during integration window
+6 −0
Original line number Diff line number Diff line
@@ -324,6 +324,12 @@ interface IAudioService {
    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    boolean isCsdEnabled();

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

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

    int setHdmiSystemAudioSupported(boolean on);

    boolean isHdmiSystemAudioSupported();
+43 −8
Original line number Diff line number Diff line
@@ -16,6 +16,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;

@@ -23,8 +24,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import java.util.Objects;

@@ -41,8 +44,16 @@ import java.util.Objects;
    private final int mDeviceType;

    private final int mInternalDeviceType;

    @NonNull
    private final String mDeviceAddress;

    /** Unique device id from internal device type and address. */
    private final Pair<Integer, String> mDeviceId;

    @AudioManager.AudioDeviceCategory
    private int mAudioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN;

    private boolean mSAEnabled;
    private boolean mHasHeadTracker = false;
    private boolean mHeadTrackerEnabled;
@@ -68,6 +79,12 @@ import java.util.Objects;
        }
        mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
                address) : "";

        mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
    }

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

    @AudioDeviceInfo.AudioDeviceType
@@ -109,6 +126,15 @@ import java.util.Objects;
        return mHasHeadTracker;
    }

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

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
@@ -127,20 +153,23 @@ import java.util.Objects;
                && mDeviceAddress.equals(sads.mDeviceAddress)  // NonNull
                && mSAEnabled == sads.mSAEnabled
                && mHasHeadTracker == sads.mHasHeadTracker
                && mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
                && mHeadTrackerEnabled == sads.mHeadTrackerEnabled
                && mAudioDeviceCategory == sads.mAudioDeviceCategory;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled,
                mHasHeadTracker, mHeadTrackerEnabled);
                mHasHeadTracker, mHeadTrackerEnabled, mAudioDeviceCategory);
    }

    @Override
    public String toString() {
        return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
                + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
                + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
                + " addr: " + mDeviceAddress + " bt audio type: "
                + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
                + " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker
                + " HTenabled: " + mHeadTrackerEnabled;
    }

    public String toPersistableString() {
@@ -150,6 +179,7 @@ import java.util.Objects;
                .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
                .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
                .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType)
                .append(SETTING_FIELD_SEPARATOR).append(mAudioDeviceCategory)
                .toString());
    }

@@ -174,21 +204,27 @@ import java.util.Objects;
        String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
        // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal
        // device type
        if (fields.length != 5 && fields.length != 6) {
            // expecting all fields, fewer may mean corruption, ignore those settings
        if (fields.length < 5 || fields.length > 7) {
            // different number of fields may mean corruption, ignore those settings
            // newly added fields are optional (mInternalDeviceType, mBtAudioDeviceCategory)
            return null;
        }
        try {
            final int deviceType = Integer.parseInt(fields[0]);
            int internalDeviceType = -1;
            if (fields.length == 6) {
            if (fields.length >= 6) {
                internalDeviceType = Integer.parseInt(fields[5]);
            }
            int audioDeviceCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN;
            if (fields.length == 7) {
                audioDeviceCategory = Integer.parseInt(fields[6]);
            }
            final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
                    internalDeviceType, fields[1]);
            deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
            deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
            deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
            deviceState.setAudioDeviceCategory(audioDeviceCategory);
            return deviceState;
        } catch (NumberFormatException e) {
            Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
@@ -200,5 +236,4 @@ import java.util.Objects;
        return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
                mDeviceType, mDeviceAddress);
    }

}
+20 −9
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -2493,13 +2494,13 @@ public class AudioDeviceBroker {

    void onPersistAudioDeviceSettings() {
        final String deviceSettings = mDeviceInventory.getDeviceSettings();
        Log.v(TAG, "saving audio device settings: " + deviceSettings);
        Log.v(TAG, "saving AdiDeviceState: " + deviceSettings);
        final SettingsAdapter settings = mAudioService.getSettings();
        boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(),
                Settings.Secure.AUDIO_DEVICE_INVENTORY,
                deviceSettings, UserHandle.USER_CURRENT);
        if (!res) {
            Log.e(TAG, "error saving audio device settings: " + deviceSettings);
            Log.e(TAG, "error saving AdiDeviceState: " + deviceSettings);
        }
    }

@@ -2509,7 +2510,7 @@ public class AudioDeviceBroker {
        String settings = settingsAdapter.getSecureStringForUser(contentResolver,
                Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
        if (settings == null) {
            Log.i(TAG, "reading spatial audio device settings from legacy key"
            Log.i(TAG, "reading AdiDeviceState from legacy key"
                    + Settings.Secure.SPATIAL_AUDIO_ENABLED);
            // legacy string format for key SPATIAL_AUDIO_ENABLED has the same order of fields like
            // the strings for key AUDIO_DEVICE_INVENTORY. This will ensure to construct valid
@@ -2517,21 +2518,21 @@ public class AudioDeviceBroker {
            settings = settingsAdapter.getSecureStringForUser(contentResolver,
                    Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
            if (settings == null) {
                Log.i(TAG, "no spatial audio device settings stored with legacy key");
                Log.i(TAG, "no AdiDeviceState stored with legacy key");
            } else if (!settings.equals("")) {
                // Delete old key value and update the new key
                if (!settingsAdapter.putSecureStringForUser(contentResolver,
                        Settings.Secure.SPATIAL_AUDIO_ENABLED,
                        /*value=*/"",
                        UserHandle.USER_CURRENT)) {
                    Log.w(TAG, "cannot erase the legacy audio device settings with key "
                    Log.w(TAG, "cannot erase the legacy AdiDeviceState with key "
                            + Settings.Secure.SPATIAL_AUDIO_ENABLED);
                }
                if (!settingsAdapter.putSecureStringForUser(contentResolver,
                        Settings.Secure.AUDIO_DEVICE_INVENTORY,
                        settings,
                        UserHandle.USER_CURRENT)) {
                    Log.e(TAG, "error updating the new audio device settings with key "
                    Log.e(TAG, "error updating the new AdiDeviceState with key "
                            + Settings.Secure.AUDIO_DEVICE_INVENTORY);
                }
            }
@@ -2551,19 +2552,29 @@ public class AudioDeviceBroker {
        return mDeviceInventory.getDeviceSettings();
    }

    List<AdiDeviceState> getImmutableDeviceInventory() {
    Collection<AdiDeviceState> getImmutableDeviceInventory() {
        return mDeviceInventory.getImmutableDeviceInventory();
    }

    void addDeviceStateToInventory(AdiDeviceState deviceState) {
        mDeviceInventory.addDeviceStateToInventory(deviceState);
    void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
        mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState);
    }

    void addOrUpdateBtAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
        mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState);
    }

    @Nullable
    AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
            int canonicalType) {
        return mDeviceInventory.findDeviceStateForAudioDeviceAttributes(ada, canonicalType);
    }

    @Nullable
    AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
        return mDeviceInventory.findBtDeviceStateForAddress(address, isBle);
    }

    //------------------------------------------------
    // for testing purposes only
    void clearDeviceInventory() {
+91 −25
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.server.audio;

import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET;
import static android.media.AudioSystem.isBluetoothDevice;

import android.annotation.NonNull;
@@ -61,11 +63,13 @@ import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -90,35 +94,95 @@ public class AudioDeviceInventory {
    private static final String mMetricsId = "audio.device.";

    private final Object mDeviceInventoryLock = new Object();
    @GuardedBy("mDeviceCatalogLock")
    private final ArrayList<AdiDeviceState> mDeviceInventory = new ArrayList<>(0);
    List<AdiDeviceState> getImmutableDeviceInventory() {

    @GuardedBy("mDeviceInventoryLock")
    private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();

    Collection<AdiDeviceState> getImmutableDeviceInventory() {
        synchronized (mDeviceInventoryLock) {
            return mDeviceInventory.values();
        }
    }

    /**
     * Adds a new AdiDeviceState or updates the spatial audio related properties of the matching
     * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
     * @param deviceState the device to update
     */
    void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
        synchronized (mDeviceInventoryLock) {
            return List.copyOf(mDeviceInventory);
            mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> {
                oldState.setHasHeadTracker(newState.hasHeadTracker());
                oldState.setHeadTrackerEnabled(newState.isHeadTrackerEnabled());
                oldState.setSAEnabled(newState.isSAEnabled());
                return oldState;
            });
        }
    }

    /**
     * Adds a new AdiDeviceState or updates the audio device cateogory of the matching
     * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
     * @param deviceState the device to update
     */
    void addOrUpdateAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
        synchronized (mDeviceInventoryLock) {
            mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> {
                oldState.setAudioDeviceCategory(newState.getAudioDeviceCategory());
                return oldState;
            });
        }
    }

    void addDeviceStateToInventory(AdiDeviceState deviceState) {
    /**
     * Finds the BT device that matches the passed {@code address}. Currently, this method only
     * returns a valid device for A2DP and BLE devices.
     *
     * @param address MAC address of BT device
     * @param isBle true if the device is BLE, false for A2DP
     * @return the found {@link AdiDeviceState} or {@code null} otherwise.
     */
    @Nullable
    AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
        synchronized (mDeviceInventoryLock) {
            mDeviceInventory.add(deviceState);
            final Set<Integer> deviceSet = isBle ? DEVICE_OUT_ALL_BLE_SET : DEVICE_OUT_ALL_A2DP_SET;
            for (Integer internalType : deviceSet) {
                AdiDeviceState deviceState = mDeviceInventory.get(
                        new Pair<>(internalType, address));
                if (deviceState != null) {
                    return deviceState;
                }
            }
        }
        return null;
    }

    /**
     * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device
     * type. Note: currently this method only returns a valid device for A2DP and BLE devices.
     *
     * @param ada attributes of device to match
     * @param canonicalDeviceType external device type to match
     * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or
     *         {@code null} otherwise.
     */
    @Nullable
    AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
            int canonicalDeviceType) {
        final boolean isWireless = isBluetoothDevice(ada.getInternalType());
        synchronized (mDeviceInventoryLock) {
            for (AdiDeviceState deviceSetting : mDeviceInventory) {
                if (deviceSetting.getDeviceType() == canonicalDeviceType
            for (AdiDeviceState deviceState : mDeviceInventory.values()) {
                if (deviceState.getDeviceType() == canonicalDeviceType
                        && (!isWireless || ada.getAddress().equals(
                        deviceSetting.getDeviceAddress()))) {
                    return deviceSetting;
                        deviceState.getDeviceAddress()))) {
                    return deviceState;
                }
            }
        }
        return null;
    }

    /** Clears all cached {@link AdiDeviceState}'s. */
    void clearDeviceInventory() {
        synchronized (mDeviceInventoryLock) {
            mDeviceInventory.clear();
@@ -384,7 +448,7 @@ public class AudioDeviceInventory {
                    +  " role:" + key.second + " devices:" + devices); });
        pw.println("\ndevices:\n");
        synchronized (mDeviceInventoryLock) {
            for (AdiDeviceState device : mDeviceInventory) {
            for (AdiDeviceState device : mDeviceInventory.values()) {
                pw.println("\t" + device + "\n");
            }
        }
@@ -1232,11 +1296,11 @@ public class AudioDeviceInventory {
            AudioDeviceInfo[] connectedDevices = AudioManager.getDevicesStatic(
                    AudioManager.GET_DEVICES_ALL);

            Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole =
            Iterator<Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole =
                    rolesMap.entrySet().iterator();

            while (itRole.hasNext()) {
                Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry =
                Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry =
                        itRole.next();
                Pair<Integer, Integer> keyRole = entry.getKey();
                Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator();
@@ -2423,20 +2487,21 @@ public class AudioDeviceInventory {
        int deviceCatalogSize = 0;
        synchronized (mDeviceInventoryLock) {
            deviceCatalogSize = mDeviceInventory.size();
        }

            final StringBuilder settingsBuilder = new StringBuilder(
                            deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());

        synchronized (mDeviceInventoryLock) {
            for (int i = 0; i < mDeviceInventory.size(); i++) {
                settingsBuilder.append(mDeviceInventory.get(i).toPersistableString());
                if (i != mDeviceInventory.size() - 1) {
                    settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
                }
            Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator();
            if (iterator.hasNext()) {
                settingsBuilder.append(iterator.next().toPersistableString());
            }
            while (iterator.hasNext()) {
                settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
                settingsBuilder.append(iterator.next().toPersistableString());
            }
            return settingsBuilder.toString();
        }
    }

    /*package*/ void setDeviceSettings(String settings) {
        clearDeviceInventory();
@@ -2448,7 +2513,8 @@ public class AudioDeviceInventory {
            // Note if the device is not compatible with spatialization mode or the device
            // type is not canonical, it will be ignored in {@link SpatializerHelper}.
            if (devState != null) {
                addDeviceStateToInventory(devState);
                addOrUpdateDeviceSAStateInInventory(devState);
                addOrUpdateAudioDeviceCategoryInInventory(devState);
            }
        }
    }
Loading