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

Commit 6f113fa1 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "[le audio] Update LE broadcast APIs to support Public Broadcast"

parents 5b563d21 94c889f3
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -872,6 +872,7 @@ jobject prepareBluetoothLeBroadcastMetadataObject(
      (jint)broadcast_metadata.adv_sid, (jint)broadcast_metadata.broadcast_id,
      (jint)broadcast_metadata.pa_interval,
      broadcast_metadata.broadcast_code ? true : false,
      false, nullptr,
      broadcast_metadata.broadcast_code ? code.get() : nullptr,
      (jint)broadcast_metadata.basic_audio_announcement.presentation_delay,
      subgroup_list_obj.get());
@@ -990,9 +991,10 @@ static void BroadcasterClassInitNative(JNIEnv* env, jclass clazz) {

  jclass jniBluetoothLeBroadcastMetadataClass =
      env->FindClass("android/bluetooth/BluetoothLeBroadcastMetadata");
  android_bluetooth_BluetoothLeBroadcastMetadata.constructor = env->GetMethodID(
      jniBluetoothLeBroadcastMetadataClass, "<init>",
      "(ILandroid/bluetooth/BluetoothDevice;IIIZ[BILjava/util/List;)V");
  android_bluetooth_BluetoothLeBroadcastMetadata.constructor =
      env->GetMethodID(jniBluetoothLeBroadcastMetadataClass, "<init>",
                       "(ILandroid/bluetooth/BluetoothDevice;IIIZZLjava/lang/"
                       "String;[BILjava/util/List;)V");
}

