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

Commit 58f01a47 authored by Łukasz Rymanowski (xWF)'s avatar Łukasz Rymanowski (xWF) Committed by Gerrit Code Review
Browse files

Merge changes from topic "broadcast_to_unicast_fallback_group_api" into main

* changes:
  le_audio: Implement default setter for fallback to unicast group
  le_audio: Introduce set/get BroadcastToUnicastFallbackGroup API
parents 99ce6458 9bb69005
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -60,6 +60,10 @@ interface IBluetoothLeAudio {
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void setCodecConfigPreference(in int groupId, in BluetoothLeAudioCodecConfig inputCodecConfig, in BluetoothLeAudioCodecConfig outputCodecConfig, in AttributionSource source);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void setBroadcastToUnicastFallbackGroup(in int groupId, in AttributionSource attributionSource);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    int getBroadcastToUnicastFallbackGroup(in AttributionSource attributionSource);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    oneway void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    oneway void unregisterCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource);
+176 −7
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static com.android.bluetooth.bass_client.BassConstants.INVALID_BROADCAST_ID;
import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiManagePrimaryGroup;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;

@@ -843,6 +843,35 @@ public class LeAudioService extends ProfileService {
        mNativeInterface.setEnableState(device, enabled);
    }

    private void setDefaultBroadcastToUnicastFallbackGroup() {
        DatabaseManager dbManager = mAdapterService.getDatabase();
        if (dbManager == null) {
            Log.i(
                    TAG,
                    "Can't get db manager to pick default Broadcast to Unicast fallback group"
                            + ", leaving: "
                            + mUnicastGroupIdDeactivatedForBroadcastTransition);
            return;
        }

        List<BluetoothDevice> devices = dbManager.getMostRecentlyConnectedDevices();

        int targetDeviceIdx = -1;
        int targetGroupId = LE_AUDIO_GROUP_ID_INVALID;
        for (BluetoothDevice device : getConnectedDevices()) {
            LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device);
            if (devices.contains(device)) {
                int idx = devices.indexOf(device);
                if (idx > targetDeviceIdx) {
                    targetDeviceIdx = idx;
                    targetGroupId = descriptor.mGroupId;
                }
            }
        }

        updateFallbackUnicastGroupIdForBroadcast(targetGroupId);
    }

    public boolean connect(BluetoothDevice device) {
        Log.d(TAG, "connect(): " + device);

@@ -2417,7 +2446,8 @@ public class LeAudioService extends ProfileService {
                        + ", mExposedActiveDevice: "
                        + mExposedActiveDevice);

        if (isBroadcastActive()
        if (!Flags.leaudioBroadcastPrimaryGroupSelection()
                && isBroadcastActive()
                && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID
                && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) {

@@ -3205,7 +3235,9 @@ public class LeAudioService extends ProfileService {
                    TAG,
                    "transitionFromBroadcastToUnicast: No valid unicast device for group ID: "
                            + mUnicastGroupIdDeactivatedForBroadcastTransition);
            if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
                updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
            }
            updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false);
            return;
        }
@@ -3217,7 +3249,9 @@ public class LeAudioService extends ProfileService {
                        + ", with device: "
                        + unicastDevice);

        if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
            updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
        }
        setActiveDevice(unicastDevice);
    }

