| /* |
| * 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 "bta/le_audio/broadcaster/state_machine.h" |
| |
| #include <bind_helpers.h> |
| #include <bluetooth/log.h> |
| |
| #include <functional> |
| #include <iostream> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "bta/le_audio/broadcaster/broadcaster_types.h" |
| #include "bta/le_audio/codec_manager.h" |
| #include "bta/le_audio/le_audio_types.h" |
| #include "common/strings.h" |
| #include "hci/le_advertising_manager.h" |
| #include "os/log.h" |
| #include "osi/include/properties.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::le_audio::CodecManager; |
| using bluetooth::le_audio::types::CodecLocation; |
| |
| using namespace bluetooth::le_audio::broadcaster; |
| using namespace bluetooth; |
| |
| namespace { |
| |
| // Advertising channels. These should be kept the same as those defined in the |
| // stack. |
| const int kAdvertisingChannel37 = (1 << 0); |
| const int kAdvertisingChannel38 = (1 << 1); |
| const int kAdvertisingChannel39 = (1 << 2); |
| const int kAdvertisingChannelAll = |
| (kAdvertisingChannel37 | kAdvertisingChannel38 | kAdvertisingChannel39); |
| |
| class BroadcastStateMachineImpl : public BroadcastStateMachine { |
| public: |
| BroadcastStateMachineImpl(BroadcastStateMachineConfig msg) |
| : active_config_(std::nullopt), |
| sm_config_(std::move(msg)), |
| suspending_(false) {} |
| |
| ~BroadcastStateMachineImpl() { |
| if (GetState() == State::STREAMING) TerminateBig(); |
| DestroyBroadcastAnnouncement(); |
| if (callbacks_) callbacks_->OnStateMachineDestroyed(GetBroadcastId()); |
| } |
| |
| bool Initialize() override { |
| static constexpr uint8_t sNumBisMax = 31; |
| |
| if (sm_config_.config.GetNumBisTotal() > sNumBisMax) { |
| log::error( |
| "Channel count of {} exceeds the maximum number of possible BISes, " |
| "which is {}", |
| sm_config_.config.GetNumBisTotal(), sNumBisMax); |
| return false; |
| } |
| |
| CreateBroadcastAnnouncement( |
| sm_config_.is_public, sm_config_.broadcast_name, |
| sm_config_.broadcast_id, sm_config_.public_announcement, |
| sm_config_.announcement, sm_config_.streaming_phy); |
| return true; |
| } |
| |
| const std::vector<BroadcastSubgroupCodecConfig>& GetCodecConfig() |
| const override { |
| return sm_config_.config.subgroups; |
| } |
| |
| const BroadcastConfiguration& GetBroadcastConfig() const override { |
| return sm_config_.config; |
| } |
| |
| std::optional<BigConfig> const& GetBigConfig() const override { |
| return active_config_; |
| } |
| |
| BroadcastStateMachineConfig const& GetStateMachineConfig() const override { |
| return sm_config_; |
| } |
| |
| void RequestOwnAddress( |
| base::Callback<void(uint8_t /* address_type*/, RawAddress /*address*/)> |
| cb) override { |
| uint8_t advertising_sid = GetAdvertisingSid(); |
| advertiser_if_->GetOwnAddress(advertising_sid, cb); |
| } |
| |
| void RequestOwnAddress(void) override { |
| auto broadcast_id = GetBroadcastId(); |
| RequestOwnAddress( |
| base::Bind(&IBroadcastStateMachineCallbacks::OnOwnAddressResponse, |
| base::Unretained(this->callbacks_), broadcast_id)); |
| } |
| |
| RawAddress GetOwnAddress() override { return addr_; } |
| |
| uint8_t GetOwnAddressType() override { return addr_type_; } |
| |
| bluetooth::le_audio::BroadcastId GetBroadcastId() const override { |
| return sm_config_.broadcast_id; |
| } |
| |
| std::optional<bluetooth::le_audio::BroadcastCode> GetBroadcastCode() |
| const override { |
| return sm_config_.broadcast_code; |
| } |
| |
| const bluetooth::le_audio::BasicAudioAnnouncementData& |
| GetBroadcastAnnouncement() const override { |
| return sm_config_.announcement; |
| } |
| |
| bool IsPublicBroadcast() override { return sm_config_.is_public; } |
| |
| std::string GetBroadcastName() override { return sm_config_.broadcast_name; } |
| |
| const bluetooth::le_audio::PublicBroadcastAnnouncementData& |
| GetPublicBroadcastAnnouncement() const override { |
| return sm_config_.public_announcement; |
| } |
| |
| void OnCreateAnnouncement(uint8_t advertising_sid, int8_t tx_power, |
| uint8_t status) { |
| log::info("advertising_sid={} tx_power={} status={}", advertising_sid, |
| tx_power, status); |
| |
| /* If this callback gets called the advertising_sid is valid even though the |
| * status can be other than SUCCESS. |
| */ |
| advertising_sid_ = advertising_sid; |
| |
| if (status != |
| bluetooth::hci::AdvertisingCallback::AdvertisingStatus::SUCCESS) { |
| log::error("Creating Announcement failed"); |
| callbacks_->OnStateMachineCreateStatus(GetBroadcastId(), false); |
| return; |
| } |
| |
| advertiser_if_->GetOwnAddress( |
| advertising_sid, |
| base::Bind(&BroadcastStateMachineImpl::OnAddressResponse, |
| base::Unretained(this))); |
| } |
| |
| void OnEnableAnnouncement(bool enable, uint8_t status) { |
| log::info("operation={}, broadcast_id={}, status={}", |
| enable ? "enable" : "disable", GetBroadcastId(), status); |
| |
| if (status == |
| bluetooth::hci::AdvertisingCallback::AdvertisingStatus::SUCCESS) { |
| /* Periodic is enabled but without BIGInfo. Stream is suspended. */ |
| if (enable) { |
| SetState(State::CONFIGURED); |
| /* Target state is always STREAMING state - start it now. */ |
| ProcessMessage(Message::START); |
| } else { |
| /* User wanted to stop the announcement - report target state reached */ |
| SetState(State::STOPPED); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState()); |
| } |
| } else { |
| // Handle error case |
| if (enable) { |
| /* Error on enabling */ |
| SetState(State::STOPPED); |
| } else { |
| /* Error on disabling */ |
| SetState(State::CONFIGURED); |
| } |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState()); |
| } |
| } |
| |
| void UpdatePublicBroadcastAnnouncement( |
| uint32_t broadcast_id, const std::string& broadcast_name, |
| const bluetooth::le_audio::PublicBroadcastAnnouncementData& announcement) |
| override { |
| std::vector<uint8_t> adv_data; |
| PrepareAdvertisingData(true, broadcast_name, broadcast_id, announcement, |
| adv_data); |
| |
| sm_config_.broadcast_name = broadcast_name; |
| sm_config_.public_announcement = announcement; |
| advertiser_if_->SetData(advertising_sid_, false, adv_data, |
| base::DoNothing()); |
| } |
| |
| void UpdateBroadcastAnnouncement( |
| bluetooth::le_audio::BasicAudioAnnouncementData announcement) override { |
| std::vector<uint8_t> periodic_data; |
| PreparePeriodicData(announcement, periodic_data); |
| |
| sm_config_.announcement = std::move(announcement); |
| advertiser_if_->SetPeriodicAdvertisingData(advertising_sid_, periodic_data, |
| base::DoNothing()); |
| } |
| |
| void ProcessMessage(Message msg, const void* data = nullptr) override { |
| log::info("broadcast_id={}, state={}, message={}", GetBroadcastId(), |
| ToString(GetState()), ToString(msg)); |
| switch (msg) { |
| case Message::START: |
| start_msg_handlers[StateMachine::GetState()](data); |
| break; |
| case Message::STOP: |
| stop_msg_handlers[StateMachine::GetState()](data); |
| break; |
| case Message::SUSPEND: |
| suspend_msg_handlers[StateMachine::GetState()](data); |
| break; |
| }; |
| } |
| |
| static IBroadcastStateMachineCallbacks* callbacks_; |
| static BleAdvertiserInterface* advertiser_if_; |
| |
| private: |
| std::optional<BigConfig> active_config_; |
| BroadcastStateMachineConfig sm_config_; |
| bool suspending_; |
| |
| /* Message handlers for each possible state */ |
| typedef std::function<void(const void*)> msg_handler_t; |
| const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> |
| start_msg_handlers{ |
| /* in STOPPED state */ |
| [this](const void*) { |
| SetState(State::CONFIGURING); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState()); |
| EnableAnnouncement(); |
| }, |
| /* in CONFIGURING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURED state */ |
| [this](const void*) { CreateBig(); }, |
| /* in STOPPING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in STREAMING state */ |
| [](const void*) { /* Do nothing */ }}; |
| |
| const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> |
| stop_msg_handlers{ |
| /* in STOPPED state */ |
| [](const void*) { /* Already stopped */ }, |
| /* in CONFIGURING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURED state */ |
| [this](const void*) { |
| SetState(State::STOPPING); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState()); |
| DisableAnnouncement(); |
| }, |
| /* in STOPPING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in STREAMING state */ |
| [this](const void*) { |
| if ((active_config_ != std::nullopt) && !suspending_) { |
| suspending_ = false; |
| SetState(State::STOPPING); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState()); |
| TriggerIsoDatapathTeardown(active_config_->connection_handles[0]); |
| } |
| }}; |
| |
| const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> |
| suspend_msg_handlers{ |
| /* in STOPPED state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURED state */ |
| [](const void*) { /* Already suspended */ }, |
| /* in STOPPING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in STREAMING state */ |
| [this](const void*) { |
| if ((active_config_ != std::nullopt) && !suspending_) { |
| suspending_ = true; |
| TriggerIsoDatapathTeardown(active_config_->connection_handles[0]); |
| } |
| }}; |
| |
| const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> |
| resume_msg_handlers{/* in STOPPED state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in CONFIGURED state */ |
| [this](const void*) { CreateBig(); }, |
| /* in STOPPING state */ |
| [](const void*) { /* Do nothing */ }, |
| /* in STREAMING state */ |
| [](const void*) { /* Already streaming */ }}; |
| |
| void OnAddressResponse(uint8_t addr_type, RawAddress addr) { |
| log::info("own address={}, type={}", addr, addr_type); |
| addr_ = addr; |
| addr_type_ = addr_type; |
| |
| /* Ext. advertisings are already on */ |
| SetState(State::CONFIGURED); |
| |
| callbacks_->OnStateMachineCreateStatus(GetBroadcastId(), true); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), State::CONFIGURED); |
| } |
| |
| void CreateBroadcastAnnouncement( |
| bool is_public, const std::string& broadcast_name, |
| bluetooth::le_audio::BroadcastId& broadcast_id, |
| const bluetooth::le_audio::PublicBroadcastAnnouncementData& |
| public_announcement, |
| const bluetooth::le_audio::BasicAudioAnnouncementData& announcement, |
| uint8_t streaming_phy) { |
| log::info("is_public={}, broadcast_name={}, public_features={}", |
| is_public ? "public" : "non-public", broadcast_name, |
| public_announcement.features); |
| if (advertiser_if_ != nullptr) { |
| AdvertiseParameters adv_params; |
| PeriodicAdvertisingParameters periodic_params; |
| std::vector<uint8_t> adv_data; |
| std::vector<uint8_t> periodic_data; |
| |
| PrepareAdvertisingData(is_public, broadcast_name, broadcast_id, |
| public_announcement, adv_data); |
| PreparePeriodicData(announcement, periodic_data); |
| |
| adv_params.min_interval = 0x00A0; /* 160 * 0,625 = 100ms */ |
| adv_params.max_interval = 0x0140; /* 320 * 0,625 = 200ms */ |
| adv_params.advertising_event_properties = 0; |
| adv_params.channel_map = kAdvertisingChannelAll; |
| adv_params.tx_power = 8; |
| adv_params.primary_advertising_phy = PHY_LE_1M; |
| adv_params.secondary_advertising_phy = streaming_phy; |
| adv_params.scan_request_notification_enable = 0; |
| adv_params.own_address_type = kBroadcastAdvertisingType; |
| |
| periodic_params.max_interval = BroadcastStateMachine::kPaIntervalMax; |
| periodic_params.min_interval = BroadcastStateMachine::kPaIntervalMin; |
| periodic_params.periodic_advertising_properties = 0; |
| periodic_params.enable = true; |
| |
| /* Status and timeout callbacks are handled by OnAdvertisingSetStarted() |
| * which returns the status and handle to be used later in CreateBIG |
| * command. |
| */ |
| advertiser_if_->StartAdvertisingSet( |
| kAdvertiserClientIdLeAudio, kLeAudioBroadcastRegId, base::DoNothing(), |
| adv_params, adv_data, std::vector<uint8_t>(), periodic_params, |
| periodic_data, 0 /* duration */, 0 /* maxExtAdvEvents */, |
| base::DoNothing()); |
| } |
| } |
| |
| void DestroyBroadcastAnnouncement() { |
| advertiser_if_->Unregister(GetAdvertisingSid()); |
| } |
| |
| void EnableAnnouncement() { |
| log::info("broadcast_id={}", GetBroadcastId()); |
| // Callback is handled by OnAdvertisingEnabled() which returns the status |
| advertiser_if_->Enable(GetAdvertisingSid(), true, base::DoNothing(), 0, |
| 0, /* Enable until stopped */ |
| base::DoNothing()); |
| } |
| |
| void CreateBig(void) { |
| log::info("broadcast_id={}", GetBroadcastId()); |
| /* TODO: Figure out how to decide on the currently hard-codded params. */ |
| struct bluetooth::hci::iso_manager::big_create_params big_params = { |
| .adv_handle = GetAdvertisingSid(), |
| .num_bis = sm_config_.config.GetNumBisTotal(), |
| .sdu_itv = sm_config_.config.GetSduIntervalUs(), |
| .max_sdu_size = sm_config_.config.GetMaxSduOctets(), |
| .max_transport_latency = sm_config_.config.qos.getMaxTransportLatency(), |
| .rtn = sm_config_.config.qos.getRetransmissionNumber(), |
| .phy = sm_config_.streaming_phy, |
| .packing = 0x00, /* Sequencial */ |
| .framing = 0x00, /* Unframed */ |
| .enc = static_cast<uint8_t>(sm_config_.broadcast_code ? 1 : 0), |
| .enc_code = sm_config_.broadcast_code ? *sm_config_.broadcast_code |
| : std::array<uint8_t, 16>({0}), |
| }; |
| |
| IsoManager::GetInstance()->CreateBig(GetAdvertisingSid(), |
| std::move(big_params)); |
| } |
| |
| void DisableAnnouncement(void) { |
| log::info("broadcast_id={}", GetBroadcastId()); |
| // Callback is handled by OnAdvertisingEnabled() which returns the status |
| advertiser_if_->Enable(GetAdvertisingSid(), false, base::DoNothing(), 0, 0, |
| base::DoNothing()); |
| } |
| |
| void TerminateBig() { |
| log::info("suspending={}", suspending_); |
| /* Terminate with reason: Connection Terminated By Local Host */ |
| IsoManager::GetInstance()->TerminateBig(GetAdvertisingSid(), 0x16); |
| } |
| |
| void OnSetupIsoDataPath(uint8_t status, uint16_t conn_hdl) override { |
| log::assert_that(active_config_ != std::nullopt, |
| "assert failed: active_config_ != std::nullopt"); |
| |
| if (status != 0) { |
| log::error("Failure creating data path. Tearing down the BIG now."); |
| suspending_ = true; |
| TerminateBig(); |
| return; |
| } |
| |
| /* Look for the next BIS handle */ |
| auto handle_it = std::find_if( |
| active_config_->connection_handles.begin(), |
| active_config_->connection_handles.end(), |
| [conn_hdl](const auto& handle) { return conn_hdl == handle; }); |
| log::assert_that( |
| handle_it != active_config_->connection_handles.end(), |
| "assert failed: handle_it != active_config_->connection_handles.end()"); |
| handle_it = std::next(handle_it); |
| |
| if (handle_it == active_config_->connection_handles.end()) { |
| /* It was the last BIS to set up - change state to streaming */ |
| SetState(State::STREAMING); |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState(), nullptr); |
| } else { |
| /* Note: We would feed a watchdog here if we had one */ |
| /* There are more BISes to set up data path for */ |
| log::info("There is more data paths to set up."); |
| TriggerIsoDatapathSetup(*handle_it); |
| } |
| } |
| |
| void OnRemoveIsoDataPath(uint8_t status, uint16_t conn_handle) override { |
| log::assert_that(active_config_ != std::nullopt, |
| "assert failed: active_config_ != std::nullopt"); |
| |
| if (status != 0) { |
| log::error("Failure removing data path. Tearing down the BIG now."); |
| TerminateBig(); |
| return; |
| } |
| |
| /* Look for the next BIS handle */ |
| auto handle_it = std::find_if( |
| active_config_->connection_handles.begin(), |
| active_config_->connection_handles.end(), |
| [conn_handle](const auto& handle) { return conn_handle == handle; }); |
| log::assert_that( |
| handle_it != active_config_->connection_handles.end(), |
| "assert failed: handle_it != active_config_->connection_handles.end()"); |
| handle_it = std::next(handle_it); |
| |
| if (handle_it == active_config_->connection_handles.end()) { |
| /* It was the last one to set up - start tearing down the BIG */ |
| TerminateBig(); |
| } else { |
| /* Note: We would feed a watchdog here if we had one */ |
| /* There are more BISes to tear down data path for */ |
| log::info("There is more data paths to tear down."); |
| TriggerIsoDatapathTeardown(*handle_it); |
| } |
| } |
| |
| void TriggerIsoDatapathSetup(uint16_t conn_handle) { |
| log::info("conn_hdl={}", conn_handle); |
| log::assert_that(active_config_ != std::nullopt, |
| "assert failed: active_config_ != std::nullopt"); |
| |
| /* Note: If coding format is transparent, 'codec_id_company' and |
| * 'codec_id_vendor' shall be ignored. |
| */ |
| auto& iso_datapath_config = sm_config_.config.data_path.isoDataPathConfig; |
| bluetooth::hci::iso_manager::iso_data_path_params param = { |
| .data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionIn, |
| .data_path_id = |
| static_cast<uint8_t>(sm_config_.config.data_path.dataPathId), |
| .codec_id_format = static_cast<uint8_t>( |
| iso_datapath_config.isTransparent |
| ? bluetooth::hci::kIsoCodingFormatTransparent |
| : iso_datapath_config.codecId.coding_format), |
| .codec_id_company = static_cast<uint16_t>( |
| iso_datapath_config.isTransparent |
| ? 0x0000 |
| : iso_datapath_config.codecId.vendor_company_id), |
| .codec_id_vendor = static_cast<uint16_t>( |
| iso_datapath_config.isTransparent |
| ? 0x0000 |
| : iso_datapath_config.codecId.vendor_codec_id), |
| .controller_delay = iso_datapath_config.controllerDelayUs, |
| .codec_conf = iso_datapath_config.configuration, |
| }; |
| IsoManager::GetInstance()->SetupIsoDataPath(conn_handle, std::move(param)); |
| } |
| |
| void TriggerIsoDatapathTeardown(uint16_t conn_handle) { |
| log::info("conn_hdl={}", conn_handle); |
| log::assert_that(active_config_ != std::nullopt, |
| "assert failed: active_config_ != std::nullopt"); |
| |
| SetMuted(true); |
| IsoManager::GetInstance()->RemoveIsoDataPath( |
| conn_handle, |
| bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput); |
| } |
| |
| void HandleHciEvent(uint16_t event, void* data) override { |
| switch (event) { |
| case HCI_BLE_CREATE_BIG_CPL_EVT: { |
| auto* evt = static_cast<big_create_cmpl_evt*>(data); |
| |
| if (evt->big_id != GetAdvertisingSid()) { |
| log::error("State={}, Event={}, Unknown big, big_id={}", |
| ToString(GetState()), event, evt->big_id); |
| break; |
| } |
| |
| if (evt->status == 0x00) { |
| log::info("BIG create BIG complete, big_id={}", evt->big_id); |
| active_config_ = { |
| .status = evt->status, |
| .big_id = evt->big_id, |
| .big_sync_delay = evt->big_sync_delay, |
| .transport_latency_big = evt->transport_latency_big, |
| .phy = evt->phy, |
| .nse = evt->nse, |
| .bn = evt->bn, |
| .pto = evt->pto, |
| .irc = evt->irc, |
| .max_pdu = evt->max_pdu, |
| .iso_interval = evt->iso_interval, |
| .connection_handles = evt->conn_handles, |
| }; |
| callbacks_->OnBigCreated(evt->conn_handles); |
| TriggerIsoDatapathSetup(evt->conn_handles[0]); |
| } else { |
| log::error( |
| "State={} Event={}. Unable to create big, big_id={}, status={}", |
| ToString(GetState()), event, evt->big_id, evt->status); |
| } |
| } break; |
| case HCI_BLE_TERM_BIG_CPL_EVT: { |
| auto* evt = static_cast<big_terminate_cmpl_evt*>(data); |
| |
| log::info("BIG terminate BIG cmpl, reason={} big_id={}", evt->reason, |
| evt->big_id); |
| |
| if (evt->big_id != GetAdvertisingSid()) { |
| log::error("State={} Event={}, unknown adv.sid={}", |
| ToString(GetState()), event, evt->big_id); |
| break; |
| } |
| |
| active_config_ = std::nullopt; |
| |
| /* Go back to configured if BIG is inactive (we are still announcing) */ |
| SetState(State::CONFIGURED); |
| |
| /* Check if we got this HCI event due to STOP or SUSPEND message. */ |
| if (suspending_) { |
| callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState(), evt); |
| suspending_ = false; |
| } else { |
| DisableAnnouncement(); |
| } |
| } break; |
| default: |
| log::error("State={} Unknown event={}", ToString(GetState()), event); |
| break; |
| } |
| } |
| }; |
| |
| IBroadcastStateMachineCallbacks* BroadcastStateMachineImpl::callbacks_ = |
| nullptr; |
| BleAdvertiserInterface* BroadcastStateMachineImpl::advertiser_if_ = nullptr; |
| } /* namespace */ |
| |
| std::unique_ptr<BroadcastStateMachine> BroadcastStateMachine::CreateInstance( |
| BroadcastStateMachineConfig msg) { |
| return std::make_unique<BroadcastStateMachineImpl>(std::move(msg)); |
| } |
| |
| void BroadcastStateMachine::Initialize( |
| IBroadcastStateMachineCallbacks* callbacks, |
| AdvertisingCallbacks* adv_callbacks) { |
| BroadcastStateMachineImpl::callbacks_ = callbacks; |
| /* Get gd le advertiser interface */ |
| BroadcastStateMachineImpl::advertiser_if_ = |
| bluetooth::shim::get_ble_advertiser_instance(); |
| if (BroadcastStateMachineImpl::advertiser_if_ != nullptr) { |
| log::info("Advertiser_instance acquired"); |
| BroadcastStateMachineImpl::advertiser_if_->RegisterCallbacksNative( |
| adv_callbacks, kAdvertiserClientIdLeAudio); |
| } else { |
| log::error("Could not acquire advertiser_instance!"); |
| BroadcastStateMachineImpl::advertiser_if_ = nullptr; |
| } |
| } |
| |
| namespace bluetooth::le_audio { |
| namespace broadcaster { |
| |
| std::ostream& operator<<(std::ostream& os, |
| const BroadcastStateMachine::Message& msg) { |
| static const char* char_value_[BroadcastStateMachine::MESSAGE_COUNT] = { |
| "START", "SUSPEND", "STOP"}; |
| os << char_value_[static_cast<uint8_t>(msg)]; |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const BroadcastStateMachine::State& state) { |
| static const char* char_value_[BroadcastStateMachine::STATE_COUNT] = { |
| "STOPPED", "CONFIGURING", "CONFIGURED", "STOPPING", "STREAMING"}; |
| os << char_value_[static_cast<uint8_t>(state)]; |
| return os; |
| } |
| |
| std::ostream& operator<<( |
| std::ostream& os, |
| const bluetooth::le_audio::broadcaster::BigConfig& config) { |
| os << "\n"; |
| os << " Status: 0x" << std::hex << +config.status << std::dec << "\n"; |
| os << " BIG ID: " << +config.big_id << "\n"; |
| os << " Sync delay: " << config.big_sync_delay << "\n"; |
| os << " Transport Latency: " << config.transport_latency_big << "\n"; |
| os << " Phy: " << +config.phy << "\n"; |
| os << " Nse: " << +config.nse << "\n"; |
| os << " Bn: " << +config.bn << "\n"; |
| os << " Pto: " << +config.pto << "\n"; |
| os << " Irc: " << +config.irc << "\n"; |
| os << " Max pdu: " << config.max_pdu << "\n"; |
| os << " Iso interval: " << config.iso_interval << "\n"; |
| os << " Connection handles (BISes): ["; |
| for (auto& el : config.connection_handles) { |
| os << std::hex << +el << std::dec << ":"; |
| } |
| os << "]"; |
| return os; |
| } |
| |
| std::ostream& operator<<( |
| std::ostream& os, |
| const bluetooth::le_audio::broadcaster::BroadcastStateMachineConfig& |
| config) { |
| const char* const PHYS[] = {"NONE", "1M", "2M", "CODED"}; |
| |
| os << "\n"; |
| os << " Broadcast ID: " << config.broadcast_id << "\n"; |
| os << " Streaming PHY: " |
| << ((config.streaming_phy > 3) ? std::to_string(config.streaming_phy) |
| : PHYS[config.streaming_phy]) |
| << "\n"; |
| os << " Subgroups: {\n"; |
| for (auto const& subgroup : config.config.subgroups) { |
| os << " " << subgroup << "\n"; |
| } |
| os << " }\n"; |
| os << " Qos Config: " << config.config.qos << "\n"; |
| if (config.broadcast_code) { |
| os << " Broadcast Code: ["; |
| for (auto& el : *config.broadcast_code) { |
| os << std::hex << +el << ":"; |
| } |
| os << "]\n"; |
| } else { |
| os << " Broadcast Code: NONE\n"; |
| } |
| |
| std::vector<uint8_t> an_raw; |
| ToRawPacket(config.announcement, an_raw); |
| os << " Announcement RAW: ["; |
| for (auto& el : an_raw) { |
| os << std::hex << +el << ":"; |
| } |
| os << "]"; |
| |
| return os; |
| } |
| |
| std::ostream& operator<<( |
| std::ostream& os, |
| const bluetooth::le_audio::broadcaster::BroadcastStateMachine& machine) { |
| os << " Broadcast state machine: {" |
| << " Advertising SID: " << +machine.GetAdvertisingSid() << "\n" |
| << " State: " << machine.GetState() << "\n"; |
| os << " State Machine Config: " << machine.GetStateMachineConfig() |
| << "\n"; |
| |
| if (machine.GetBigConfig()) { |
| os << " BigConfig: " << *machine.GetBigConfig() << "\n"; |
| } else { |
| os << " BigConfig: NONE\n"; |
| } |
| os << " }\n"; |
| return os; |
| } |
| |
| } // namespace broadcaster |
| } // namespace bluetooth::le_audio |