static void BroadcasterInitNative(JNIEnv* env, jobject object) {
+112 −24
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastSettings;
import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
@@ -727,14 +729,17 @@ public class LeAudioService extends ProfileService {
    }

    /**
     * Creates LeAudio Broadcast instance.
     * @param metadata metadata buffer with TLVs
     * Creates LeAudio Broadcast instance with BluetoothLeBroadcastSettings.
     *
     * @param broadcastSettings broadcast settings for this broadcast source
     */
    public void createBroadcast(BluetoothLeAudioContentMetadata metadata, byte[] broadcastCode) {
    public void createBroadcast(BluetoothLeBroadcastSettings broadcastSettings) {
        if (mLeAudioBroadcasterNativeInterface == null) {
            Log.w(TAG, "Native interface not available.");
            return;
        }

        byte[] broadcastCode = broadcastSettings.getBroadcastCode();
        boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0);
        if (isEncrypted) {
            if ((broadcastCode.length > 16) || (broadcastCode.length < 4)) {
@@ -742,10 +747,24 @@ public class LeAudioService extends ProfileService {
                return;
            }
        }

        Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));
        mLeAudioBroadcasterNativeInterface.createBroadcast(metadata.getRawMetadata(),
                broadcastCode);

        List<BluetoothLeBroadcastSubgroupSettings> settings =
                broadcastSettings.getSubgroupSettings();
        if (settings == null || settings.size() < 1) {
            Log.d(TAG, "subgroup settings is not valid value");
            return;
        }
        // only one subgroup is supported now
        // TODO(b/267783231): Extend LE broadcast support for multi subgroup
        BluetoothLeAudioContentMetadata contentMetadata = settings.get(0).getContentMetadata();
        if (contentMetadata == null) {
            Log.d(TAG, "contentMetadata cannot be null");
            return;
        }

        mLeAudioBroadcasterNativeInterface.createBroadcast(
                contentMetadata.getRawMetadata(), broadcastCode);
    }

    /**
@@ -762,23 +781,39 @@ public class LeAudioService extends ProfileService {
    }

    /**
     * Updates LeAudio Broadcast instance metadata.
     * Updates LeAudio broadcast instance metadata.
     *
     * @param broadcastId broadcast instance identifier
     * @param metadata metadata for the default Broadcast subgroup
     * @param broadcastSettings broadcast settings for this broadcast source
     */
    public void updateBroadcast(int broadcastId, BluetoothLeAudioContentMetadata metadata) {
    public void updateBroadcast(int broadcastId, BluetoothLeBroadcastSettings broadcastSettings) {
        if (mLeAudioBroadcasterNativeInterface == null) {
            Log.w(TAG, "Native interface not available.");
            return;
        }
        if (!mBroadcastStateMap.containsKey(broadcastId)) {
            notifyBroadcastUpdateFailed(broadcastId,
                    BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID);
            notifyBroadcastUpdateFailed(
                    broadcastId, BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID);
            return;
        }

        List<BluetoothLeBroadcastSubgroupSettings> settings =
                broadcastSettings.getSubgroupSettings();
        if (settings == null || settings.size() < 1) {
            Log.d(TAG, "subgroup settings is not valid value");
            return;
        }
        // only one subgroup is supported now
        // TODO(b/267783231): Extend LE broadcast support for multi subgroup
        BluetoothLeAudioContentMetadata contentMetadata = settings.get(0).getContentMetadata();
        if (contentMetadata == null) {
            Log.d(TAG, "contentMetadata cannot be null");
            return;
        }

        if (DBG) Log.d(TAG, "updateBroadcast");
        mLeAudioBroadcasterNativeInterface.updateMetadata(broadcastId, metadata.getRawMetadata());
        mLeAudioBroadcasterNativeInterface.updateMetadata(
                broadcastId, contentMetadata.getRawMetadata());
    }

    /**
@@ -845,6 +880,26 @@ public class LeAudioService extends ProfileService {
        return 1;
    }

    /**
     * Get the maximum number of supported streams per broadcast.
     *
     * @return number of supported streams per broadcast
     */
    public int getMaximumStreamsPerBroadcast() {
        /* TODO: This is currently fixed to 1 */
        return 1;
    }

    /**
     * Get the maximum number of supported subgroups per broadcast.
     *
     * @return number of supported subgroups per broadcast
     */
    public int getMaximumSubgroupsPerBroadcast() {
        /* TODO: This is currently fixed to 1 */
        return 1;
    }

    private BluetoothDevice getFirstDeviceFromGroup(Integer groupId) {
        if (groupId == LE_AUDIO_GROUP_ID_INVALID) {
            return null;
@@ -2173,12 +2228,11 @@ public class LeAudioService extends ProfileService {
    }

    /**
     * This function is called when the framework registers
     * a callback with the service for this first time.
     * This is used as an indication that Bluetooth has been enabled.
     * This function is called when the framework registers a callback with the service for this
     * first time. This is used as an indication that Bluetooth has been enabled.
     *
     * It is used to authorize all known LeAudio devices in the services
     * which requires that e.g. GMCS
     * <p>It is used to authorize all known LeAudio devices in the services which requires that e.g.
     * GMCS
     */
    @VisibleForTesting
    void handleBluetoothEnabled() {
@@ -3109,12 +3163,12 @@ public class LeAudioService extends ProfileService {
        }

        @Override
        public void startBroadcast(BluetoothLeAudioContentMetadata contentMetadata,
                byte[] broadcastCode, AttributionSource source) {
        public void startBroadcast(
                BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) {
            LeAudioService service = getService(source);
            if (service != null) {
                enforceBluetoothPrivilegedPermission(service);
                service.createBroadcast(contentMetadata, broadcastCode);
                service.createBroadcast(broadcastSettings);
            }
        }

@@ -3128,12 +3182,14 @@ public class LeAudioService extends ProfileService {
        }

        @Override
        public void updateBroadcast(int broadcastId,
                BluetoothLeAudioContentMetadata contentMetadata, AttributionSource source) {
        public void updateBroadcast(
                int broadcastId,
                BluetoothLeBroadcastSettings broadcastSettings,
                AttributionSource source) {
            LeAudioService service = getService(source);
            if (service != null) {
                enforceBluetoothPrivilegedPermission(service);
                service.updateBroadcast(broadcastId, contentMetadata);
                service.updateBroadcast(broadcastId, broadcastSettings);
            }
        }

@@ -3185,6 +3241,38 @@ public class LeAudioService extends ProfileService {
            }
        }

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

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

        @Override
        public void getCodecStatus(int groupId,
                AttributionSource source, SynchronousResultReceiver receiver) {
+48 −9
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastSettings;
import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothLeAudioCallback;
@@ -56,6 +58,10 @@ public class LeAudioBinderTest {
    private LeAudioService.BluetoothLeAudioBinder mBinder;
    private BluetoothAdapter mAdapter;

    private static final String TEST_BROADCAST_NAME = "TEST";
    private static final int TEST_QUALITY =
            BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
@@ -298,13 +304,11 @@ public class LeAudioBinderTest {

    @Test
    public void startBroadcast() {
        BluetoothLeAudioContentMetadata metadata =
                new BluetoothLeAudioContentMetadata.Builder().build();
        byte[] code = new byte[] { 0x00 };
        BluetoothLeBroadcastSettings broadcastSettings = buildBroadcastSettingsFromMetadata();
        AttributionSource source = new AttributionSource.Builder(0).build();

        mBinder.startBroadcast(metadata, code, source);
        verify(mMockService).createBroadcast(metadata, code);
        mBinder.startBroadcast(broadcastSettings, source);
        verify(mMockService).createBroadcast(broadcastSettings);
    }

    @Test
@@ -319,12 +323,11 @@ public class LeAudioBinderTest {
    @Test
    public void updateBroadcast() {
        int id = 1;
        BluetoothLeAudioContentMetadata metadata =
                new BluetoothLeAudioContentMetadata.Builder().build();
        BluetoothLeBroadcastSettings broadcastSettings = buildBroadcastSettingsFromMetadata();
        AttributionSource source = new AttributionSource.Builder(0).build();

        mBinder.updateBroadcast(id, metadata, source);
        verify(mMockService).updateBroadcast(id, metadata);
        mBinder.updateBroadcast(id, broadcastSettings, source);
        verify(mMockService).updateBroadcast(id, broadcastSettings);
    }

    @Test
@@ -358,6 +361,24 @@ public class LeAudioBinderTest {
        verify(mMockService).getMaximumNumberOfBroadcasts();
    }

    @Test
    public void getMaximumStreamsPerBroadcast() {
        AttributionSource source = new AttributionSource.Builder(0).build();
        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();

        mBinder.getMaximumStreamsPerBroadcast(source, recv);
        verify(mMockService).getMaximumStreamsPerBroadcast();
    }

    @Test
    public void getMaximumSubgroupsPerBroadcast() {
        AttributionSource source = new AttributionSource.Builder(0).build();
        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();

        mBinder.getMaximumSubgroupsPerBroadcast(source, recv);
        verify(mMockService).getMaximumSubgroupsPerBroadcast();
    }

    @Test
    public void getCodecStatus() {
        int groupId = 1;
@@ -381,4 +402,22 @@ public class LeAudioBinderTest {
        mBinder.setCodecConfigPreference(groupId, inputConfig, outputConfig, source);
        verify(mMockService).setCodecConfigPreference(groupId, inputConfig, outputConfig);
    }

    private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata() {
        BluetoothLeAudioContentMetadata metadata =
                new BluetoothLeAudioContentMetadata.Builder().build();

        BluetoothLeBroadcastSubgroupSettings.Builder subgroupBuilder =
                new BluetoothLeBroadcastSubgroupSettings.Builder()
                .setPreferredQuality(TEST_QUALITY)
                .setContentMetadata(metadata);

        BluetoothLeBroadcastSettings.Builder builder = new BluetoothLeBroadcastSettings.Builder()
                        .setPublicBroadcast(false)
                        .setBroadcastName(TEST_BROADCAST_NAME)
                        .setBroadcastCode(null);
        // builder expect at least one subgroup setting
        builder.addSubgroupSettings(subgroupBuilder.build());
        return builder.build();
    }
}
+20 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.bluetooth.le_audio;

import static org.mockito.Mockito.*;

import android.annotation.Nullable;
import android.bluetooth.*;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -243,7 +244,7 @@ public class LeAudioBroadcastServiceTest {

    void verifyBroadcastStarted(int broadcastId, byte[] code,
            BluetoothLeAudioContentMetadata meta) {
        mService.createBroadcast(meta, code);
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code));

        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()),
                eq(code));
@@ -325,7 +326,7 @@ public class LeAudioBroadcastServiceTest {
        meta_builder.setLanguage("deu");
        meta_builder.setProgramInfo("Public broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();
        mService.createBroadcast(meta, code);
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code));

        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(code));

@@ -371,7 +372,8 @@ public class LeAudioBroadcastServiceTest {
        new BluetoothLeAudioContentMetadata.Builder();
        meta_builder.setLanguage("eng");
        meta_builder.setProgramInfo("Public broadcast info");
        mService.updateBroadcast(broadcastId, meta_builder.build());
        mService.updateBroadcast(broadcastId,
                buildBroadcastSettingsFromMetadata(meta_builder.build(), null));
        Assert.assertFalse(mOnBroadcastUpdatedCalled);
        Assert.assertTrue(mOnBroadcastUpdateFailedCalled);
    }
@@ -430,7 +432,7 @@ public class LeAudioBroadcastServiceTest {
        meta_builder.setLanguage("ENG");
        meta_builder.setProgramInfo("Public broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();
        mService.createBroadcast(meta, code);
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code));

        // Inject metadata stack event and verify if getter API works as expected
        LeAudioStackEvent state_event =
@@ -456,4 +458,18 @@ public class LeAudioBroadcastServiceTest {
        }
    }

    private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
            BluetoothLeAudioContentMetadata contentMetadata,
            @Nullable byte[] broadcastCode) {
        BluetoothLeBroadcastSubgroupSettings.Builder subgroupBuilder =
                new BluetoothLeBroadcastSubgroupSettings.Builder()
                .setContentMetadata(contentMetadata);

        BluetoothLeBroadcastSettings.Builder builder = new BluetoothLeBroadcastSettings.Builder()
                        .setPublicBroadcast(false)
                        .setBroadcastCode(broadcastCode);
        // builder expect at least one subgroup setting
        builder.addSubgroupSettings(subgroupBuilder.build());
        return builder.build();
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.BluetoothProfileConnectionInfo;
import android.os.Parcel;
import android.os.ParcelUuid;

import androidx.test.InstrumentationRegistry;
@@ -56,7 +55,6 @@ import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -66,7 +64,6 @@ import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.vc.VolumeControlService;

import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1558,6 +1555,8 @@ public class LeAudioServiceTest {
    @Test
    public void testDefaultValuesOfSeveralGetters() {
        assertThat(mService.getMaximumNumberOfBroadcasts()).isEqualTo(1);
        assertThat(mService.getMaximumStreamsPerBroadcast()).isEqualTo(1);
        assertThat(mService.getMaximumSubgroupsPerBroadcast()).isEqualTo(1);
        assertThat(mService.isPlaying(100)).isFalse();
        assertThat(mService.isValidDeviceGroup(LE_AUDIO_GROUP_ID_INVALID)).isFalse();
    }
Loading