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

Commit 15c02e84 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge changes from topic "pbp-source"

* changes:
  [le audio] Implement public broadcast for broadcast source
  [le audio] Support multiple subgroup broadcast settings in native
parents df2841d7 4da85d8b
Loading
Loading
Loading
Loading
+105 −15
Original line number Diff line number Diff line
@@ -907,6 +907,33 @@ jobject prepareBluetoothLeBroadcastMetadataObject(
    CHECK(!env->ExceptionCheck());
  }

  ScopedLocalRef<jstring> broadcast_name(
      env, env->NewStringUTF(broadcast_metadata.broadcast_name.c_str()));
  if (!broadcast_name.get()) {
    LOG(ERROR) << "Failed to create new broadcast name String";
    return nullptr;
  }

  jint audio_cfg_quality = 0;
  if (broadcast_metadata.public_announcement.features &
      bluetooth::le_audio::kLeAudioQualityStandard) {
    // Set bit 0 for AUDIO_CONFIG_QUALITY_STANDARD
    audio_cfg_quality |= 0x1 << bluetooth::le_audio::QUALITY_STANDARD;
  }
  if (broadcast_metadata.public_announcement.features &
      bluetooth::le_audio::kLeAudioQualityHigh) {
    // Set bit 1 for AUDIO_CONFIG_QUALITY_HIGH
    audio_cfg_quality |= 0x1 << bluetooth::le_audio::QUALITY_HIGH;
  }

  ScopedLocalRef<jobject> public_meta_obj(
      env, prepareLeAudioContentMetadataObject(
               env, broadcast_metadata.public_announcement.metadata));
  if (!public_meta_obj.get()) {
    LOG(ERROR) << "Failed to create new public metadata obj";
    return nullptr;
  }

  return env->NewObject(
      android_bluetooth_BluetoothLeBroadcastMetadata.clazz,
      android_bluetooth_BluetoothLeBroadcastMetadata.constructor,
@@ -914,10 +941,10 @@ 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.is_public, broadcast_name.get(),
      broadcast_metadata.broadcast_code ? code.get() : nullptr,
      (jint)broadcast_metadata.basic_audio_announcement.presentation_delay,
      (jint)0, nullptr, subgroup_list_obj.get());
      audio_cfg_quality, public_meta_obj.get(), subgroup_list_obj.get());
}

class LeAudioBroadcasterCallbacksImpl : public LeAudioBroadcasterCallbacks {
@@ -1193,9 +1220,29 @@ static void BroadcasterCleanupNative(JNIEnv* env, jobject object) {
  }
}

std::vector<std::vector<uint8_t>> convertToDataVectors(JNIEnv* env,
                                                       jobjectArray dataArray) {
  jsize arraySize = env->GetArrayLength(dataArray);
  std::vector<std::vector<uint8_t>> res(arraySize);

  for (int i = 0; i < arraySize; ++i) {
    jbyteArray rowData = (jbyteArray)env->GetObjectArrayElement(dataArray, i);
    jsize dataSize = env->GetArrayLength(rowData);
    std::vector<uint8_t>& rowVector = res[i];
    rowVector.resize(dataSize);
    env->GetByteArrayRegion(rowData, 0, dataSize,
                            reinterpret_cast<jbyte*>(rowVector.data()));
    env->DeleteLocalRef(rowData);
  }
  return res;
}

static void CreateBroadcastNative(JNIEnv* env, jobject object,
                                  jbyteArray metadata,
                                  jbyteArray broadcast_code) {
                                  jboolean isPublic, jstring broadcastName,
                                  jbyteArray broadcast_code,
                                  jbyteArray publicMetadata,
                                  jintArray qualityArray,
                                  jobjectArray metadataArray) {
  LOG(INFO) << __func__;
  std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
  if (!sLeAudioBroadcasterInterface) return;
@@ -1212,21 +1259,62 @@ static void CreateBroadcastNative(JNIEnv* env, jobject object,
    env->GetByteArrayRegion(broadcast_code, 0, size, (jbyte*)code_array.data());
  }

  jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
  const char* broadcast_name = nullptr;
  if (broadcastName) {
    broadcast_name = env->GetStringUTFChars(broadcastName, nullptr);
  }

  jbyte* public_meta = nullptr;
  if (publicMetadata) {
    public_meta = env->GetByteArrayElements(publicMetadata, nullptr);
  }

  jint* quality_array = nullptr;
  if (qualityArray) {
    quality_array = env->GetIntArrayElements(qualityArray, nullptr);
  }

  sLeAudioBroadcasterInterface->CreateBroadcast(
      std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)),
      isPublic, broadcast_name ? broadcast_name : "",
      broadcast_code ? std::optional<std::array<uint8_t, 16>>(code_array)
                     : std::nullopt);
  env->ReleaseByteArrayElements(metadata, meta, 0);
                     : std::nullopt,
      public_meta ? std::vector<uint8_t>(
                        public_meta, public_meta + env->GetArrayLength(publicMetadata))
                  : std::vector<uint8_t>(),
      quality_array ? std::vector<uint8_t>(
                        quality_array, quality_array + env->GetArrayLength(qualityArray))
                  : std::vector<uint8_t>(),
      convertToDataVectors(env, metadataArray));

  if (broadcast_name) env->ReleaseStringUTFChars(broadcastName, broadcast_name);
  if (public_meta) env->ReleaseByteArrayElements(publicMetadata, public_meta, 0);
  if (quality_array) env->ReleaseIntArrayElements(qualityArray, quality_array, 0);
}