@@ -3705,7 +3739,9 @@ public class LeAudioService extends ProfileService {
                        if (isBroadcastAllowedToBeActivateInCurrentAudioMode()) {
                            /* Check if broadcast was deactivated due to unicast */
                            if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) {
                                if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
                                    updateFallbackUnicastGroupIdForBroadcast(groupId);
                                }
                                if (!leaudioUseAudioModeListener()) {
                                    mQueuedInCallValue = Optional.empty();
                                }
@@ -3722,7 +3758,9 @@ public class LeAudioService extends ProfileService {
                                }
                            } else {
                                if (!mCreateBroadcastQueue.isEmpty()) {
                                    if (!Flags.leaudioBroadcastPrimaryGroupSelection()) {
                                        updateFallbackUnicastGroupIdForBroadcast(groupId);
                                    }
                                    BluetoothLeBroadcastSettings settings =
                                            mCreateBroadcastQueue.remove();
                                    createBroadcast(settings);
@@ -4181,6 +4219,12 @@ public class LeAudioService extends ProfileService {
        if (!isScannerNeeded()) {
            stopAudioServersBackgroundScan();
        }

        /* Set by default earliest connected device */
        if (Flags.leaudioBroadcastPrimaryGroupSelection()
                && mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) {
            setDefaultBroadcastToUnicastFallbackGroup();
        }
    }

    /** Process a change for disconnection of a device. */
@@ -4246,6 +4290,12 @@ public class LeAudioService extends ProfileService {
                            false);
                    return;
                }

                /* Set by default earliest connected device */
                if (Flags.leaudioBroadcastPrimaryGroupSelection()
                        && mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
                    setDefaultBroadcastToUnicastFallbackGroup();
                }
            }

            if (descriptor.isActive()
@@ -4840,6 +4890,12 @@ public class LeAudioService extends ProfileService {
            mGroupWriteLock.unlock();
        }

        /* Set by default earliest connected device */
        if (Flags.leaudioBroadcastPrimaryGroupSelection()
                && mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) {
            setDefaultBroadcastToUnicastFallbackGroup();
        }

        if (mBluetoothEnabled) {
            setAuthorizationForRelatedProfiles(device, true);
            startAudioServersBackgroundScan(/* retry= */ false);
@@ -4905,9 +4961,13 @@ public class LeAudioService extends ProfileService {
                }

                if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
                    if (Flags.leaudioBroadcastPrimaryGroupSelection()) {
                        setDefaultBroadcastToUnicastFallbackGroup();
                    } else {
                        updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID);
                    }
                }
            }
            mHandler.post(() -> notifyGroupNodeRemoved(device, groupId));
        } finally {
            mGroupReadLock.unlock();
@@ -5231,6 +5291,93 @@ public class LeAudioService extends ProfileService {
        mNativeInterface.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
    }

    void setBroadcastToUnicastFallbackGroup(int groupId) {
        if (!leaudioBroadcastApiManagePrimaryGroup()) {
            return;
        }

        Log.d(TAG, "setBroadcastToUnicastFallbackGroup(" + groupId + ")");

        if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
            Log.d(TAG, "Requested Broadcast to Unicast fallback group is already set");
            return;
        }

        mGroupReadLock.lock();
        try {
            LeAudioGroupDescriptor oldFallbackGroupDescriptor =
                    getGroupDescriptor(mUnicastGroupIdDeactivatedForBroadcastTransition);
            LeAudioGroupDescriptor newFallbackGroupDescriptor = getGroupDescriptor(groupId);
            if (oldFallbackGroupDescriptor == null && newFallbackGroupDescriptor == null) {
                Log.w(
                        TAG,
                        "Failed to set Broadcast to Unicast Fallback group "
                                + "(lack of new and old group descriptors): "
                                + mUnicastGroupIdDeactivatedForBroadcastTransition
                                + " -> "
                                + groupId);
                return;
            }

            /* Fallback group should be not updated if new group is not connected or requested
             * group ID is different than INVALID but there is no such descriptor.
             */
            if (groupId != LE_AUDIO_GROUP_ID_INVALID
                    && (newFallbackGroupDescriptor == null
                            || !newFallbackGroupDescriptor.mIsConnected)) {
                Log.w(
                        TAG,
                        "Failed to set Broadcast to Unicast Fallback group (invalid new group): "
                                + mUnicastGroupIdDeactivatedForBroadcastTransition
                                + " -> "
                                + groupId);
                return;
            }

            /* Update exposed monitoring input device while being in Broadcast mode */
            if (isBroadcastActive()
                    && getActiveGroupId() == LE_AUDIO_GROUP_ID_INVALID
                    && mUnicastGroupIdDeactivatedForBroadcastTransition
                            != LE_AUDIO_GROUP_ID_INVALID) {
                /* In case of removing fallback unicast group, monitoring input
                 * device should be removed from active devices.
                 */
                int newDirection = AUDIO_DIRECTION_NONE;
                int oldDirection = oldFallbackGroupDescriptor != null
                        ? oldFallbackGroupDescriptor.mDirection : AUDIO_DIRECTION_NONE;
                boolean notifyAndUpdateInactiveOutDeviceOnly = false;
                boolean hasFallbackDeviceWhenGettingInactive = oldFallbackGroupDescriptor != null
                        ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive
                        : false;
                if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
                    newDirection = AUDIO_DIRECTION_INPUT_BIT;
                    notifyAndUpdateInactiveOutDeviceOnly = true;
                }
                updateActiveDevices(
                        groupId,
                        oldDirection,
                        newDirection,
                        false, // isActive
                        hasFallbackDeviceWhenGettingInactive,
                        notifyAndUpdateInactiveOutDeviceOnly);
            }
        } finally {
            mGroupReadLock.unlock();
        }

        updateFallbackUnicastGroupIdForBroadcast(groupId);
    }

    int getBroadcastToUnicastFallbackGroup() {
        if (!leaudioBroadcastApiManagePrimaryGroup()) {
            return LE_AUDIO_GROUP_ID_INVALID;
        }

        Log.v(TAG, "getBroadcastToUnicastFallbackGroup()");

        return mUnicastGroupIdDeactivatedForBroadcastTransition;
    }

    /**
     * Checks if the remote device supports LE Audio duplex (output and input).
     *
@@ -5907,6 +6054,28 @@ public class LeAudioService extends ProfileService {
            service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
        }

        @Override
        public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) {
            LeAudioService service = getServiceAndEnforceConnect(source);
            if (service == null) {
                return;
            }

            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
            service.setBroadcastToUnicastFallbackGroup(groupId);
        }

        @Override
        public int getBroadcastToUnicastFallbackGroup(AttributionSource source) {
            LeAudioService service = getServiceAndEnforceConnect(source);
            if (service == null) {
                return LE_AUDIO_GROUP_ID_INVALID;
            }

            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
            return service.getBroadcastToUnicastFallbackGroup();
        }

        @Override
        public boolean isBroadcastActive(AttributionSource source) {
            LeAudioService service = getServiceAndEnforceConnect(source);
+46 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

@@ -66,6 +67,7 @@ import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeoutException;
@@ -1564,6 +1566,7 @@ public class LeAudioBroadcastServiceTest {
    }

    @Test
    @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION)
    public void testUpdateFallbackInputDevice() {
        mSetFlagsRule.disableFlags(Flags.FLAG_LEAUDIO_USE_AUDIO_MODE_LISTENER);
        int groupId = 1;
@@ -1658,6 +1661,49 @@ public class LeAudioBroadcastServiceTest {
        }
    }

    @Test
    @EnableFlags({
        Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION,
        Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP,
        Flags.FLAG_LEAUDIO_USE_AUDIO_MODE_LISTENER
    })
    public void testManageBroadcastToUnicastFallbackGroup() {
        int groupId = 1;
        int groupId2 = 2;
        int broadcastId = 243;
        byte[] code = {0x00, 0x01, 0x00, 0x02};
        List<BluetoothDevice> devices = new ArrayList<>();

        when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);

        initializeNative();
        devices.add(mDevice);
        prepareHandoverStreamingBroadcast(groupId, broadcastId, code);
        mService.deviceConnected(mDevice);
        devices.add(mDevice2);
        prepareConnectedUnicastDevice(groupId2, mDevice2);
        mService.deviceConnected(mDevice2);

        Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId);

        reset(mAudioManager);

        /* Update fallback active device (only input is active) */
        ArgumentCaptor<BluetoothProfileConnectionInfo> connectionInfoArgumentCaptor =
                ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class);

        mService.setBroadcastToUnicastFallbackGroup(groupId2);

        verify(mAudioManager)
                .handleBluetoothActiveDeviceChanged(
                        eq(mDevice2), eq(mDevice), connectionInfoArgumentCaptor.capture());
        List<BluetoothProfileConnectionInfo> connInfos =
                connectionInfoArgumentCaptor.getAllValues();
        Assert.assertEquals(connInfos.size(), 1);
        Assert.assertFalse(connInfos.get(0).isLeOutput());
        Assert.assertEquals(mService.getBroadcastToUnicastFallbackGroup(), groupId2);
    }

    private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
            BluetoothLeAudioContentMetadata contentMetadata,
            @Nullable byte[] broadcastCode,
