blob: 315939ebe41254c7901357c4e7c603cceb78f814 [file] [log] [blame]
/*
* Copyright 2021 HIMSA II K/S - www.himsa.com.
* Represented by EHIMA - www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <base/functional/bind.h>
#include <base/logging.h>
#include <lc3.h>
#include <mutex>
#include "bta/include/bta_le_audio_broadcaster_api.h"
#include "bta/le_audio/broadcaster/state_machine.h"
#include "bta/le_audio/codec_interface.h"
#include "bta/le_audio/content_control_id_keeper.h"
#include "bta/le_audio/le_audio_types.h"
#include "bta/le_audio/le_audio_utils.h"
#include "bta/le_audio/metrics_collector.h"
#include "common/strings.h"
#include "device/include/controller.h"
#include "include/check.h"
#include "internal_include/stack_config.h"
#include "os/log.h"
#include "osi/include/properties.h"
#include "stack/include/bt_types.h"
#include "stack/include/btm_api_types.h"
#include "stack/include/btm_iso_api.h"
using bluetooth::common::ToString;
using bluetooth::hci::IsoManager;
using bluetooth::hci::iso_manager::big_create_cmpl_evt;
using bluetooth::hci::iso_manager::big_terminate_cmpl_evt;
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::DsaMode;
using le_audio::LeAudioCodecConfiguration;
using le_audio::LeAudioSourceAudioHalClient;
using le_audio::broadcaster::BigConfig;
using le_audio::broadcaster::BroadcastCodecWrapper;
using le_audio::broadcaster::BroadcastQosConfig;
using le_audio::broadcaster::BroadcastStateMachine;
using le_audio::broadcaster::BroadcastStateMachineConfig;
using le_audio::broadcaster::IBroadcastStateMachineCallbacks;
using le_audio::types::AudioContexts;
using le_audio::types::CodecLocation;
using le_audio::types::kLeAudioCodingFormatLC3;
using le_audio::types::LeAudioContextType;
using le_audio::types::LeAudioLtvMap;
using le_audio::utils::GetAudioContextsFromSourceMetadata;
namespace {
class LeAudioBroadcasterImpl;
LeAudioBroadcasterImpl* instance;
std::mutex instance_mutex;
/* Class definitions */
/* LeAudioBroadcasterImpl class represents main implementation class for le
* audio broadcaster feature in the stack.
*
* This class may be bonded with Test socket which allows to drive an instance
* for test purposes.
*/
class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
enum class AudioDataPathState {
INACTIVE,
ACTIVE,
SUSPENDED,
};
public:
LeAudioBroadcasterImpl(
bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_)
: callbacks_(callbacks_),
current_phy_(PHY_LE_2M),
audio_data_path_state_(AudioDataPathState::INACTIVE),
le_audio_source_hal_client_(nullptr) {
LOG_INFO();
/* Register State machine callbacks */
BroadcastStateMachine::Initialize(&state_machine_callbacks_,
&state_machine_adv_callbacks_);
GenerateBroadcastIds();
}
~LeAudioBroadcasterImpl() override = default;
void GenerateBroadcastIds(void) {
btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) {
if (!instance) return;
/* LE Rand returns 8 octets. Lets' make 2 outstanding Broadcast Ids out
* of it */
for (int i = 0; i < 8; i += 4) {
BroadcastId broadcast_id = 0;
/* Broadcast ID should be 3 octets long (BAP v1.0 spec.) */
STREAM_TO_UINT24(broadcast_id, rand);
if (broadcast_id == bluetooth::le_audio::kBroadcastIdInvalid) continue;
instance->available_broadcast_ids_.emplace_back(broadcast_id);
}
if (instance->available_broadcast_ids_.empty()) {
LOG_ALWAYS_FATAL("Unable to generate proper broadcast identifiers.");
}
}));
}
void CleanUp() {
LOG_INFO("Broadcaster");
broadcasts_.clear();
callbacks_ = nullptr;
is_iso_running_ = false;
queued_start_broadcast_request_ = std::nullopt;
queued_create_broadcast_request_ = std::nullopt;
if (le_audio_source_hal_client_) {
le_audio_source_hal_client_->Stop();
le_audio_source_hal_client_.reset();
}
}
void Stop() {
LOG_INFO("Broadcaster");
for (auto& sm_pair : broadcasts_) {
StopAudioBroadcast(sm_pair.first);
}
}
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) {
BasicAudioAnnouncementData announcement;
/* Prepare the announcement */
announcement.presentation_delay_us = 40000; /* us */
auto const& codec_id = codec_config.GetLeAudioCodecId();
for (const LeAudioLtvMap& metadata : metadata_group) {
/* Note: Currently we have a single audio source configured with a one
* set of codec/pcm parameters thus we can use a single subgroup
* for all the BISes. Configure common BIS codec params at the
* subgroup level.
*/
BasicAudioAnnouncementSubgroup config = {
.codec_config =
{
.codec_id = codec_id.coding_format,
.vendor_company_id = codec_id.vendor_company_id,
.vendor_codec_id = codec_id.vendor_codec_id,
.codec_specific_params =
codec_config.GetSubgroupCodecSpecData().Values(),
},
.metadata = metadata.Values(),
.bis_configs = {},
};
/* BIS indices range is [1-31] - BASS, Sec.3.2 Broadcast Receive State. */
for (size_t i = 0; i < codec_config.GetNumChannels(); ++i) {
config.bis_configs.push_back(
{.codec_specific_params =
codec_config.GetBisCodecSpecData(i + 1).Values(),
.bis_index = static_cast<uint8_t>(i + 1)});
}
announcement.subgroup_configs.push_back(config);
}
return announcement;
}
void UpdateStreamingContextTypeOnAllSubgroups(const AudioContexts& contexts) {
LOG_DEBUG("%s context_type_map=%s", __func__, contexts.to_string().c_str());
auto ccids = ContentControlIdKeeper::GetInstance()->GetAllCcids(contexts);
if (ccids.empty()) {
LOG_WARN("%s No content providers available for context_type_map=%s.",
__func__, contexts.to_string().c_str());
}
std::vector<uint8_t> stream_context_vec(2);
auto pp = stream_context_vec.data();
UINT16_TO_STREAM(pp, contexts.value());
for (auto const& kv_it : broadcasts_) {
auto& broadcast = kv_it.second;
if (broadcast->GetState() == BroadcastStateMachine::State::STREAMING) {
auto announcement = broadcast->GetBroadcastAnnouncement();
bool broadcast_update = false;
// Replace context type and CCID list
for (auto& subgroup : announcement.subgroup_configs) {
auto subgroup_ltv = LeAudioLtvMap(subgroup.metadata);
bool subgroup_update = false;
auto existing_context = subgroup_ltv.Find(
le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
if (existing_context) {
if (memcmp(stream_context_vec.data(), existing_context->data(),
existing_context->size()) != 0) {
subgroup_ltv.Add(
le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
stream_context_vec);
subgroup_update = true;
}
} else {
subgroup_ltv.Add(
le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
stream_context_vec);
subgroup_update = true;
}
auto existing_ccid_list =
subgroup_ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList);
if (existing_ccid_list) {
if (ccids.empty()) {
subgroup_ltv.Remove(
le_audio::types::kLeAudioMetadataTypeCcidList);
subgroup_update = true;
} else if (!std::is_permutation(ccids.begin(), ccids.end(),
existing_ccid_list->begin())) {
subgroup_ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList,
ccids);
subgroup_update = true;
}
} else if (!ccids.empty()) {
subgroup_ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList,
ccids);
subgroup_update = true;
}
if (subgroup_update) {
subgroup.metadata = subgroup_ltv.Values();
broadcast_update = true;
}
}
if (broadcast_update) {
broadcast->UpdateBroadcastAnnouncement(std::move(announcement));
}
}
}
}
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) override {
std::vector<LeAudioLtvMap> subgroup_ltvs;
if (broadcasts_.count(broadcast_id) == 0) {
LOG_ERROR("No such broadcast_id=%d", broadcast_id);
return;
}
LOG_INFO("For broadcast_id=%d", broadcast_id);
auto& codec_config = broadcasts_[broadcast_id]->GetCodecConfig();
for (const std::vector<uint8_t>& metadata : subgroup_metadata) {
/* Prepare the announcement format */
bool is_metadata_valid;
auto ltv = LeAudioLtvMap::Parse(metadata.data(), metadata.size(), is_metadata_valid);
if (!is_metadata_valid) {
LOG_ERROR("Invalid metadata provided.");
return;
}
auto context_type = AudioContexts(LeAudioContextType::MEDIA);
/* Adds multiple contexts and CCIDs regardless of the incoming audio
* context. Android has only two CCIDs, one for Media and one for
* Conversational context. Even though we are not broadcasting
* Conversational streams, some PTS test cases wants multiple CCIDs.
*/
if (stack_config_get_interface()
->get_pts_force_le_audio_multiple_contexts_metadata()) {
context_type =
LeAudioContextType::MEDIA | LeAudioContextType::CONVERSATIONAL;
auto stream_context_vec = ltv.Find(
le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
if (stream_context_vec) {
auto pp = stream_context_vec.value().data();
if (stream_context_vec.value().size() < 2) {
LOG_ERROR("stream_context_vec.value() size < 2");
return;
}
UINT16_TO_STREAM(pp, context_type.value());
}
}
auto stream_context_vec =
ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
if (stream_context_vec) {
auto pp = stream_context_vec.value().data();
if (stream_context_vec.value().size() < 2) {
LOG_ERROR("stream_context_vec.value() size < 2");
return;
}
STREAM_TO_UINT16(context_type.value_ref(), pp);
}
// Append the CCID list
auto ccid_vec =
ContentControlIdKeeper::GetInstance()->GetAllCcids(context_type);
if (!ccid_vec.empty()) {
ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccid_vec);
}
// Push to subgroup ltvs
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, subgroup_ltvs);
broadcasts_[broadcast_id]->UpdateBroadcastAnnouncement(
std::move(announcement));
}
void CreateAudioBroadcast(
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) override {
uint8_t public_features = 0;
LeAudioLtvMap public_ltv;
std::vector<LeAudioLtvMap> subgroup_ltvs;
if (queued_create_broadcast_request_) {
LOG_ERROR("Not processed yet queued broadcast");
callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid,
false);
return;
}
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.");
callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid,
false);
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();
auto context_type = AudioContexts(LeAudioContextType::MEDIA);
/* Adds multiple contexts and CCIDs regardless of the incoming audio
* context. Android has only two CCIDs, one for Media and one for
* Conversational context. Even though we are not broadcasting
* Conversational streams, some PTS test cases wants multiple CCIDs.
*/
if (stack_config_get_interface()
->get_pts_force_le_audio_multiple_contexts_metadata()) {
context_type =
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;
auto ltv = LeAudioLtvMap::Parse(metadata.data(), metadata.size(), is_metadata_valid);
if (!is_metadata_valid) {
LOG_ERROR("Invalid metadata provided.");
callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid,
false);
return;
}
if (stack_config_get_interface()
->get_pts_force_le_audio_multiple_contexts_metadata()) {
auto stream_context_vec = ltv.Find(
le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
if (stream_context_vec) {
if (stream_context_vec.value().size() < 2) {
LOG_ERROR("kLeAudioMetadataTypeStreamingAudioContext size < 2");
callbacks_->OnBroadcastCreated(
bluetooth::le_audio::kBroadcastIdInvalid, false);
return;
}
auto pp = stream_context_vec.value().data();
UINT16_TO_STREAM(pp, context_type.value());
}
}
auto stream_context_vec =
ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
if (stream_context_vec) {
if (stream_context_vec.value().size() < 2) {
LOG_ERROR("kLeAudioMetadataTypeStreamingAudioContext size < 2");
callbacks_->OnBroadcastCreated(
bluetooth::le_audio::kBroadcastIdInvalid, false);
return;
}
auto pp = stream_context_vec.value().data();
STREAM_TO_UINT16(context_type.value_ref(), pp);
}
// Append the CCID list
auto ccid_vec =
ContentControlIdKeeper::GetInstance()->GetAllCcids(context_type);
if (!ccid_vec.empty()) {
ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList, ccid_vec);
}
// Push to subgroup ltvs
subgroup_ltvs.push_back(ltv);
}
auto codec_qos_pair = [](AudioContexts context_type)
-> std::optional<
std::pair<const BroadcastCodecWrapper, const BroadcastQosConfig>> {
if (CodecManager::GetInstance()->GetCodecLocation() ==
CodecLocation::ADSP) {
auto offload_config =
CodecManager::GetInstance()->GetBroadcastOffloadConfig();
if (offload_config == nullptr) {
return std::nullopt;
}
return std::make_pair(
BroadcastCodecWrapper(
{.coding_format = le_audio::types::kLeAudioCodingFormatLC3,
.vendor_company_id =
le_audio::types::kLeAudioVendorCompanyIdUndefined,
.vendor_codec_id =
le_audio::types::kLeAudioVendorCodecIdUndefined},
{.num_channels =
static_cast<uint8_t>(offload_config->stream_map.size()),
.sample_rate = offload_config->sampling_rate,
.bits_per_sample = offload_config->bits_per_sample,
.data_interval_us = offload_config->frame_duration},
offload_config->octets_per_frame),
BroadcastQosConfig(offload_config->retransmission_number,
offload_config->max_transport_latency));
} else {
return le_audio::broadcaster::getStreamConfigForContext(context_type);
}
}(context_type);
if (!codec_qos_pair) {
LOG_ERROR("No valid broadcast offload config");
return;
}
BroadcastStateMachineConfig msg = {
.is_public = is_public,
.broadcast_id = broadcast_id,
.broadcast_name = broadcast_name,
.streaming_phy = GetStreamingPhy(),
.codec_wrapper = codec_qos_pair->first,
.qos_config = codec_qos_pair->second,
.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);
}
// If there is ongoing ISO traffic, it might be a unicast stream
if (is_iso_running_) {
LOG_INFO("Iso is still active. Queueing broadcast creation for later.");
if (queued_create_broadcast_request_) {
LOG_WARN(
"Already queued. Updating queued broadcast creation with the new "
"configuration.");
}
queued_create_broadcast_request_ = std::move(msg);
return;
}
InstantiateBroadcast(std::move(msg));
}
void InstantiateBroadcast(BroadcastStateMachineConfig msg) {
LOG_INFO("CreateAudioBroadcast");
/* Put the new broadcast on the initialization queue, notify the error and
* drop the pending broadcast data if init fails.
*/
pending_broadcasts_.push_back(
BroadcastStateMachine::CreateInstance(std::move(msg)));
if (!pending_broadcasts_.back()->Initialize()) {
pending_broadcasts_.pop_back();
callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid,
false);
}
}
void SuspendAudioBroadcast(uint32_t broadcast_id) override {
LOG_INFO("broadcast_id=%d", broadcast_id);
if (broadcasts_.count(broadcast_id) != 0) {
LOG_INFO("Stopping AudioHalClient");
if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop();
broadcasts_[broadcast_id]->SetMuted(true);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::SUSPEND, nullptr);
} else {
LOG_ERROR("No such broadcast_id=%d", broadcast_id);
}
}
static bool IsAnyoneStreaming() {
if (!instance) return false;
auto const& iter =
std::find_if(instance->broadcasts_.cbegin(),
instance->broadcasts_.cend(), [](auto const& sm) {
return sm.second->GetState() ==
BroadcastStateMachine::State::STREAMING;
});
return (iter != instance->broadcasts_.cend());
}
void StartAudioBroadcast(uint32_t broadcast_id) override {
LOG_INFO("Starting broadcast_id=%d", broadcast_id);
if (queued_start_broadcast_request_) {
LOG_ERROR("Not processed yet start broadcast request");
return;
}
if (is_iso_running_) {
queued_start_broadcast_request_ = broadcast_id;
return;
}
if (IsAnyoneStreaming()) {
LOG_ERROR("Stop the other broadcast first!");
return;
}
if (broadcasts_.count(broadcast_id) != 0) {
if (!le_audio_source_hal_client_) {
le_audio_source_hal_client_ =
LeAudioSourceAudioHalClient::AcquireBroadcast();
if (!le_audio_source_hal_client_) {
LOG_ERROR("Could not acquire le audio");
return;
}
}
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::START, nullptr);
le_audio::MetricsCollector::Get()->OnBroadcastStateChanged(true);
} else {
LOG_ERROR("No such broadcast_id=%d", broadcast_id);
}
}
void StopAudioBroadcast(uint32_t broadcast_id) override {
if (broadcasts_.count(broadcast_id) == 0) {
LOG_ERROR("no such broadcast_id=%d", broadcast_id);
return;
}
LOG_INFO("Stopping AudioHalClient, broadcast_id=%d", broadcast_id);
if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop();
broadcasts_[broadcast_id]->SetMuted(true);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::STOP, nullptr);
le_audio::MetricsCollector::Get()->OnBroadcastStateChanged(false);
}
void DestroyAudioBroadcast(uint32_t broadcast_id) override {
LOG_INFO("Destroying broadcast_id=%d", broadcast_id);
broadcasts_.erase(broadcast_id);
}
std::optional<bluetooth::le_audio::BroadcastMetadata> GetBroadcastMetadataOpt(
bluetooth::le_audio::BroadcastId broadcast_id) {
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();
metadata.addr_type = kv_it.second->GetOwnAddressType();
metadata.broadcast_code = kv_it.second->GetBroadcastCode();
metadata.basic_audio_announcement =
kv_it.second->GetBroadcastAnnouncement();
metadata.public_announcement =
kv_it.second->GetPublicBroadcastAnnouncement();
return metadata;
}
}
return std::nullopt;
}
void GetBroadcastMetadata(uint32_t broadcast_id) override {
if (broadcasts_.count(broadcast_id) == 0) {
LOG_ERROR("No such broadcast_id=%d", broadcast_id);
return;
}
auto meta = GetBroadcastMetadataOpt(broadcast_id);
if (!meta) {
LOG_ERROR("No metadata for broadcast_id=%d", broadcast_id);
return;
}
callbacks_->OnBroadcastMetadataChanged(broadcast_id,
std::move(meta.value()));
}
void GetAllBroadcastStates(void) override {
for (auto const& kv_it : broadcasts_) {
callbacks_->OnBroadcastStateChanged(
kv_it.second->GetBroadcastId(),
static_cast<bluetooth::le_audio::BroadcastState>(
kv_it.second->GetState()));
}
}
void IsValidBroadcast(
uint32_t broadcast_id, uint8_t addr_type, RawAddress addr,
base::Callback<void(uint8_t /* broadcast_id */, uint8_t /* addr_type */,
RawAddress /* addr */, bool /* is_local */)>
cb) override {
if (broadcasts_.count(broadcast_id) == 0) {
LOG_ERROR("No such broadcast_id=%d", broadcast_id);
std::move(cb).Run(broadcast_id, addr_type, addr, false);
return;
}
broadcasts_[broadcast_id]->RequestOwnAddress(base::Bind(
[](uint32_t broadcast_id, uint8_t req_address_type,
RawAddress req_address,
base::Callback<void(uint8_t /* broadcast_id */,
uint8_t /* addr_type */, RawAddress /* addr */,
bool /* is_local */)>
cb,
uint8_t rcv_address_type, RawAddress rcv_address) {
bool is_local = (req_address_type == rcv_address_type) &&
(req_address == rcv_address);
std::move(cb).Run(broadcast_id, req_address_type, req_address,
is_local);
},
broadcast_id, addr_type, addr, std::move(cb)));
}
void SetStreamingPhy(uint8_t phy) override { current_phy_ = phy; }
uint8_t GetStreamingPhy(void) const override { return current_phy_; }
BroadcastId BroadcastIdFromBigHandle(uint8_t big_handle) const {
auto pair_it =
std::find_if(broadcasts_.begin(), broadcasts_.end(),
[big_handle](auto const& entry) {
return entry.second->GetAdvertisingSid() == big_handle;
});
if (pair_it != broadcasts_.end()) {
return pair_it->second->GetBroadcastId();
}
return bluetooth::le_audio::kBroadcastIdInvalid;
}
void OnSetupIsoDataPath(uint8_t status, uint16_t conn_handle,
uint8_t big_handle) override {
auto broadcast_id = BroadcastIdFromBigHandle(big_handle);
CHECK(broadcasts_.count(broadcast_id) != 0);
broadcasts_[broadcast_id]->OnSetupIsoDataPath(status, conn_handle);
}
void OnRemoveIsoDataPath(uint8_t status, uint16_t conn_handle,
uint8_t big_handle) override {
auto broadcast_id = BroadcastIdFromBigHandle(big_handle);
CHECK(broadcasts_.count(broadcast_id) != 0);
broadcasts_[broadcast_id]->OnRemoveIsoDataPath(status, conn_handle);
}
void OnBigEvent(uint8_t event, void* data) override {
switch (event) {
case bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl: {
auto* evt = static_cast<big_create_cmpl_evt*>(data);
auto broadcast_id = BroadcastIdFromBigHandle(evt->big_id);
CHECK(broadcasts_.count(broadcast_id) != 0);
broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_CREATE_BIG_CPL_EVT,
evt);
} break;
case bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl: {
auto* evt = static_cast<big_terminate_cmpl_evt*>(data);
auto broadcast_id = BroadcastIdFromBigHandle(evt->big_id);
CHECK(broadcasts_.count(broadcast_id) != 0);
broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT,
evt);
le_audio_source_hal_client_.reset();
} break;
default:
LOG_ERROR("Invalid event=%d", event);
}
}
void IsoTrafficEventCb(bool is_active) {
is_iso_running_ = is_active;
LOG_INFO("is_iso_running: %d", is_iso_running_);
if (!is_iso_running_) {
if (queued_start_broadcast_request_) {
auto broadcast_id = *queued_start_broadcast_request_;
queued_start_broadcast_request_ = std::nullopt;
LOG_INFO("Start queued broadcast.");
StartAudioBroadcast(broadcast_id);
}
if (queued_create_broadcast_request_) {
auto broadcast_msg = std::move(*queued_create_broadcast_request_);
queued_create_broadcast_request_ = std::nullopt;
LOG_INFO("Create queued broadcast.");
InstantiateBroadcast(std::move(broadcast_msg));
}
}
}
void Dump(int fd) {
std::stringstream stream;
stream << " Number of broadcasts: " << broadcasts_.size() << "\n";
for (auto& broadcast_pair : broadcasts_) {
auto& broadcast = broadcast_pair.second;
if (broadcast) stream << *broadcast;
}
dprintf(fd, "%s", stream.str().c_str());
}
private:
static class BroadcastStateMachineCallbacks
: public IBroadcastStateMachineCallbacks {
void OnStateMachineCreateStatus(uint32_t broadcast_id,
bool initialized) override {
auto pending_broadcast = std::find_if(
instance->pending_broadcasts_.begin(),
instance->pending_broadcasts_.end(), [broadcast_id](auto& sm) {
return (sm->GetBroadcastId() == broadcast_id);
});
LOG_ASSERT(pending_broadcast != instance->pending_broadcasts_.end());
LOG_ASSERT(instance->broadcasts_.count(broadcast_id) == 0);
if (initialized) {
const uint32_t broadcast_id = (*pending_broadcast)->GetBroadcastId();
LOG_INFO("broadcast_id=%d state=%s", broadcast_id,
ToString((*pending_broadcast)->GetState()).c_str());
instance->broadcasts_[broadcast_id] = std::move(*pending_broadcast);
} else {
LOG_ERROR("Failed creating broadcast!");
}
instance->pending_broadcasts_.erase(pending_broadcast);
instance->callbacks_->OnBroadcastCreated(broadcast_id, initialized);
}
void OnStateMachineDestroyed(uint32_t broadcast_id) override {
/* This is a special case when state machine destructor calls this
* callback. It may happen during the Cleanup() call when all state
* machines are erased and instance can already be set to null to avoid
* unnecessary calls.
*/
if (instance) instance->callbacks_->OnBroadcastDestroyed(broadcast_id);
}
static int getStreamerCount() {
return std::count_if(instance->broadcasts_.begin(),
instance->broadcasts_.end(), [](auto const& sm) {
LOG_VERBOSE(
"broadcast_id=%d, state=%s",
sm.second->GetBroadcastId(),
ToString(sm.second->GetState()).c_str());
return sm.second->GetState() ==
BroadcastStateMachine::State::STREAMING;
});
}
void OnStateMachineEvent(uint32_t broadcast_id,
BroadcastStateMachine::State state,
const void* data) override {
LOG_INFO("broadcast_id=%d state=%s", broadcast_id,
ToString(state).c_str());
switch (state) {
case BroadcastStateMachine::State::STOPPED:
/* Pass through */
case BroadcastStateMachine::State::CONFIGURING:
/* Pass through */
case BroadcastStateMachine::State::CONFIGURED:
/* Pass through */
case BroadcastStateMachine::State::STOPPING:
/* Nothing to do here? */
break;
case BroadcastStateMachine::State::STREAMING:
if (getStreamerCount() == 1) {
LOG_INFO("Starting AudioHalClient");
if (instance->broadcasts_.count(broadcast_id) != 0) {
const auto& broadcast = instance->broadcasts_.at(broadcast_id);
// Reconfigure encoder instance for the new stream requirements
audio_receiver_.setCurrentCodecConfig(
broadcast->GetCodecConfig());
audio_receiver_.CheckAndReconfigureEncoders();
broadcast->SetMuted(false);
auto cfg = static_cast<const LeAudioCodecConfiguration*>(data);
auto is_started = instance->le_audio_source_hal_client_->Start(
*cfg, &audio_receiver_);
if (!is_started) {
/* Audio Source setup failed - stop the broadcast */
instance->StopAudioBroadcast(broadcast_id);
return;
}
instance->audio_data_path_state_ = AudioDataPathState::ACTIVE;
}
}
break;
};
instance->callbacks_->OnBroadcastStateChanged(
broadcast_id,
static_cast<bluetooth::le_audio::BroadcastState>(state));
}
void OnOwnAddressResponse(uint32_t broadcast_id, uint8_t addr_type,
RawAddress addr) override {
/* Not used currently */
}
void OnBigCreated(const std::vector<uint16_t>& conn_handle) {
CodecManager::GetInstance()->UpdateBroadcastConnHandle(
conn_handle,
std::bind(
&LeAudioSourceAudioHalClient::UpdateBroadcastAudioConfigToHal,
instance->le_audio_source_hal_client_.get(),
std::placeholders::_1));
}
} state_machine_callbacks_;
static class BroadcastAdvertisingCallbacks : public AdvertisingCallbacks {
void OnAdvertisingSetStarted(int reg_id, uint8_t advertiser_id,
int8_t tx_power, uint8_t status) {
if (!instance) return;
if (reg_id == BroadcastStateMachine::kLeAudioBroadcastRegId &&
!instance->pending_broadcasts_.empty()) {
instance->pending_broadcasts_.back()->OnCreateAnnouncement(
advertiser_id, tx_power, status);
} else {
LOG_WARN(
"Ignored OnAdvertisingSetStarted callback reg_id:%d "
"advertiser_id:%d",
reg_id, advertiser_id);
}
}
void OnAdvertisingEnabled(uint8_t advertiser_id, bool enable,
uint8_t status) {
if (!instance) return;
auto const& iter = std::find_if(
instance->broadcasts_.cbegin(), instance->broadcasts_.cend(),
[advertiser_id](auto const& sm) {
return sm.second->GetAdvertisingSid() == advertiser_id;
});
if (iter != instance->broadcasts_.cend()) {
iter->second->OnEnableAnnouncement(enable, status);
} else {
LOG_WARN("Ignored OnAdvertisingEnabled callback advertiser_id:%d",
advertiser_id);
}
}
void OnAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) {
LOG_WARN(
"Not being used, ignored OnAdvertisingDataSet callback "
"advertiser_id:%d",
advertiser_id);
}
void OnScanResponseDataSet(uint8_t advertiser_id, uint8_t status) {
LOG_WARN(
"Not being used, ignored OnScanResponseDataSet callback "
"advertiser_id:%d",
advertiser_id);
}
void OnAdvertisingParametersUpdated(uint8_t advertiser_id, int8_t tx_power,
uint8_t status) {
LOG_WARN(
"Not being used, ignored OnAdvertisingParametersUpdated callback "
"advertiser_id:%d",
advertiser_id);
}
void OnPeriodicAdvertisingParametersUpdated(uint8_t advertiser_id,
uint8_t status) {
LOG_WARN(
"Not being used, ignored OnPeriodicAdvertisingParametersUpdated "
"callback advertiser_id:%d",
advertiser_id);
}
void OnPeriodicAdvertisingDataSet(uint8_t advertiser_id, uint8_t status) {
LOG_WARN(
"Not being used, ignored OnPeriodicAdvertisingDataSet callback "
"advertiser_id:%d",
advertiser_id);
}
void OnPeriodicAdvertisingEnabled(uint8_t advertiser_id, bool enable,
uint8_t status) {
LOG_WARN(
"Not being used, ignored OnPeriodicAdvertisingEnabled callback "
"advertiser_id:%d",
advertiser_id);
}
void OnOwnAddressRead(uint8_t advertiser_id, uint8_t address_type,
RawAddress address) {
LOG_WARN(
"Not being used, ignored OnOwnAddressRead callback advertiser_id:%d",
advertiser_id);
}
} state_machine_adv_callbacks_;
static class LeAudioSourceCallbacksImpl
: public LeAudioSourceAudioHalClient::Callbacks {
public:
LeAudioSourceCallbacksImpl()
: codec_wrapper_(le_audio::broadcaster::getStreamConfigForContext(
AudioContexts(LeAudioContextType::UNSPECIFIED))
.first) {}
void CheckAndReconfigureEncoders() {
auto const& codec_id = codec_wrapper_.GetLeAudioCodecId();
/* TODO: We should act smart and reuse current configurations */
sw_enc_.clear();
while (sw_enc_.size() != codec_wrapper_.GetNumChannels()) {
auto codec = le_audio::CodecInterface::CreateInstance(codec_id);
auto codec_status =
codec->InitEncoder(codec_wrapper_.GetLeAudioCodecConfiguration(),
codec_wrapper_.GetLeAudioCodecConfiguration());
if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
LOG_ERROR("Channel %d codec setup failed with err: %d",
(uint32_t)sw_enc_.size(), codec_status);
return;
}
sw_enc_.emplace_back(std::move(codec));
}
}
const BroadcastCodecWrapper& getCurrentCodecConfig(void) const {
return codec_wrapper_;
}
void setCurrentCodecConfig(BroadcastCodecWrapper const& config) {
codec_wrapper_ = config;
}
static void sendBroadcastData(
const std::unique_ptr<BroadcastStateMachine>& broadcast,
std::vector<std::unique_ptr<le_audio::CodecInterface>>& encoders) {
auto const& config = broadcast->GetBigConfig();
if (config == std::nullopt) {
LOG_ERROR(
"Broadcast broadcast_id=%d has no valid BIS configurations in "
"state=%s",
broadcast->GetBroadcastId(),
ToString(broadcast->GetState()).c_str());
return;
}
if (config->connection_handles.size() < encoders.size()) {
LOG_ERROR("Not enough BIS'es to broadcast all channels!");
return;
}
for (uint8_t chan = 0; chan < encoders.size(); ++chan) {
IsoManager::GetInstance()->SendIsoData(
config->connection_handles[chan],
(const uint8_t*)encoders[chan]->GetDecodedSamples().data(),
encoders[chan]->GetDecodedSamples().size() * 2);
}
}
virtual void OnAudioDataReady(const std::vector<uint8_t>& data) override {
if (!instance) return;
LOG_VERBOSE("Received %zu bytes.", data.size());
/* Constants for the channel data configuration */
const auto num_channels = codec_wrapper_.GetNumChannels();
const auto bytes_per_sample = (codec_wrapper_.GetBitsPerSample() / 8);
/* Prepare encoded data for all channels */
for (uint8_t chan = 0; chan < num_channels; ++chan) {
auto initial_channel_offset = chan * bytes_per_sample;
sw_enc_[chan]->Encode(data.data() + initial_channel_offset,
num_channels,
codec_wrapper_.GetOctetsPerCodecFrame());
}
/* Currently there is no way to broadcast multiple distinct streams.
* We just receive all system sounds mixed into a one stream and each
* broadcast gets the same data.
*/
for (auto& broadcast_pair : instance->broadcasts_) {
auto& broadcast = broadcast_pair.second;
if ((broadcast->GetState() ==
BroadcastStateMachine::State::STREAMING) &&
!broadcast->IsMuted())
sendBroadcastData(broadcast, sw_enc_);
}
LOG_VERBOSE("All data sent.");
}
virtual void OnAudioSuspend(void) override {
LOG_INFO();
/* TODO: Should we suspend all broadcasts - remove BIGs? */
if (instance)
instance->audio_data_path_state_ = AudioDataPathState::SUSPENDED;
}
virtual void OnAudioResume(void) override {
LOG_INFO();
if (!instance) return;
/* TODO: Should we resume all broadcasts - recreate BIGs? */
instance->audio_data_path_state_ = AudioDataPathState::ACTIVE;
if (!IsAnyoneStreaming()) {
instance->le_audio_source_hal_client_->CancelStreamingRequest();
return;
}
instance->le_audio_source_hal_client_->ConfirmStreamingRequest();
}
virtual void OnAudioMetadataUpdate(source_metadata_v7 source_metadata,
DsaMode dsa_mode) override {
LOG_INFO();
if (!instance) return;
/* TODO: Should we take supported contexts from ASCS? */
auto contexts = GetAudioContextsFromSourceMetadata(source_metadata);
if (contexts.any()) {
/* NOTICE: We probably don't want to change the stream configuration
* on each metadata change, so just update the context type metadata.
* Since we are not able to identify individual track streams and
* they are all mixed inside a single data stream, we will update
* the metadata of all BIS subgroups with the same combined context.
*/
instance->UpdateStreamingContextTypeOnAllSubgroups(contexts);
}
}
private:
BroadcastCodecWrapper codec_wrapper_;
std::vector<std::unique_ptr<le_audio::CodecInterface>> sw_enc_;
} audio_receiver_;
bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_;
std::map<uint32_t, std::unique_ptr<BroadcastStateMachine>> broadcasts_;
std::vector<std::unique_ptr<BroadcastStateMachine>> pending_broadcasts_;
std::optional<BroadcastStateMachineConfig> queued_create_broadcast_request_;
std::optional<uint32_t> queued_start_broadcast_request_;
/* Some BIG params are set globally */
uint8_t current_phy_;
AudioDataPathState audio_data_path_state_;
std::unique_ptr<LeAudioSourceAudioHalClient> le_audio_source_hal_client_;
std::vector<BroadcastId> available_broadcast_ids_;
// Flag to track iso state
bool is_iso_running_ = false;
};
/* Static members definitions */
LeAudioBroadcasterImpl::BroadcastStateMachineCallbacks
LeAudioBroadcasterImpl::state_machine_callbacks_;
LeAudioBroadcasterImpl::LeAudioSourceCallbacksImpl
LeAudioBroadcasterImpl::audio_receiver_;
LeAudioBroadcasterImpl::BroadcastAdvertisingCallbacks
LeAudioBroadcasterImpl::state_machine_adv_callbacks_;
} /* namespace */
void LeAudioBroadcaster::Initialize(
bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks,
base::Callback<bool()> audio_hal_verifier) {
std::scoped_lock<std::mutex> lock(instance_mutex);
LOG_INFO();
if (instance) {
LOG_ERROR("Already initialized");
return;
}
if (!controller_get_interface()->SupportsBleIsochronousBroadcaster() &&
!osi_property_get_bool("persist.bluetooth.fake_iso_support", false)) {
LOG_WARN("Isochronous Broadcast not supported by the controller!");
return;
}
if (!std::move(audio_hal_verifier).Run()) {
LOG_ALWAYS_FATAL("HAL requirements not met. Init aborted.");
}
IsoManager::GetInstance()->Start();
instance = new LeAudioBroadcasterImpl(callbacks);
/* Register HCI event handlers */
IsoManager::GetInstance()->RegisterBigCallbacks(instance);
/* Register for active traffic */
IsoManager::GetInstance()->RegisterOnIsoTrafficActiveCallback(
[](bool is_active) {
if (instance) instance->IsoTrafficEventCb(is_active);
});
}
bool LeAudioBroadcaster::IsLeAudioBroadcasterRunning() { return instance; }
LeAudioBroadcaster* LeAudioBroadcaster::Get(void) {
LOG_INFO();
CHECK(instance);
return instance;
}
void LeAudioBroadcaster::Stop(void) {
LOG_INFO();
if (instance) {
instance->Stop();
}
}
void LeAudioBroadcaster::Cleanup(void) {
std::scoped_lock<std::mutex> lock(instance_mutex);
LOG_INFO();
if (instance == nullptr) return;
LeAudioBroadcasterImpl* ptr = instance;
instance = nullptr;
ptr->CleanUp();
delete ptr;
}
void LeAudioBroadcaster::DebugDump(int fd) {
std::scoped_lock<std::mutex> lock(instance_mutex);
dprintf(fd, "Le Audio Broadcaster:\n");
if (instance) instance->Dump(fd);
dprintf(fd, "\n");
}