static void UpdateMetadataNative(JNIEnv* env, jobject object, jint broadcast_id,
                                 jbyteArray metadata) {
  jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
                                 jstring broadcastName,
                                 jbyteArray publicMetadata,
                                 jobjectArray metadataArray) {
  const char* broadcast_name = nullptr;
  if (broadcastName) {
    broadcast_name = env->GetStringUTFChars(broadcastName, nullptr);
  }

  jbyte* public_meta = nullptr;
  if (publicMetadata) {
    public_meta = env->GetByteArrayElements(publicMetadata, nullptr);
  }

  sLeAudioBroadcasterInterface->UpdateMetadata(
      broadcast_id,
      std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)));
  env->ReleaseByteArrayElements(metadata, meta, 0);
      broadcast_id, broadcast_name ? broadcast_name : "",
      public_meta
          ? std::vector<uint8_t>(
                public_meta, public_meta + env->GetArrayLength(publicMetadata))
          : std::vector<uint8_t>(),
      convertToDataVectors(env, metadataArray));

  if (broadcast_name) env->ReleaseStringUTFChars(broadcastName, broadcast_name);
  if (public_meta) env->ReleaseByteArrayElements(publicMetadata, public_meta, 0);
}

static void StartBroadcastNative(JNIEnv* env, jobject object,
@@ -1274,8 +1362,10 @@ static JNINativeMethod sBroadcasterMethods[] = {
    {"initNative", "()V", (void*)BroadcasterInitNative},
    {"stopNative", "()V", (void*)BroadcasterStopNative},
    {"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
    {"createBroadcastNative", "([B[B)V", (void*)CreateBroadcastNative},
    {"updateMetadataNative", "(I[B)V", (void*)UpdateMetadataNative},
    {"createBroadcastNative", "(ZLjava/lang/String;[B[B[I[[B)V",
     (void*)CreateBroadcastNative},
    {"updateMetadataNative", "(ILjava/lang/String;[B[[B)V",
     (void*)UpdateMetadataNative},
    {"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
    {"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
    {"pauseBroadcastNative", "(I)V", (void*)PauseBroadcastNative},
+24 −9
Original line number Diff line number Diff line
@@ -168,23 +168,35 @@ public class LeAudioBroadcasterNativeInterface {
    /**
     * Creates LeAudio Broadcast instance.
     *
     * @param metadata metadata buffer with TLVs
     * @param broadcastCode optional code if broadcast should be encrypted
     * @param isPublicBroadcast this BIG is public broadcast
     * @param broadcastName BIG broadcast name
     * @param broadcastCode BIG broadcast code
     * @param publicMetadata BIG public broadcast meta data
     * @param qualityArray BIG sub group audio quality array
     * @param metadataArray BIG sub group metadata array
     *
     * qualityArray and metadataArray use the same subgroup index
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void createBroadcast(byte[] metadata, byte[] broadcastCode) {
        createBroadcastNative(metadata, broadcastCode);
    public void createBroadcast(boolean isPublicBroadcast, String broadcastName,
            byte[] broadcastCode, byte[] publicMetadata, int[] qualityArray,
            byte[][] metadataArray) {
        createBroadcastNative(isPublicBroadcast, broadcastName, broadcastCode, publicMetadata,
                qualityArray, metadataArray);
    }

    /**
     * Update LeAudio Broadcast instance metadata.
     *
     * @param broadcastId broadcast instance identifier
     * @param metadata metadata buffer with TLVs
     * @param broadcastName BIG broadcast name
     * @param publicMetadata BIG public broadcast meta data
     * @param metadataArray BIG sub group metadata array
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void updateMetadata(int broadcastId, byte[] metadata) {
        updateMetadataNative(broadcastId, metadata);
    public void updateMetadata(int broadcastId, String broadcastName,
            byte[] publicMetadata, byte[][] metadataArray) {
        updateMetadataNative(broadcastId, broadcastName, publicMetadata, metadataArray);
    }

    /**
@@ -240,8 +252,11 @@ public class LeAudioBroadcasterNativeInterface {
    private native void initNative();
    private native void stopNative();
    private native void cleanupNative();
    private native void createBroadcastNative(byte[] metadata, byte[] broadcastCode);
    private native void updateMetadataNative(int broadcastId, byte[] metadata);
    private native void createBroadcastNative(boolean isPublicBroadcast, String broadcastName,
            byte[] broadcastCode, byte[] publicMetadata, int[] qualityArray,
            byte[][] metadataArray);
    private native void updateMetadataNative(int broadcastId, String broadcastName,
            byte[] publicMetadata, byte[][] metadataArray);
    private native void startBroadcastNative(int broadcastId);
    private native void stopBroadcastNative(int broadcastId);
    private native void pauseBroadcastNative(int broadcastId);
+25 −23
Original line number Diff line number Diff line
@@ -768,24 +768,26 @@ public class LeAudioService extends ProfileService {
                return;
            }
        }
        Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));

        List<BluetoothLeBroadcastSubgroupSettings> settings =
        List<BluetoothLeBroadcastSubgroupSettings> settingsList =
                broadcastSettings.getSubgroupSettings();
        if (settings == null || settings.size() < 1) {
        if (settingsList == null || settingsList.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);
        BluetoothLeAudioContentMetadata publicMetadata =
                broadcastSettings.getPublicBroadcastMetadata();

        Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));
        mLeAudioBroadcasterNativeInterface.createBroadcast(broadcastSettings.isPublicBroadcast(),
                broadcastSettings.getBroadcastName(), broadcastCode,
                publicMetadata == null ? null : publicMetadata.getRawMetadata(),
                settingsList.stream()
                        .mapToInt(s -> s.getPreferredQuality()).toArray(),
                settingsList.stream()
                        .map(s -> s.getContentMetadata().getRawMetadata())
                        .toArray(byte[][]::new));
    }

    /**
@@ -818,23 +820,23 @@ public class LeAudioService extends ProfileService {
            return;
        }

        List<BluetoothLeBroadcastSubgroupSettings> settings =
        List<BluetoothLeBroadcastSubgroupSettings> settingsList =
                broadcastSettings.getSubgroupSettings();
        if (settings == null || settings.size() < 1) {
        if (settingsList == null || settingsList.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;
        }

        BluetoothLeAudioContentMetadata publicMetadata =
                broadcastSettings.getPublicBroadcastMetadata();

        if (DBG) Log.d(TAG, "updateBroadcast");
        mLeAudioBroadcasterNativeInterface.updateMetadata(
                broadcastId, contentMetadata.getRawMetadata());
        mLeAudioBroadcasterNativeInterface.updateMetadata(broadcastId,
                broadcastSettings.getBroadcastName(),
                publicMetadata == null ? null : publicMetadata.getRawMetadata(),
                settingsList.stream()
                        .map(s -> s.getContentMetadata().getRawMetadata())
                        .toArray(byte[][]::new));
    }

    /**
+69 −21
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ public class LeAudioBroadcastServiceTest {
    private static final String TEST_PROGRAM_INFO = "Test";
    // German language code in ISO 639-3
    private static final String TEST_LANGUAGE = "deu";
    private static final String TEST_BROADCAST_NAME = "Name Test";

    private boolean mOnBroadcastStartedCalled = false;
    private boolean mOnBroadcastStartFailedCalled = false;
@@ -242,12 +243,25 @@ public class LeAudioBroadcastServiceTest {
        });
    }

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

        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()),
                eq(code));
        List<BluetoothLeBroadcastSubgroupSettings> settingsList =
                settings.getSubgroupSettings();

        int[] expectedQualityArray =
                settingsList.stream()
                        .mapToInt(setting -> setting.getPreferredQuality()).toArray();
        byte[][] expectedDataArray =
                settingsList.stream()
                        .map(setting -> setting.getContentMetadata().getRawMetadata())
                        .toArray(byte[][]::new);

        verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
                eq(settings.getBroadcastCode()),
                eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
                eq(expectedQualityArray),
                eq(expectedDataArray));

        // Check if broadcast is started automatically when created
        LeAudioStackEvent create_event =
@@ -309,9 +323,26 @@ public class LeAudioBroadcastServiceTest {
        BluetoothLeAudioContentMetadata.Builder meta_builder =
                new BluetoothLeAudioContentMetadata.Builder();
        meta_builder.setLanguage("deu");
        meta_builder.setProgramInfo("Public broadcast info");
        meta_builder.setProgramInfo("Subgroup broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();

        verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1));
    }

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

        mService.mBroadcastCallbacks.register(mCallbacks);

        verifyBroadcastStarted(broadcastId, code, meta_builder.build());
        BluetoothLeAudioContentMetadata.Builder meta_builder =
                new BluetoothLeAudioContentMetadata.Builder();
        meta_builder.setLanguage("deu");
        meta_builder.setProgramInfo("Subgroup broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();

        verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 3));
    }

    @Test
@@ -326,9 +357,19 @@ public class LeAudioBroadcastServiceTest {
        meta_builder.setLanguage("deu");
        meta_builder.setProgramInfo("Public broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code));
        BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1);
        mService.createBroadcast(settings);

        // Test data with only one subgroup
        int[] expectedQualityArray =
                {settings.getSubgroupSettings().get(0).getPreferredQuality()};
        byte[][] expectedDataArray =
                {settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata()};

        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(code));
        verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
                eq(code), eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
                eq(expectedQualityArray),
                eq(expectedDataArray));

        LeAudioStackEvent create_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
@@ -349,10 +390,11 @@ public class LeAudioBroadcastServiceTest {

        BluetoothLeAudioContentMetadata.Builder meta_builder =
                new BluetoothLeAudioContentMetadata.Builder();
        meta_builder.setLanguage("eng");
        meta_builder.setProgramInfo("Public broadcast info");
        meta_builder.setLanguage("deu");
        meta_builder.setProgramInfo("Subgroup broadcast info");
        BluetoothLeAudioContentMetadata meta = meta_builder.build();

        verifyBroadcastStarted(broadcastId, code, meta_builder.build());
        verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1));
        verifyBroadcastStopped(broadcastId);
    }

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

        // Inject metadata stack event and verify if getter API works as expected
        LeAudioStackEvent state_event =
@@ -460,20 +502,26 @@ public class LeAudioBroadcastServiceTest {

    private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
            BluetoothLeAudioContentMetadata contentMetadata,
            @Nullable byte[] broadcastCode) {
        BluetoothLeAudioContentMetadata publicBroadcastMetadata =
                new BluetoothLeAudioContentMetadata.Builder().build();
            @Nullable byte[] broadcastCode,
            int numOfGroups) {
        BluetoothLeAudioContentMetadata.Builder publicMetaBuilder =
                new BluetoothLeAudioContentMetadata.Builder();
        publicMetaBuilder.setProgramInfo("Public broadcast info");

        BluetoothLeBroadcastSubgroupSettings.Builder subgroupBuilder =
                new BluetoothLeBroadcastSubgroupSettings.Builder()
                .setContentMetadata(contentMetadata);

        BluetoothLeBroadcastSettings.Builder builder = new BluetoothLeBroadcastSettings.Builder()
                        .setPublicBroadcast(false)
                        .setPublicBroadcast(true)
                        .setBroadcastName(TEST_BROADCAST_NAME)
                        .setBroadcastCode(broadcastCode)
                        .setPublicBroadcastMetadata(publicBroadcastMetadata);
                        .setPublicBroadcastMetadata(publicMetaBuilder.build());
        // builder expect at least one subgroup setting
        for (int i = 0; i < numOfGroups; i++) {
            // add subgroup settings with the same content
            builder.addSubgroupSettings(subgroupBuilder.build());
        }
        return builder.build();
    }
}
+9 −5
Original line number Diff line number Diff line
@@ -40,17 +40,21 @@ class LeAudioBroadcaster {
  static void DebugDump(int fd);

  virtual void CreateAudioBroadcast(
      std::vector<uint8_t> metadata,
      std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code =
          std::nullopt) = 0;
      bool is_public, const std::string& broadcast_name,
      const std::optional<bluetooth::le_audio::BroadcastCode>& broadcast_code,
      const std::vector<uint8_t>& public_metadata,
      const std::vector<uint8_t>& subgroup_quality,
      const std::vector<std::vector<uint8_t>>& subgroup_metadata) = 0;
  virtual void SuspendAudioBroadcast(uint32_t broadcast_id) = 0;
  virtual void StartAudioBroadcast(uint32_t broadcast_id) = 0;
  virtual void StopAudioBroadcast(uint32_t broadcast_id) = 0;
  virtual void DestroyAudioBroadcast(uint32_t broadcast_id) = 0;
  virtual void GetBroadcastMetadata(uint32_t broadcast_id) = 0;
  virtual void GetAllBroadcastStates(void) = 0;
  virtual void UpdateMetadata(uint32_t broadcast_id,
                              std::vector<uint8_t> metadata) = 0;
  virtual void UpdateMetadata(
      uint32_t broadcast_id, const std::string& broadcast_name,
      const std::vector<uint8_t>& public_metadata,
      const std::vector<std::vector<uint8_t>>& subgroup_metadata) = 0;
  virtual void IsValidBroadcast(
      uint32_t broadcast_id, uint8_t addr_type, RawAddress addr,
      base::Callback<void(uint8_t /* broadcast_id */, uint8_t /* addr_type */,
Loading