+80 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.sysprop.BluetoothProperties;
@@ -1826,6 +1827,7 @@ public class LeAudioServiceTest {

    /** Test update unicast fallback active group when broadcast is ongoing */
    @Test
    @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION)
    public void testUpdateUnicastFallbackActiveDeviceGroupDuringBroadcast() {
        int groupId = 1;
        int preGroupId = 2;
@@ -3639,4 +3641,82 @@ public class LeAudioServiceTest {
                .setGroupAllowedContextMask(
                        groupId, BluetoothLeAudio.CONTEXTS_ALL, BluetoothLeAudio.CONTEXTS_ALL);
    }

    /** Test managing broadcast to unicast fallback group */
    @Test
    @EnableFlags({
        Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION,
        Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP
    })
    public void testManageBroadcastToUnicastFallbackGroup() {
        int firstGroupId = 1;
        int secondGroupId = 2;
        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
        int direction = 1;
        int snkAudioLocation = 3;
        int srcAudioLocation = 4;
        int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
        List<BluetoothDevice> devices = new ArrayList<>();

        when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);

        // Not connected device
        assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
        assertThat(mService.getBroadcastToUnicastFallbackGroup())
                .isEqualTo(BluetoothLeAudio.GROUP_ID_INVALID);

        // Connected device
        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        devices.add(mSingleDevice);
        connectTestDevice(mSingleDevice, testGroupId);

        // Group should be updated to default (earliest connected)
        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId);

        // Add location support
        LeAudioStackEvent audioConfChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
        audioConfChangedEvent.device = mSingleDevice;
        audioConfChangedEvent.valueInt1 = direction;
        audioConfChangedEvent.valueInt2 = firstGroupId;
        audioConfChangedEvent.valueInt3 = snkAudioLocation;
        audioConfChangedEvent.valueInt4 = srcAudioLocation;
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
        verify(mNativeInterface).groupSetActive(firstGroupId);

        // Set group and device as active
        LeAudioStackEvent groupStatusChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
        groupStatusChangedEvent.valueInt1 = firstGroupId;
        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
        mService.messageFromNative(groupStatusChangedEvent);

        // Set fallback group to not valid (not connected)
        mService.setBroadcastToUnicastFallbackGroup(secondGroupId);

        // Connect second device
        devices.add(mLeftDevice);
        connectTestDevice(mLeftDevice, secondGroupId);
        mService.deviceConnected(mLeftDevice);

        // Fallback device should remain earliest connected
        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId);

        // Set fallback group to valid second
        mService.setBroadcastToUnicastFallbackGroup(secondGroupId);

        // Fallback device should be changed to second
        assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(secondGroupId);

        // no active device
        assertThat(mService.removeActiveDevice(false)).isTrue();
        verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID);

        // Set group and device as inactive active
        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
        mService.messageFromNative(groupStatusChangedEvent);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -473,10 +473,12 @@ package android.bluetooth {

  public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
    method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_manage_primary_group") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getBroadcastToUnicastFallbackGroup();
    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeAudioCodecStatus getCodecStatus(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInbandRingtoneEnabled(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeAudio.Callback);
    method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_manage_primary_group") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setBroadcastToUnicastFallbackGroup(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setCodecConfigPreference(int, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@IntRange(from=0, to=255) int);
Loading