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

Commit 8bc28f86 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "AudioService: add support for Bluetooth LE audio codec selection" into main

parents e7ca644d f85dddf6
Loading
Loading
Loading
Loading
+22 −3
Original line number Diff line number Diff line
@@ -318,11 +318,12 @@ public class AudioSystem

    /**
     * @hide
     * Convert a Bluetooth codec to an audio format enum
     * Convert an A2DP Bluetooth codec to an audio format enum
     * @param btCodec the codec to convert.
     * @return the audio format, or {@link #AUDIO_FORMAT_DEFAULT} if unknown
     */
    public static @AudioFormatNativeEnumForBtCodec int bluetoothCodecToAudioFormat(int btCodec) {
    public static @AudioFormatNativeEnumForBtCodec int bluetoothA2dpCodecToAudioFormat(
            int btCodec) {
        switch (btCodec) {
            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
                return AudioSystem.AUDIO_FORMAT_SBC;
@@ -339,7 +340,25 @@ public class AudioSystem
            case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
                return AudioSystem.AUDIO_FORMAT_OPUS;
            default:
                Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
                Log.e(TAG, "Unknown A2DP BT codec 0x" + Integer.toHexString(btCodec)
                        + " for conversion to audio format");
                // TODO returning DEFAULT is the current behavior, should this return INVALID?
                return AudioSystem.AUDIO_FORMAT_DEFAULT;
        }
    }

    /**
     * @hide
     * Convert a LE Audio Bluetooth codec to an audio format enum
     * @param btCodec the codec to convert.
     * @return the audio format, or {@link #AUDIO_FORMAT_DEFAULT} if unknown
     */
    public static @AudioFormatNativeEnumForBtCodec int bluetoothLeCodecToAudioFormat(int btCodec) {
        switch (btCodec) {
            case BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3:
                return AudioSystem.AUDIO_FORMAT_LC3;
            default:
                Log.e(TAG, "Unknown LE Audio BT codec 0x" + Integer.toHexString(btCodec)
                        + " for conversion to audio format");
                // TODO returning DEFAULT is the current behavior, should this return INVALID?
                return AudioSystem.AUDIO_FORMAT_DEFAULT;
+15 −12
Original line number Diff line number Diff line
@@ -1595,8 +1595,8 @@ public class AudioDeviceBroker {
        sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
    }

    /*package*/ void setLeAudioTimeout(String address, int device, int delayMs) {
        sendILMsg(MSG_IL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, address, delayMs);
    /*package*/ void setLeAudioTimeout(String address, int device, int codec, int delayMs) {
        sendIILMsg(MSG_IIL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, codec, address, delayMs);
    }

    /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
@@ -1794,8 +1794,9 @@ public class AudioDeviceBroker {
                                return;
                            }
                            @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
                                    mBtHelper.getA2dpCodecWithFallbackToSBC(
                                            btInfo.mDevice, "MSG_L_SET_BT_ACTIVE_DEVICE");
                                    mBtHelper.getCodecWithFallback(btInfo.mDevice,
                                            btInfo.mProfile, btInfo.mIsLeOutput,
                                            "MSG_L_SET_BT_ACTIVE_DEVICE");
                            mDeviceInventory.onSetBtActiveDevice(btInfo, codec,
                                    (btInfo.mProfile
                                            != BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
@@ -1819,22 +1820,24 @@ public class AudioDeviceBroker {
                case MSG_IL_BTA2DP_TIMEOUT:
                    // msg.obj  == address of BTA2DP device
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
                        mDeviceInventory.onMakeA2dpDeviceUnavailableNow(
                                (String) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_IL_BTLEAUDIO_TIMEOUT:
                case MSG_IIL_BTLEAUDIO_TIMEOUT:
                    // msg.obj  == address of LE Audio device
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onMakeLeAudioDeviceUnavailableNow(
                                (String) msg.obj, msg.arg1);
                                (String) msg.obj, msg.arg1, msg.arg2);
                    }
                    break;
                case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: {
                    final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
                    synchronized (mDeviceStateLock) {
                        @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
                                mBtHelper.getA2dpCodecWithFallbackToSBC(
                                        btInfo.mDevice, "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
                                mBtHelper.getCodecWithFallback(btInfo.mDevice,
                                        btInfo.mProfile, btInfo.mIsLeOutput,
                                        "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
                        mDeviceInventory.onBluetoothDeviceConfigChange(
                                btInfo, codec, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
                    }
@@ -2084,7 +2087,7 @@ public class AudioDeviceBroker {

    private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
    private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
    private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
    private static final int MSG_IIL_BTLEAUDIO_TIMEOUT = 49;

    private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
    private static final int MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL = 53;
@@ -2104,7 +2107,7 @@ public class AudioDeviceBroker {
            case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
            case MSG_L_SET_BT_ACTIVE_DEVICE:
            case MSG_IL_BTA2DP_TIMEOUT:
            case MSG_IL_BTLEAUDIO_TIMEOUT:
            case MSG_IIL_BTLEAUDIO_TIMEOUT:
            case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
            case MSG_TOGGLE_HDMI:
            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
@@ -2196,7 +2199,7 @@ public class AudioDeviceBroker {
                case MSG_L_SET_BT_ACTIVE_DEVICE:
                case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
                case MSG_IL_BTA2DP_TIMEOUT:
                case MSG_IL_BTLEAUDIO_TIMEOUT:
                case MSG_IIL_BTLEAUDIO_TIMEOUT:
                case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
                    if (sLastDeviceConnectMsgTime >= time) {
                        // add a little delay to make sure messages are ordered as expected
+26 −19
Original line number Diff line number Diff line
@@ -689,9 +689,11 @@ public class AudioDeviceInventory {
                case BluetoothProfile.LE_AUDIO:
                case BluetoothProfile.LE_AUDIO_BROADCAST:
                    if (switchToUnavailable) {
                        makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
                        makeLeAudioDeviceUnavailableNow(address,
                                btInfo.mAudioSystemDevice, di.mDeviceCodecFormat);
                    } else if (switchToAvailable) {
                        makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice");
                        makeLeAudioDeviceAvailable(
                                btInfo, streamType, codec, "onSetBtActiveDevice");
                    }
                    break;
                default: throw new IllegalArgumentException("Invalid profile "
@@ -752,12 +754,13 @@ public class AudioDeviceInventory {


            if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
                boolean a2dpCodecChange = false;
                if (btInfo.mProfile == BluetoothProfile.A2DP) {
                boolean codecChange = false;
                if (btInfo.mProfile == BluetoothProfile.A2DP
                        || btInfo.mProfile == BluetoothProfile.LE_AUDIO) {
                    if (di.mDeviceCodecFormat != codec) {
                        di.mDeviceCodecFormat = codec;
                        mConnectedDevices.replace(key, di);
                        a2dpCodecChange = true;
                        codecChange = true;
                    }
                    final int res = mAudioSystem.handleDeviceConfigChange(
                            btInfo.mAudioSystemDevice, address, BtHelper.getName(btDevice), codec);
@@ -782,7 +785,7 @@ public class AudioDeviceInventory {

                    }
                }
                if (!a2dpCodecChange) {
                if (!codecChange) {
                    updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/);
                }
            }
@@ -796,9 +799,9 @@ public class AudioDeviceInventory {
        }
    }

    /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device) {
    /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device, int codec) {
        synchronized (mDevicesLock) {
            makeLeAudioDeviceUnavailableNow(address, device);
            makeLeAudioDeviceUnavailableNow(address, device, codec);
        }
    }

@@ -1657,11 +1660,12 @@ public class AudioDeviceInventory {
        }

        synchronized (mDevicesLock) {
            final ArraySet<String> toRemove = new ArraySet<>();
            final ArraySet<Pair<String, Integer>> toRemove = new ArraySet<>();
            // Disconnect ALL DEVICE_OUT_BLE_HEADSET or DEVICE_OUT_BLE_BROADCAST devices
            mConnectedDevices.values().forEach(deviceInfo -> {
                if (deviceInfo.mDeviceType == device) {
                    toRemove.add(deviceInfo.mDeviceAddress);
                    toRemove.add(
                            new Pair<>(deviceInfo.mDeviceAddress, deviceInfo.mDeviceCodecFormat));
                }
            });
            new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
@@ -1671,8 +1675,8 @@ public class AudioDeviceInventory {
                final int delay = checkSendBecomingNoisyIntentInt(device,
                        AudioService.CONNECTION_STATE_DISCONNECTED,
                        AudioSystem.DEVICE_NONE);
                toRemove.stream().forEach(deviceAddress ->
                        makeLeAudioDeviceUnavailableLater(deviceAddress, device, delay)
                toRemove.stream().forEach(entry ->
                        makeLeAudioDeviceUnavailableLater(entry.first, device, entry.second, delay)
                );
            }
        }
@@ -2216,7 +2220,8 @@ public class AudioDeviceInventory {

    @GuardedBy("mDevicesLock")
    private void makeLeAudioDeviceAvailable(
            AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, String eventSource) {
            AudioDeviceBroker.BtDeviceInfo btInfo, int streamType,
            @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, String eventSource) {
        final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10;
        final int device = btInfo.mAudioSystemDevice;

@@ -2250,7 +2255,7 @@ public class AudioDeviceInventory {

            AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
            final int res = AudioSystem.setDeviceConnectionState(ada,
                    AudioSystem.DEVICE_STATE_AVAILABLE,  AudioSystem.AUDIO_FORMAT_DEFAULT);
                    AudioSystem.DEVICE_STATE_AVAILABLE, codec);
            if (res != AudioSystem.AUDIO_STATUS_OK) {
                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                        "APM failed to make available LE Audio device addr=" + address
@@ -2265,7 +2270,7 @@ public class AudioDeviceInventory {
            // Reset LEA suspend state each time a new sink is connected
            mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
            mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                    new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
                    new DeviceInfo(device, name, address, codec,
                            peerAddress, groupId));
            mDeviceBroker.postAccessoryPlugMediaUnmute(device);
            setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
@@ -2288,13 +2293,14 @@ public class AudioDeviceInventory {
    }

    @GuardedBy("mDevicesLock")
    private void makeLeAudioDeviceUnavailableNow(String address, int device) {
    private void makeLeAudioDeviceUnavailableNow(String address, int device,
            @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) {
        AudioDeviceAttributes ada = null;
        if (device != AudioSystem.DEVICE_NONE) {
            ada = new AudioDeviceAttributes(device, address);
            final int res = AudioSystem.setDeviceConnectionState(ada,
                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
                    AudioSystem.AUDIO_FORMAT_DEFAULT);
                    codec);

            if (res != AudioSystem.AUDIO_STATUS_OK) {
                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
@@ -2319,7 +2325,8 @@ public class AudioDeviceInventory {
    }

    @GuardedBy("mDevicesLock")
    private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
    private void makeLeAudioDeviceUnavailableLater(
            String address, int device, int codec, int delayMs) {
        // prevent any activity on the LEA output to avoid unwanted
        // reconnection of the sink.
        mDeviceBroker.setLeAudioSuspended(
@@ -2327,7 +2334,7 @@ public class AudioDeviceInventory {
        // the device will be made unavailable later, so consider it disconnected right away
        mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
        // send the delayed message to make the device unavailable later
        mDeviceBroker.setLeAudioTimeout(address, device, delayMs);
        mDeviceBroker.setLeAudioTimeout(address, device, codec, delayMs);
    }

    @GuardedBy("mDevicesLock")
+62 −23
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -250,8 +251,10 @@ public class BtHelper {
        }
    }

    /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
            @NonNull BluetoothDevice device) {
    /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec(
            @NonNull BluetoothDevice device, @AudioService.BtProfile int profile) {
        switch (profile) {
            case BluetoothProfile.A2DP: {
                if (mA2dp == null) {
                    return AudioSystem.AUDIO_FORMAT_DEFAULT;
                }
@@ -268,17 +271,53 @@ public class BtHelper {
                if (btCodecConfig == null) {
                    return AudioSystem.AUDIO_FORMAT_DEFAULT;
                }
        return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
                return AudioSystem.bluetoothA2dpCodecToAudioFormat(btCodecConfig.getCodecType());
            }
            case BluetoothProfile.LE_AUDIO: {
                if (mLeAudio == null) {
                    return AudioSystem.AUDIO_FORMAT_DEFAULT;
                }
                BluetoothLeAudioCodecStatus btLeCodecStatus = null;
                int groupId = mLeAudio.getGroupId(device);
                try {
                    btLeCodecStatus = mLeAudio.getCodecStatus(groupId);
                } catch (Exception e) {
                    Log.e(TAG, "Exception while getting status of " + device, e);
                }
                if (btLeCodecStatus == null) {
                    return AudioSystem.AUDIO_FORMAT_DEFAULT;
                }
                BluetoothLeAudioCodecConfig btLeCodecConfig =
                        btLeCodecStatus.getOutputCodecConfig();
                if (btLeCodecConfig == null) {
                    return AudioSystem.AUDIO_FORMAT_DEFAULT;
                }
                return AudioSystem.bluetoothLeCodecToAudioFormat(btLeCodecConfig.getCodecType());
            }
            default:
                return AudioSystem.AUDIO_FORMAT_DEFAULT;
        }
    }

    /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec
            int getA2dpCodecWithFallbackToSBC(
                    @NonNull BluetoothDevice device, @NonNull String source) {
        @AudioSystem.AudioFormatNativeEnumForBtCodec int codec = getA2dpCodec(device);
            int getCodecWithFallback(
                    @NonNull BluetoothDevice device, @AudioService.BtProfile int profile,
                    boolean isLeOutput, @NonNull String source) {
        // For profiles other than A2DP and LE Audio output, the audio codec format must be
        // AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format
        // only if audio HW module selection based on format is supported for the device type.
        if (!(profile == BluetoothProfile.A2DP
                || (profile == BluetoothProfile.LE_AUDIO && isLeOutput))) {
            return AudioSystem.AUDIO_FORMAT_DEFAULT;
        }
        @AudioSystem.AudioFormatNativeEnumForBtCodec int codec =
                getCodec(device, profile);
        if (codec == AudioSystem.AUDIO_FORMAT_DEFAULT) {
            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                    "getA2dpCodec DEFAULT from " + source + " fallback to SBC"));
            return AudioSystem.AUDIO_FORMAT_SBC;
                    "getCodec DEFAULT from " + source + " fallback to "
                            + (profile == BluetoothProfile.A2DP ? "SBC" : "LC3")));
            return profile == BluetoothProfile.A2DP
                    ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3;
        }
        return codec;
    }