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

Commit 4da85d8b authored by Rongxuan Liu's avatar Rongxuan Liu
Browse files

[le audio] Implement public broadcast for broadcast source

Tag: #feature
Bug: 269799082
Test: atest bluetooth_le_audio_test bluetooth_test_broadcaster
Test: manual broadcast test with assistant
Change-Id: Ie58964d81805b1030c3e6da6ddc2524c62a6d403
parent 6adf740d
Loading
Loading
Loading
Loading
+63 −21
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 {
@@ -1232,27 +1259,36 @@ static void CreateBroadcastNative(JNIEnv* env, jobject object,
    env->GetByteArrayRegion(broadcast_code, 0, size, (jbyte*)code_array.data());
  }

  jbyte* public_meta = env->GetByteArrayElements(publicMetadata, nullptr);
  jint* quality_array = env->GetIntArrayElements(qualityArray, nullptr);

  const char* broadcast_name = nullptr;
  if (broadcastName != 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(
      isPublic, std::string(broadcast_name),
      isPublic, broadcast_name ? broadcast_name : "",
      broadcast_code ? std::optional<std::array<uint8_t, 16>>(code_array)
                     : std::nullopt,
      std::vector<uint8_t>(public_meta,
                           public_meta + env->GetArrayLength(publicMetadata)),
      std::vector<uint8_t>(quality_array,
                           quality_array + env->GetArrayLength(qualityArray)),
      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));

  env->ReleaseStringUTFChars(broadcastName, broadcast_name);
  env->ReleaseIntArrayElements(qualityArray, quality_array, 0);
  env->ReleaseByteArrayElements(publicMetadata, public_meta, 0);
  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,
@@ -1260,19 +1296,25 @@ static void UpdateMetadataNative(JNIEnv* env, jobject object, jint broadcast_id,
                                 jbyteArray publicMetadata,
                                 jobjectArray metadataArray) {
  const char* broadcast_name = nullptr;
  if (broadcastName != nullptr) {
  if (broadcastName) {
    broadcast_name = env->GetStringUTFChars(broadcastName, nullptr);
  }
  jbyte* public_meta = env->GetByteArrayElements(publicMetadata, nullptr);

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

  sLeAudioBroadcasterInterface->UpdateMetadata(
      broadcast_id, std::string(broadcast_name),
      std::vector<uint8_t>(public_meta,
                           public_meta + env->GetArrayLength(publicMetadata)),
      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));

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

static void StartBroadcastNative(JNIEnv* env, jobject object,
+80 −7
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ using bluetooth::hci::iso_manager::BigCallbacks;
using bluetooth::le_audio::BasicAudioAnnouncementData;
using bluetooth::le_audio::BasicAudioAnnouncementSubgroup;
using bluetooth::le_audio::BroadcastId;
using bluetooth::le_audio::PublicBroadcastAnnouncementData;
using le_audio::CodecManager;
using le_audio::ContentControlIdKeeper;
using le_audio::LeAudioCodecConfiguration;
@@ -133,6 +134,16 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
    }
  }

  static PublicBroadcastAnnouncementData preparePublicAnnouncement(
      uint8_t features, const LeAudioLtvMap& metadata) {
    PublicBroadcastAnnouncementData announcement;

    /* Prepare the announcement */
    announcement.features = features;
    announcement.metadata = metadata.Values();
    return announcement;
  }

  static BasicAudioAnnouncementData prepareBasicAnnouncement(
      const BroadcastCodecWrapper& codec_config,
      const std::vector<LeAudioLtvMap>& metadata_group) {
@@ -310,8 +321,29 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
      subgroup_ltvs.push_back(ltv);
    }

    if (broadcasts_[broadcast_id]->IsPublicBroadcast()) {
      // Only update broadcast name and public metadata if current broadcast is
      // public Otherwise ignore those fields
      bool is_public_metadata_valid;
      LeAudioLtvMap public_ltv =
          LeAudioLtvMap::Parse(public_metadata.data(), public_metadata.size(),
                               is_public_metadata_valid);
      if (!is_public_metadata_valid) {
        LOG_ERROR("Invalid public metadata provided.");
        return;
      }
      PublicBroadcastAnnouncementData pb_announcement =
          preparePublicAnnouncement(broadcasts_[broadcast_id]
                                        ->GetPublicBroadcastAnnouncement()
                                        .features,
                                    public_ltv);

      broadcasts_[broadcast_id]->UpdatePublicBroadcastAnnouncement(
          broadcast_id, broadcast_name, pb_announcement);
    }

    BasicAudioAnnouncementData announcement =
        prepareBasicAnnouncement(codec_config, std::move(subgroup_ltvs));
        prepareBasicAnnouncement(codec_config, subgroup_ltvs);

    broadcasts_[broadcast_id]->UpdateBroadcastAnnouncement(
        std::move(announcement));
@@ -323,7 +355,27 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
      const std::vector<uint8_t>& public_metadata,
      const std::vector<uint8_t>& subgroup_quality,
      const std::vector<std::vector<uint8_t>>& subgroup_metadata) override {
    uint8_t public_features = 0;
    LeAudioLtvMap public_ltv;
    std::vector<LeAudioLtvMap> subgroup_ltvs;

    if (is_public) {
      // Prepare public broadcast announcement format
      bool is_metadata_valid;
      public_ltv = LeAudioLtvMap::Parse(
          public_metadata.data(), public_metadata.size(), is_metadata_valid);
      if (!is_metadata_valid) {
        LOG_ERROR("Invalid metadata provided.");
        return;
      }
      // Prepare public features byte
      // bit 0 Encryption broadcast stream encrypted or not
      // bit 1 Standard quality audio configuration present or not
      // bit 2 High quality audio configuration present or not
      // bit 3-7 RFU
      public_features = static_cast<uint8_t>(broadcast_code ? 1 : 0);
    }

    auto broadcast_id = available_broadcast_ids_.back();
    available_broadcast_ids_.pop_back();
    if (available_broadcast_ids_.size() == 0) GenerateBroadcastIds();
@@ -341,6 +393,14 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
          LeAudioContextType::MEDIA | LeAudioContextType::CONVERSATIONAL;
    }

    for (const uint8_t quality : subgroup_quality) {
      if (quality == bluetooth::le_audio::QUALITY_STANDARD) {
        public_features |= bluetooth::le_audio::kLeAudioQualityStandard;
      } else if (quality == bluetooth::le_audio::QUALITY_HIGH) {
        public_features |= bluetooth::le_audio::kLeAudioQualityHigh;
      }
    }

    for (const std::vector<uint8_t>& metadata : subgroup_metadata) {
      /* Prepare the announcement format */
      bool is_metadata_valid;
@@ -401,28 +461,37 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
                                    offload_config->max_transport_latency);

      BroadcastStateMachineConfig msg = {
          .is_public = is_public,
          .broadcast_name = broadcast_name,
          .broadcast_id = broadcast_id,
          .streaming_phy = GetStreamingPhy(),
          .codec_wrapper = codec_config,
          .qos_config = qos_config,
          .announcement =
              prepareBasicAnnouncement(codec_config, std::move(subgroup_ltvs)),
          .announcement = prepareBasicAnnouncement(codec_config, subgroup_ltvs),
          .broadcast_code = std::move(broadcast_code)};

      if (is_public) {
        msg.public_announcement =
            preparePublicAnnouncement(public_features, public_ltv);
      }
      pending_broadcasts_.push_back(
          std::move(BroadcastStateMachine::CreateInstance(std::move(msg))));
    } else {
      auto codec_qos_pair =
          le_audio::broadcaster::getStreamConfigForContext(context_type);
      BroadcastStateMachineConfig msg = {
          .is_public = is_public,
          .broadcast_name = broadcast_name,
          .broadcast_id = broadcast_id,
          .streaming_phy = GetStreamingPhy(),
          .codec_wrapper = codec_qos_pair.first,
          .qos_config = codec_qos_pair.second,
          .announcement = prepareBasicAnnouncement(codec_qos_pair.first,
                                                   std::move(subgroup_ltvs)),
          .announcement =
              prepareBasicAnnouncement(codec_qos_pair.first, subgroup_ltvs),
          .broadcast_code = std::move(broadcast_code)};

      if (is_public) {
        msg.public_announcement =
            preparePublicAnnouncement(public_features, public_ltv);
      }
      /* Create the broadcaster instance - we'll receive it's init state in the
       * async callback
       */
@@ -517,7 +586,9 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
    bluetooth::le_audio::BroadcastMetadata metadata;
    for (auto const& kv_it : broadcasts_) {
      if (kv_it.second->GetBroadcastId() == broadcast_id) {
        metadata.is_public = kv_it.second->IsPublicBroadcast();
        metadata.broadcast_id = kv_it.second->GetBroadcastId();
        metadata.broadcast_name = kv_it.second->GetBroadcastName();
        metadata.adv_sid = kv_it.second->GetAdvertisingSid();
        metadata.pa_interval = kv_it.second->GetPaInterval();
        metadata.addr = kv_it.second->GetOwnAddress();
@@ -525,6 +596,8 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
        metadata.broadcast_code = kv_it.second->GetBroadcastCode();
        metadata.basic_audio_announcement =
            kv_it.second->GetBroadcastAnnouncement();
        metadata.public_announcement =
            kv_it.second->GetPublicBroadcastAnnouncement();
        return metadata;
      }
    }
+33 −1
Original line number Diff line number Diff line
@@ -143,6 +143,8 @@ static const std::vector<uint8_t> default_metadata = {
    default_context & 0x00FF, (default_context & 0xFF00) >> 8};
static const std::vector<uint8_t> default_public_metadata = {
    5, le_audio::types::kLeAudioMetadataTypeProgramInfo, 0x1, 0x2, 0x3, 0x4};
// bit 0: encrypted, bit 1: standard quality present
static const uint8_t test_public_broadcast_features = 0x3;

static constexpr uint8_t default_num_of_groups = 1;
static constexpr uint8_t media_ccid = 0xC0;
@@ -454,6 +456,9 @@ TEST_F(BroadcasterTest, GetBroadcastAllStates) {
TEST_F(BroadcasterTest, UpdateMetadata) {
  auto broadcast_id = InstantiateBroadcast();
  std::vector<uint8_t> ccid_list;
  std::vector<uint8_t> expected_public_meta;
  std::string expected_broadcast_name;

  EXPECT_CALL(*MockBroadcastStateMachine::GetLastInstance(),
              UpdateBroadcastAnnouncement)
      .WillOnce(
@@ -468,6 +473,16 @@ TEST_F(BroadcasterTest, UpdateMetadata) {
            }
          });

  EXPECT_CALL(*MockBroadcastStateMachine::GetLastInstance(),
              UpdatePublicBroadcastAnnouncement)
      .WillOnce([&](uint32_t broadcast_id, const std::string& broadcast_name,
                    const bluetooth::le_audio::PublicBroadcastAnnouncementData&
                        announcement) {
        expected_broadcast_name = broadcast_name;
        expected_public_meta =
            types::LeAudioLtvMap(announcement.metadata).RawPacket();
      });

  ContentControlIdKeeper::GetInstance()->SetCcid(LeAudioContextType::ALERTS,
                                                 default_ccid);

@@ -478,6 +493,8 @@ TEST_F(BroadcasterTest, UpdateMetadata) {
  ASSERT_EQ(2u, ccid_list.size());
  ASSERT_NE(0, std::count(ccid_list.begin(), ccid_list.end(), media_ccid));
  ASSERT_NE(0, std::count(ccid_list.begin(), ccid_list.end(), default_ccid));
  ASSERT_EQ(expected_broadcast_name, test_broadcast_name);
  ASSERT_EQ(expected_public_meta, default_public_metadata);
}

static BasicAudioAnnouncementData prepareAnnouncement(
@@ -603,10 +620,22 @@ TEST_F(BroadcasterTest, GetMetadata) {
      32000, 40);
  auto announcement = prepareAnnouncement(codec_config, meta);

  ON_CALL(*sm, GetAdvertisingSid()).WillByDefault(Return(test_adv_sid));
  bool is_public_metadata_valid;
  types::LeAudioLtvMap public_ltv = types::LeAudioLtvMap::Parse(
      default_public_metadata.data(), default_public_metadata.size(),
      is_public_metadata_valid);
  PublicBroadcastAnnouncementData pb_announcement = {
      .features = test_public_broadcast_features,
      .metadata = public_ltv.Values()};

  ON_CALL(*sm, IsPublicBroadcast()).WillByDefault(Return(true));
  ON_CALL(*sm, GetBroadcastName()).WillByDefault(Return(test_broadcast_name));
  ON_CALL(*sm, GetBroadcastCode()).WillByDefault(Return(test_broadcast_code));
  ON_CALL(*sm, GetAdvertisingSid()).WillByDefault(Return(test_adv_sid));
  ON_CALL(*sm, GetBroadcastAnnouncement())
      .WillByDefault(ReturnRef(announcement));
  ON_CALL(*sm, GetPublicBroadcastAnnouncement())
      .WillByDefault(ReturnRef(pb_announcement));

  EXPECT_CALL(mock_broadcaster_callbacks_,
              OnBroadcastMetadataChanged(broadcast_id, _))
@@ -622,6 +651,9 @@ TEST_F(BroadcasterTest, GetMetadata) {
  ASSERT_EQ(sm->GetOwnAddress(), metadata.addr);
  ASSERT_EQ(sm->GetOwnAddressType(), metadata.addr_type);
  ASSERT_EQ(sm->GetAdvertisingSid(), metadata.adv_sid);
  ASSERT_EQ(sm->IsPublicBroadcast(), metadata.is_public);
  ASSERT_EQ(sm->GetBroadcastName(), metadata.broadcast_name);
  ASSERT_EQ(sm->GetPublicBroadcastAnnouncement(), metadata.public_announcement);
}

TEST_F(BroadcasterTest, SetStreamingPhy) {
+56 −6
Original line number Diff line number Diff line
@@ -92,6 +92,21 @@ static void EmitMetadata(
  }
}

static void EmitBroadcastName(const std::string& name,
                              std::vector<uint8_t>& data) {
  int name_len = name.length();
  size_t old_size = data.size();
  data.resize(old_size + name_len + 2);

  // Set the cursor behind the old data
  uint8_t* p_value = data.data() + old_size;
  UINT8_TO_STREAM(p_value, name_len + 1);
  UINT8_TO_STREAM(p_value, BTM_BLE_AD_TYPE_BROADCAST_NAME);

  std::vector<uint8_t> vec(name.begin(), name.end());
  ARRAY_TO_STREAM(p_value, vec.data(), name_len);
}

static void EmitBisConfigs(
    const std::vector<BasicAudioAnnouncementBisConfig>& bis_configs,
    std::vector<uint8_t>& data) {
@@ -156,15 +171,42 @@ bool ToRawPacket(BasicAudioAnnouncementData const& in,
  return true;
}

void PrepareAdvertisingData(bluetooth::le_audio::BroadcastId& broadcast_id,
                            std::vector<uint8_t>& periodic_data) {
  periodic_data.resize(7);
  uint8_t* data_ptr = periodic_data.data();
void PrepareAdvertisingData(
    bool is_public, const std::string& broadcast_name,
    bluetooth::le_audio::BroadcastId& broadcast_id,
    const bluetooth::le_audio::PublicBroadcastAnnouncementData&
        public_announcement,
    std::vector<uint8_t>& adv_data) {
  adv_data.resize(7);
  uint8_t* data_ptr = adv_data.data();
  UINT8_TO_STREAM(data_ptr, 6);
  UINT8_TO_STREAM(data_ptr, BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE);
  UINT16_TO_STREAM(data_ptr, kBroadcastAudioAnnouncementServiceUuid);
  UINT24_TO_STREAM(data_ptr, broadcast_id)
};
  UINT24_TO_STREAM(data_ptr, broadcast_id);

  // Prepare public broadcast announcement data
  if (is_public) {
    size_t old_size = adv_data.size();
    // 5: datalen(1) + adtype(1) + serviceuuid(2) + features(1)
    adv_data.resize(old_size + 5);
    // Skip the data length field until the full content is generated
    data_ptr = adv_data.data() + old_size + 1;
    UINT8_TO_STREAM(data_ptr, BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE);
    UINT16_TO_STREAM(data_ptr, kPublicBroadcastAnnouncementServiceUuid);
    UINT8_TO_STREAM(data_ptr, public_announcement.features);
    // Set metadata length to 0 if no meta data present
    EmitMetadata(public_announcement.metadata, adv_data);

    // Update the length field accordingly
    data_ptr = adv_data.data() + old_size;
    UINT8_TO_STREAM(data_ptr, adv_data.size() - old_size - 1);

    // Prepare broadcast name
    if (!broadcast_name.empty()) {
      EmitBroadcastName(broadcast_name, adv_data);
    }
  }
}

void PreparePeriodicData(const BasicAudioAnnouncementData& announcement,
                         std::vector<uint8_t>& periodic_data) {
@@ -502,5 +544,13 @@ bool operator==(const BasicAudioAnnouncementData& lhs,

  return true;
}

bool operator==(const PublicBroadcastAnnouncementData& lhs,
                const PublicBroadcastAnnouncementData& rhs) {
  if (lhs.features != rhs.features) return false;
  if (!isMetadataSame(lhs.metadata, rhs.metadata)) return false;

  return true;
}
}  // namespace le_audio
}  // namespace bluetooth
+9 −2
Original line number Diff line number Diff line
@@ -32,14 +32,19 @@ namespace le_audio {
namespace broadcaster {
static const uint16_t kBroadcastAudioAnnouncementServiceUuid = 0x1852;
static const uint16_t kBasicAudioAnnouncementServiceUuid = 0x1851;
static const uint16_t kPublicBroadcastAnnouncementServiceUuid = 0x1856;

static const uint8_t kBisIndexInvalid = 0;

bool ToRawPacket(bluetooth::le_audio::BasicAudioAnnouncementData const&,
                 std::vector<uint8_t>&);

void PrepareAdvertisingData(bluetooth::le_audio::BroadcastId& broadcast_id,
                            std::vector<uint8_t>& periodic_data);
void PrepareAdvertisingData(
    bool is_public, const std::string& broadcast_name,
    bluetooth::le_audio::BroadcastId& broadcast_id,
    const bluetooth::le_audio::PublicBroadcastAnnouncementData&
        public_announcement,
    std::vector<uint8_t>& adv_data);
void PreparePeriodicData(
    const bluetooth::le_audio::BasicAudioAnnouncementData& announcement,
    std::vector<uint8_t>& periodic_data);
@@ -169,5 +174,7 @@ namespace bluetooth {
namespace le_audio {
bool operator==(const BasicAudioAnnouncementData& lhs,
                const BasicAudioAnnouncementData& rhs);
bool operator==(const PublicBroadcastAnnouncementData& lhs,
                const PublicBroadcastAnnouncementData& rhs);
}  // namespace le_audio
}  // namespace bluetooth
Loading