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

Commit 17a3ac95 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "[le audio] Broadcast blocks mcp operation device to become active automatically" into main

parents 372c4f4d dd712113
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -119,4 +119,6 @@ oneway interface IBluetoothLeAudio {
    void getMaximumStreamsPerBroadcast(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT})")
    void getMaximumSubgroupsPerBroadcast(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void isBroadcastActive(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
}
+25 −0
Original line number Diff line number Diff line
@@ -1077,6 +1077,15 @@ public class LeAudioService extends ProfileService {
                .collect(Collectors.toList());
    }

    /**
     * Check if broadcast is active
     *
     * @return true if there is active broadcast, false otherwise
     */
    public boolean isBroadcastActive() {
        return !mBroadcastDescriptors.isEmpty();
    }

    /**
     * Get the maximum number of supported simultaneous broadcasts.
     * @return number of supported simultaneous broadcasts
@@ -4464,6 +4473,22 @@ public class LeAudioService extends ProfileService {
            enforceBluetoothPrivilegedPermission(service);
            service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
        }

        @Override
        public void isBroadcastActive(
                AttributionSource source, SynchronousResultReceiver receiver) {
            try {
                boolean result = false;
                LeAudioService service = getService(source);
                if (service != null) {
                    enforceBluetoothPrivilegedPermission(service);
                    result = service.isBroadcastActive();
                }
                receiver.send(result);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }
    }

    @Override
+21 −4
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.bluetooth.BluetoothEventLogger;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
@@ -1222,18 +1223,20 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
                        + " request up");

        // TODO: Activate/deactivate devices with ActiveDeviceManager
        if (req.getOpcode() == Request.Opcodes.PLAY) {
        if (mLeAudioService == null) {
            mLeAudioService = LeAudioService.getLeAudioService();
        }
        if (!isBroadcastActive() && req.getOpcode() == Request.Opcodes.PLAY) {
            if (mAdapterService.getActiveDevices(BluetoothProfile.A2DP).size() > 0) {
                A2dpService.getA2dpService().removeActiveDevice(false);
            }
            if (mAdapterService.getActiveDevices(BluetoothProfile.HEARING_AID).size() > 0) {
                HearingAidService.getHearingAidService().removeActiveDevice(false);
            }
            if (mLeAudioService == null) {
                mLeAudioService = LeAudioService.getLeAudioService();
            }
            if (mLeAudioService != null) {
                mLeAudioService.setActiveDevice(device);
            }
        }
        mCallbacks.onMediaControlRequest(req);

        return BluetoothGatt.GATT_SUCCESS;
@@ -2053,6 +2056,20 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
        return (mFeatures & featureBit) != 0;
    }

    /**
     * Checks if le audio broadcasting is ON
     *
     * @return {@code true} if is broadcasting audio, {@code false} otherwise
     */
    private boolean isBroadcastActive() {
        if (!Flags.leaudioBroadcastFeatureSupport()) {
            // disable this if feature flag is false
            return false;
        }

        return mLeAudioService != null && mLeAudioService.isBroadcastActive();
    }

    @VisibleForTesting
    boolean isOpcodeSupported(int opcode) {
        if (opcode < Request.Opcodes.PLAY || opcode > Request.Opcodes.GOTO_GROUP) {
+46 −0
Original line number Diff line number Diff line
@@ -510,6 +510,52 @@ public class LeAudioBroadcastServiceTest {
        Assert.assertEquals(meta_list.get(0), state_event.broadcastMetadata);
    }

    @Test
    public void testIsBroadcastActive() {
        int broadcastId = 243;
        byte[] code = {0x00, 0x01, 0x00, 0x02};

        BluetoothLeAudioContentMetadata.Builder meta_builder =
                new BluetoothLeAudioContentMetadata.Builder();
        meta_builder.setLanguage("ENG");
        meta_builder.setProgramInfo("Public broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code, 1));

        LeAudioStackEvent create_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
        create_event.valueInt1 = broadcastId;
        create_event.valueBool1 = true;
        mService.messageFromNative(create_event);

        // Inject metadata stack event and verify if getter API works as expected
        LeAudioStackEvent state_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED);
        state_event.valueInt1 = broadcastId;
        state_event.broadcastMetadata = createBroadcastMetadata();
        mService.messageFromNative(state_event);

        // Verify if broadcast is active
        Assert.assertTrue(mService.isBroadcastActive());

        mService.stopBroadcast(broadcastId);
        verify(mLeAudioBroadcasterNativeInterface, times(1)).stopBroadcast(eq(broadcastId));

        state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
        state_event.valueInt1 = broadcastId;
        state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_STOPPED;
        mService.messageFromNative(state_event);

        verify(mLeAudioBroadcasterNativeInterface, times(1)).destroyBroadcast(eq(broadcastId));

        state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED);
        state_event.valueInt1 = broadcastId;
        mService.messageFromNative(state_event);

        // Verify if broadcast is not active
        Assert.assertFalse(mService.isBroadcastActive());
    }

    private void verifyConnectionStateIntent(
            int timeoutMs, BluetoothDevice device, int newState, int prevState) {
        Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue);
+11 −1
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsEnabled;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -34,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.le_audio.LeAudioService;

import org.junit.After;
@@ -939,13 +942,20 @@ public class MediaControlGattServiceTest {
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_LEAUDIO_BROADCAST_FEATURE_SUPPORT)
    public void testMediaControlPointeRequest_OpcodePlayCallLeAudioServiceSetActiveDevice() {
        BluetoothGattService service = initAllFeaturesGattService();
        prepareConnectedDevice();
        mMcpService.updateSupportedOpcodesChar(Request.SupportedOpcodes.PLAY, true);
        verifyMediaControlPointRequest(service, Request.Opcodes.PLAY, null,
                BluetoothGatt.GATT_SUCCESS, 1);
        if (!Flags.leaudioBroadcastFeatureSupport()) {
            verify(mMockLeAudioService).setActiveDevice(any(BluetoothDevice.class));
        } else {
            final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
            when(mMockLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
            verify(mMockMcsCallbacks, times(1)).onMediaControlRequest(any(Request.class));
        }
    }

    @Test