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

Commit 646c6475 authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk Committed by Grzegorz Kolodziejczyk (xWF)
Browse files

le_audio: Introduce set/get BroadcastToUnicastFallbackGroup API

This CL extends the BluetoothLeAudio API to allow setting and getting a
fallback Broadcast to Unicast group.

Bug: 375421718
Bug: 375422410
Flag: com.android.bluetooth.flags.leaudio_broadcast_api_manage_primary_group
Test: atest BluetoothLeAudioTest
Change-Id: Ic0d64282b141c523b1813d2df1ae535bfbacf1c8
parent 37de3cb8
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);
+110 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ 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.leaudioBroadcastApiManagePrimaryGroup;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiManagePrimaryGroup;
import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener;
@@ -5231,6 +5232,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 +5995,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);
+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);
+73 −0
Original line number Diff line number Diff line
@@ -1430,4 +1430,77 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
            }
        }
    }

    /**
     * Sets broadcast to unicast fallback group.
     *
     * <p>In broadcast handover situations where unicast is unavailable, this group acts as the
     * fallback.
     *
     * <p>A handover can occur when ongoing broadcast is interrupted with unicast streaming request.
     *
     * <p>On fallback group changed, {@link Callback#onBroadcastToUnicastFallbackGroupChanged} will
     * be invoked.
     *
     * @param groupId the ID of the group to switch to if unicast fails during a broadcast handover,
     *     {@link #GROUP_ID_INVALID} when there should be no such fallback group.
     * @see BluetoothLeAudio#getGroupId()
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP)
    @SystemApi
    @RequiresBluetoothConnectPermission
    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
    public void setBroadcastToUnicastFallbackGroup(int groupId) {
        if (DBG) Log.d(TAG, "setBroadcastToUnicastFallbackGroup(" + groupId + ")");

        final IBluetoothLeAudio service = getService();

        if (service == null) {
            Log.w(TAG, "Proxy not attached to service");
            if (DBG) log(Log.getStackTraceString(new Throwable()));
        } else if (mAdapter.isEnabled()) {
            try {
                service.setBroadcastToUnicastFallbackGroup(groupId, mAttributionSource);
            } catch (RemoteException e) {
                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
            }
        }
    }

    /**
     * Gets broadcast to unicast fallback group.
     *
     * <p>In broadcast handover situations where unicast is unavailable, this group acts as the
     * fallback.
     *
     * <p>A broadcast handover can occur when a {@link BluetoothLeBroadcast#startBroadcast} call is
     * successful and there's an active unicast group.
     *
     * @return groupId the ID of the fallback group, {@link #GROUP_ID_INVALID} when adapter is
     *     disabled
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP)
    @SystemApi
    @RequiresBluetoothConnectPermission
    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
    public int getBroadcastToUnicastFallbackGroup() {
        if (DBG) Log.d(TAG, "getBroadcastToUnicastFallbackGroup()");

        final IBluetoothLeAudio service = getService();

        if (service == null) {
            Log.w(TAG, "Proxy not attached to service");
            if (DBG) log(Log.getStackTraceString(new Throwable()));
        } else if (mAdapter.isEnabled()) {
            try {
                return service.getBroadcastToUnicastFallbackGroup(mAttributionSource);
            } catch (RemoteException e) {
                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
            }
        }

        return GROUP_ID_INVALID;
    }
}