Loading system/bta/le_audio/broadcaster/broadcaster.cc +35 −1 Original line number Diff line number Diff line Loading @@ -141,6 +141,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } Loading Loading @@ -575,8 +579,21 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { requirements.subgroup_quality.push_back( {ChooseConfigurationContextType(context_type), idx}); } auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements); 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; } auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), true); log::assert_that(result, "Could not update session in codec manager"); } auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements); if (!config) { log::error("No valid broadcast offload config"); callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid, Loading Loading @@ -689,6 +706,11 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { log::error("Could not acquire le audio"); return; } auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), true); log::assert_that(result, "Could not update session in codec manager"); } broadcasts_[broadcast_id]->ProcessMessage( Loading Loading @@ -719,6 +741,14 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { void DestroyAudioBroadcast(uint32_t broadcast_id) override { log::info("Destroying broadcast_id={}", broadcast_id); broadcasts_.erase(broadcast_id); if (broadcasts_.empty() && le_audio_source_hal_client_) { auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } std::optional<bluetooth::le_audio::BroadcastMetadata> GetBroadcastMetadataOpt( Loading Loading @@ -845,6 +875,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { "assert failed: broadcasts_.count(broadcast_id) != 0"); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, evt); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } break; default: Loading system/bta/le_audio/broadcaster/broadcaster_test.cc +126 −9 Original line number Diff line number Diff line Loading @@ -261,13 +261,11 @@ class BroadcasterTest : public Test { ASSERT_NE(iso_manager_, nullptr); iso_manager_->Start(); is_audio_hal_acquired = false; mock_audio_source_ = new MockAudioHalClientEndpoint(); ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true)); ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() { mock_audio_source_ = nullptr; is_audio_hal_acquired = false; }); mock_iso_manager_ = MockIsoManager::GetInstance(); ON_CALL(*mock_iso_manager_, RegisterBigCallbacks(_)) .WillByDefault(SaveArg<0>(&big_callbacks_)); ConfigAudioHalClientMock(); EXPECT_CALL(*MockIsoManager::GetInstance(), RegisterOnIsoTrafficActiveCallbacks) Loading @@ -287,6 +285,10 @@ class BroadcasterTest : public Test { ConfigCodecManagerMock(types::CodecLocation::HOST); ON_CALL(*mock_codec_manager_, UpdateActiveUnicastAudioHalClient(_, _, _)) .WillByDefault(Return(true)); ON_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(_, _)) .WillByDefault(Return(true)); ON_CALL(*mock_codec_manager_, GetBroadcastConfig) .WillByDefault( Invoke([](const bluetooth::le_audio::CodecManager:: Loading @@ -297,6 +299,16 @@ class BroadcasterTest : public Test { })); } void ConfigAudioHalClientMock() { is_audio_hal_acquired = false; mock_audio_source_ = new MockAudioHalClientEndpoint(); ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true)); ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() { mock_audio_source_ = nullptr; is_audio_hal_acquired = false; }); } void ConfigCodecManagerMock(types::CodecLocation location) { codec_manager_ = le_audio::CodecManager::GetInstance(); ASSERT_NE(codec_manager_, nullptr); Loading Loading @@ -363,10 +375,41 @@ class BroadcasterTest : public Test { return broadcast_id; } void InjectBigCreateComplete(uint8_t big_id, uint8_t status) { std::vector<uint16_t> conn_handles = {0x10, 0x12}; hci::iso_manager::big_create_cmpl_evt evt = { .status = status, .big_id = big_id, .big_sync_delay = 1231, .transport_latency_big = 1234, .phy = 2, .nse = 3, .bn = 2, .pto = 2, .irc = 2, .max_pdu = 128, .iso_interval = 10, .conn_handles = conn_handles, }; big_callbacks_->OnBigEvent( bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, &evt); } void InjectBigTerminateComplete(uint8_t big_id, uint8_t reason) { hci::iso_manager::big_terminate_cmpl_evt evt = {.big_id = big_id, .reason = reason}; big_callbacks_->OnBigEvent( bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, &evt); } protected: MockLeAudioBroadcasterCallbacks mock_broadcaster_callbacks_; bluetooth::hci::testing::MockControllerInterface mock_controller_; bluetooth::hci::IsoManager* iso_manager_; MockIsoManager* mock_iso_manager_; bluetooth::hci::iso_manager::BigCallbacks* big_callbacks_ = nullptr; le_audio::CodecManager* codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; Loading Loading @@ -419,19 +462,38 @@ TEST_F(BroadcasterTest, CreateAudioBroadcastMultiGroups) { } TEST_F(BroadcasterTest, SuspendAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1)); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _)) .Times(0); LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StartAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(0); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(0); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); EXPECT_CALL(mock_broadcaster_callbacks_, Loading Loading @@ -460,11 +522,18 @@ TEST_F(BroadcasterTest, StartAudioBroadcast) { EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(1); std::vector<uint8_t> sample_data(320, 0); audio_receiver->OnAudioDataReady(sample_data); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(media_metadata, default_code, {bluetooth::le_audio::QUALITY_HIGH}); Mock::VerifyAndClearExpectations(mock_codec_manager_); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); EXPECT_CALL(mock_broadcaster_callbacks_, Loading @@ -474,6 +543,12 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(0); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(0); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); ASSERT_NE(audio_receiver, nullptr); Loading @@ -494,27 +569,68 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(2); std::vector<uint8_t> sample_data(1920, 0); audio_receiver->OnAudioDataReady(sample_data); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StopAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(1); auto broadcast_id = InstantiateBroadcast(); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // NOTICE: This is really an implementation specific part, we fake the BIG // config as the mocked state machine does not even call the // IsoManager to prepare one (and that's good since IsoManager is also // a mocked one). auto mock_state_machine = MockBroadcastStateMachine::GetLastInstance(); BigConfig big_cfg; big_cfg.big_id = mock_state_machine->GetAdvertisingSid(); big_cfg.connection_handles = {0x10, 0x12}; big_cfg.max_pdu = 128; mock_state_machine->SetExpectedBigConfig(big_cfg); InjectBigCreateComplete(big_cfg.big_id, 0x00); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1)); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); InjectBigTerminateComplete(big_cfg.big_id, 0x16); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, DestroyAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(1); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastDestroyed(broadcast_id)) .Times(1); LeAudioBroadcaster::Get()->DestroyAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); ASSERT_EQ(mock_audio_source_, nullptr); /* Create a mock again for the test purpose */ ConfigAudioHalClientMock(); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _)) .Times(0); // Expect not being able to interact with this Broadcast EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)) Loading @@ -528,6 +644,9 @@ TEST_F(BroadcasterTest, DestroyAudioBroadcast) { EXPECT_CALL(*mock_audio_source_, Stop).Times(0); LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_); } TEST_F(BroadcasterTest, GetBroadcastAllStates) { Loading Loading @@ -945,8 +1064,6 @@ TEST_F(BroadcasterTest, SanityTest) { } TEST_F(BroadcasterTest, VendorCodecConfig) { ConfigCodecManagerMock(types::CodecLocation::HOST); ON_CALL(*mock_codec_manager_, GetBroadcastConfig) .WillByDefault(Invoke([](const bluetooth::le_audio::CodecManager:: BroadcastConfigurationRequirements&) { Loading system/bta/le_audio/client.cc +12 −0 Original line number Diff line number Diff line Loading @@ -1295,6 +1295,12 @@ class LeAudioClientImpl : public LeAudioClient { } } auto result = CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient( le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(), true); log::assert_that(result, "Could not update session to codec manager"); /* Mini policy: Try configure audio HAL sessions with most recent context. * If reconfiguration is not needed it means, context type is not supported. * If most recent scenario is not supported, try to find first supported. Loading Loading @@ -5900,6 +5906,12 @@ class LeAudioClientImpl : public LeAudioClient { log::info("ClientAudioInterfaceRelease - cleanup"); } auto result = CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient( le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(), false); log::assert_that(result, "Could not update session to codec manager"); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); le_audio_source_hal_client_.reset(); Loading system/bta/le_audio/codec_manager.cc +116 −0 Original line number Diff line number Diff line Loading @@ -204,6 +204,99 @@ struct codec_manager_impl { } } bool UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) { log::debug("local_source: {}, local_sink: {}, is_active: {}", fmt::ptr(source_unicast_client), fmt::ptr(sink_unicast_client), is_active); if (source_unicast_client == nullptr && sink_unicast_client == nullptr) { return false; } if (is_active) { if (source_unicast_client && unicast_local_source_hal_client != nullptr) { log::error("Trying to override previous source hal client {}", fmt::ptr(unicast_local_source_hal_client)); return false; } if (sink_unicast_client && unicast_local_sink_hal_client != nullptr) { log::error("Trying to override previous sink hal client {}", fmt::ptr(unicast_local_sink_hal_client)); return false; } if (source_unicast_client) { unicast_local_source_hal_client = source_unicast_client; } if (sink_unicast_client) { unicast_local_sink_hal_client = sink_unicast_client; } return true; } if (source_unicast_client && source_unicast_client != unicast_local_source_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(source_unicast_client), fmt::ptr(unicast_local_source_hal_client)); return false; } if (sink_unicast_client && sink_unicast_client != unicast_local_sink_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(sink_unicast_client), fmt::ptr(unicast_local_sink_hal_client)); return false; } if (source_unicast_client) { unicast_local_source_hal_client = nullptr; } if (sink_unicast_client) { unicast_local_sink_hal_client = nullptr; } return true; } bool UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) { log::debug("local_source: {},is_active: {}", fmt::ptr(source_broadcast_client), is_active); if (source_broadcast_client == nullptr) { return false; } if (is_active) { if (broadcast_local_source_hal_client != nullptr) { log::error("Trying to override previous source hal client {}", fmt::ptr(broadcast_local_source_hal_client)); return false; } broadcast_local_source_hal_client = source_broadcast_client; return true; } if (source_broadcast_client != broadcast_local_source_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(source_broadcast_client), fmt::ptr(broadcast_local_source_hal_client)); return false; } broadcast_local_source_hal_client = nullptr; return true; } AudioSetConfigurations GetSupportedCodecConfigurations( const CodecManager::UnicastConfigurationRequirements& requirements) const { Loading Loading @@ -945,6 +1038,10 @@ struct codec_manager_impl { std::vector<btle_audio_codec_config_t> codec_input_capa = {}; std::vector<btle_audio_codec_config_t> codec_output_capa = {}; int broadcast_target_config = -1; LeAudioSourceAudioHalClient* unicast_local_source_hal_client = nullptr; LeAudioSinkAudioHalClient* unicast_local_sink_hal_client = nullptr; LeAudioSourceAudioHalClient* broadcast_local_source_hal_client = nullptr; }; std::ostream& operator<<( Loading Loading @@ -1033,6 +1130,25 @@ void CodecManager::UpdateActiveAudioConfig( stream_params, delays_ms, update_receiver); } bool CodecManager::UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) { if (pimpl_->IsRunning()) { return pimpl_->codec_manager_impl_->UpdateActiveUnicastAudioHalClient( source_unicast_client, sink_unicast_client, is_active); } return false; } bool CodecManager::UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) { if (pimpl_->IsRunning()) { return pimpl_->codec_manager_impl_->UpdateActiveBroadcastAudioHalClient( source_broadcast_client, is_active); } return false; } std::unique_ptr<AudioSetConfiguration> CodecManager::GetCodecConfig( const CodecManager::UnicastConfigurationRequirements& requirements, CodecManager::UnicastConfigurationVerifier verifier) { Loading system/bta/le_audio/codec_manager.h +8 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ namespace bluetooth::le_audio { class LeAudioSinkAudioHalClient; class LeAudioSourceAudioHalClient; struct stream_map_info { stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation, bool is_stream_active) Loading Loading @@ -106,6 +109,11 @@ class CodecManager { const std::vector<struct types::cis>& cises, const stream_parameters& stream_params, uint8_t direction); virtual void ClearCisConfiguration(uint8_t direction); virtual bool UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active); virtual bool UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active); virtual void UpdateActiveAudioConfig( const types::BidirectionalPair<stream_parameters>& stream_params, types::BidirectionalPair<uint16_t> delays_ms, Loading Loading
system/bta/le_audio/broadcaster/broadcaster.cc +35 −1 Original line number Diff line number Diff line Loading @@ -141,6 +141,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } Loading Loading @@ -575,8 +579,21 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { requirements.subgroup_quality.push_back( {ChooseConfigurationContextType(context_type), idx}); } auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements); 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; } auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), true); log::assert_that(result, "Could not update session in codec manager"); } auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements); if (!config) { log::error("No valid broadcast offload config"); callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid, Loading Loading @@ -689,6 +706,11 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { log::error("Could not acquire le audio"); return; } auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), true); log::assert_that(result, "Could not update session in codec manager"); } broadcasts_[broadcast_id]->ProcessMessage( Loading Loading @@ -719,6 +741,14 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { void DestroyAudioBroadcast(uint32_t broadcast_id) override { log::info("Destroying broadcast_id={}", broadcast_id); broadcasts_.erase(broadcast_id); if (broadcasts_.empty() && le_audio_source_hal_client_) { auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } } std::optional<bluetooth::le_audio::BroadcastMetadata> GetBroadcastMetadataOpt( Loading Loading @@ -845,6 +875,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks { "assert failed: broadcasts_.count(broadcast_id) != 0"); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, evt); auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient( le_audio_source_hal_client_.get(), false); log::assert_that(result, "Could not update session in codec manager"); le_audio_source_hal_client_.reset(); } break; default: Loading
system/bta/le_audio/broadcaster/broadcaster_test.cc +126 −9 Original line number Diff line number Diff line Loading @@ -261,13 +261,11 @@ class BroadcasterTest : public Test { ASSERT_NE(iso_manager_, nullptr); iso_manager_->Start(); is_audio_hal_acquired = false; mock_audio_source_ = new MockAudioHalClientEndpoint(); ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true)); ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() { mock_audio_source_ = nullptr; is_audio_hal_acquired = false; }); mock_iso_manager_ = MockIsoManager::GetInstance(); ON_CALL(*mock_iso_manager_, RegisterBigCallbacks(_)) .WillByDefault(SaveArg<0>(&big_callbacks_)); ConfigAudioHalClientMock(); EXPECT_CALL(*MockIsoManager::GetInstance(), RegisterOnIsoTrafficActiveCallbacks) Loading @@ -287,6 +285,10 @@ class BroadcasterTest : public Test { ConfigCodecManagerMock(types::CodecLocation::HOST); ON_CALL(*mock_codec_manager_, UpdateActiveUnicastAudioHalClient(_, _, _)) .WillByDefault(Return(true)); ON_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(_, _)) .WillByDefault(Return(true)); ON_CALL(*mock_codec_manager_, GetBroadcastConfig) .WillByDefault( Invoke([](const bluetooth::le_audio::CodecManager:: Loading @@ -297,6 +299,16 @@ class BroadcasterTest : public Test { })); } void ConfigAudioHalClientMock() { is_audio_hal_acquired = false; mock_audio_source_ = new MockAudioHalClientEndpoint(); ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true)); ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() { mock_audio_source_ = nullptr; is_audio_hal_acquired = false; }); } void ConfigCodecManagerMock(types::CodecLocation location) { codec_manager_ = le_audio::CodecManager::GetInstance(); ASSERT_NE(codec_manager_, nullptr); Loading Loading @@ -363,10 +375,41 @@ class BroadcasterTest : public Test { return broadcast_id; } void InjectBigCreateComplete(uint8_t big_id, uint8_t status) { std::vector<uint16_t> conn_handles = {0x10, 0x12}; hci::iso_manager::big_create_cmpl_evt evt = { .status = status, .big_id = big_id, .big_sync_delay = 1231, .transport_latency_big = 1234, .phy = 2, .nse = 3, .bn = 2, .pto = 2, .irc = 2, .max_pdu = 128, .iso_interval = 10, .conn_handles = conn_handles, }; big_callbacks_->OnBigEvent( bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, &evt); } void InjectBigTerminateComplete(uint8_t big_id, uint8_t reason) { hci::iso_manager::big_terminate_cmpl_evt evt = {.big_id = big_id, .reason = reason}; big_callbacks_->OnBigEvent( bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, &evt); } protected: MockLeAudioBroadcasterCallbacks mock_broadcaster_callbacks_; bluetooth::hci::testing::MockControllerInterface mock_controller_; bluetooth::hci::IsoManager* iso_manager_; MockIsoManager* mock_iso_manager_; bluetooth::hci::iso_manager::BigCallbacks* big_callbacks_ = nullptr; le_audio::CodecManager* codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; Loading Loading @@ -419,19 +462,38 @@ TEST_F(BroadcasterTest, CreateAudioBroadcastMultiGroups) { } TEST_F(BroadcasterTest, SuspendAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1)); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _)) .Times(0); LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StartAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(0); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(0); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); EXPECT_CALL(mock_broadcaster_callbacks_, Loading Loading @@ -460,11 +522,18 @@ TEST_F(BroadcasterTest, StartAudioBroadcast) { EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(1); std::vector<uint8_t> sample_data(320, 0); audio_receiver->OnAudioDataReady(sample_data); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(media_metadata, default_code, {bluetooth::le_audio::QUALITY_HIGH}); Mock::VerifyAndClearExpectations(mock_codec_manager_); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); EXPECT_CALL(mock_broadcaster_callbacks_, Loading @@ -474,6 +543,12 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(0); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(0); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); ASSERT_NE(audio_receiver, nullptr); Loading @@ -494,27 +569,68 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) { EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(2); std::vector<uint8_t> sample_data(1920, 0); audio_receiver->OnAudioDataReady(sample_data); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, StopAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(1); auto broadcast_id = InstantiateBroadcast(); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // NOTICE: This is really an implementation specific part, we fake the BIG // config as the mocked state machine does not even call the // IsoManager to prepare one (and that's good since IsoManager is also // a mocked one). auto mock_state_machine = MockBroadcastStateMachine::GetLastInstance(); BigConfig big_cfg; big_cfg.big_id = mock_state_machine->GetAdvertisingSid(); big_cfg.connection_handles = {0x10, 0x12}; big_cfg.max_pdu = 128; mock_state_machine->SetExpectedBigConfig(big_cfg); InjectBigCreateComplete(big_cfg.big_id, 0x00); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1)); LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id); InjectBigTerminateComplete(big_cfg.big_id, 0x16); Mock::VerifyAndClearExpectations(mock_codec_manager_); } TEST_F(BroadcasterTest, DestroyAudioBroadcast) { EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true)) .Times(1); auto broadcast_id = InstantiateBroadcast(); Mock::VerifyAndClearExpectations(mock_codec_manager_); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false)) .Times(1); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastDestroyed(broadcast_id)) .Times(1); LeAudioBroadcaster::Get()->DestroyAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); ASSERT_EQ(mock_audio_source_, nullptr); /* Create a mock again for the test purpose */ ConfigAudioHalClientMock(); EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _)) .Times(0); // Expect not being able to interact with this Broadcast EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)) Loading @@ -528,6 +644,9 @@ TEST_F(BroadcasterTest, DestroyAudioBroadcast) { EXPECT_CALL(*mock_audio_source_, Stop).Times(0); LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id); Mock::VerifyAndClearExpectations(mock_codec_manager_); Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_); } TEST_F(BroadcasterTest, GetBroadcastAllStates) { Loading Loading @@ -945,8 +1064,6 @@ TEST_F(BroadcasterTest, SanityTest) { } TEST_F(BroadcasterTest, VendorCodecConfig) { ConfigCodecManagerMock(types::CodecLocation::HOST); ON_CALL(*mock_codec_manager_, GetBroadcastConfig) .WillByDefault(Invoke([](const bluetooth::le_audio::CodecManager:: BroadcastConfigurationRequirements&) { Loading
system/bta/le_audio/client.cc +12 −0 Original line number Diff line number Diff line Loading @@ -1295,6 +1295,12 @@ class LeAudioClientImpl : public LeAudioClient { } } auto result = CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient( le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(), true); log::assert_that(result, "Could not update session to codec manager"); /* Mini policy: Try configure audio HAL sessions with most recent context. * If reconfiguration is not needed it means, context type is not supported. * If most recent scenario is not supported, try to find first supported. Loading Loading @@ -5900,6 +5906,12 @@ class LeAudioClientImpl : public LeAudioClient { log::info("ClientAudioInterfaceRelease - cleanup"); } auto result = CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient( le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(), false); log::assert_that(result, "Could not update session to codec manager"); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); le_audio_source_hal_client_.reset(); Loading
system/bta/le_audio/codec_manager.cc +116 −0 Original line number Diff line number Diff line Loading @@ -204,6 +204,99 @@ struct codec_manager_impl { } } bool UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) { log::debug("local_source: {}, local_sink: {}, is_active: {}", fmt::ptr(source_unicast_client), fmt::ptr(sink_unicast_client), is_active); if (source_unicast_client == nullptr && sink_unicast_client == nullptr) { return false; } if (is_active) { if (source_unicast_client && unicast_local_source_hal_client != nullptr) { log::error("Trying to override previous source hal client {}", fmt::ptr(unicast_local_source_hal_client)); return false; } if (sink_unicast_client && unicast_local_sink_hal_client != nullptr) { log::error("Trying to override previous sink hal client {}", fmt::ptr(unicast_local_sink_hal_client)); return false; } if (source_unicast_client) { unicast_local_source_hal_client = source_unicast_client; } if (sink_unicast_client) { unicast_local_sink_hal_client = sink_unicast_client; } return true; } if (source_unicast_client && source_unicast_client != unicast_local_source_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(source_unicast_client), fmt::ptr(unicast_local_source_hal_client)); return false; } if (sink_unicast_client && sink_unicast_client != unicast_local_sink_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(sink_unicast_client), fmt::ptr(unicast_local_sink_hal_client)); return false; } if (source_unicast_client) { unicast_local_source_hal_client = nullptr; } if (sink_unicast_client) { unicast_local_sink_hal_client = nullptr; } return true; } bool UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) { log::debug("local_source: {},is_active: {}", fmt::ptr(source_broadcast_client), is_active); if (source_broadcast_client == nullptr) { return false; } if (is_active) { if (broadcast_local_source_hal_client != nullptr) { log::error("Trying to override previous source hal client {}", fmt::ptr(broadcast_local_source_hal_client)); return false; } broadcast_local_source_hal_client = source_broadcast_client; return true; } if (source_broadcast_client != broadcast_local_source_hal_client) { log::error("local source session does not match {} != {}", fmt::ptr(source_broadcast_client), fmt::ptr(broadcast_local_source_hal_client)); return false; } broadcast_local_source_hal_client = nullptr; return true; } AudioSetConfigurations GetSupportedCodecConfigurations( const CodecManager::UnicastConfigurationRequirements& requirements) const { Loading Loading @@ -945,6 +1038,10 @@ struct codec_manager_impl { std::vector<btle_audio_codec_config_t> codec_input_capa = {}; std::vector<btle_audio_codec_config_t> codec_output_capa = {}; int broadcast_target_config = -1; LeAudioSourceAudioHalClient* unicast_local_source_hal_client = nullptr; LeAudioSinkAudioHalClient* unicast_local_sink_hal_client = nullptr; LeAudioSourceAudioHalClient* broadcast_local_source_hal_client = nullptr; }; std::ostream& operator<<( Loading Loading @@ -1033,6 +1130,25 @@ void CodecManager::UpdateActiveAudioConfig( stream_params, delays_ms, update_receiver); } bool CodecManager::UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) { if (pimpl_->IsRunning()) { return pimpl_->codec_manager_impl_->UpdateActiveUnicastAudioHalClient( source_unicast_client, sink_unicast_client, is_active); } return false; } bool CodecManager::UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) { if (pimpl_->IsRunning()) { return pimpl_->codec_manager_impl_->UpdateActiveBroadcastAudioHalClient( source_broadcast_client, is_active); } return false; } std::unique_ptr<AudioSetConfiguration> CodecManager::GetCodecConfig( const CodecManager::UnicastConfigurationRequirements& requirements, CodecManager::UnicastConfigurationVerifier verifier) { Loading
system/bta/le_audio/codec_manager.h +8 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ namespace bluetooth::le_audio { class LeAudioSinkAudioHalClient; class LeAudioSourceAudioHalClient; struct stream_map_info { stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation, bool is_stream_active) Loading Loading @@ -106,6 +109,11 @@ class CodecManager { const std::vector<struct types::cis>& cises, const stream_parameters& stream_params, uint8_t direction); virtual void ClearCisConfiguration(uint8_t direction); virtual bool UpdateActiveUnicastAudioHalClient( LeAudioSourceAudioHalClient* source_unicast_client, LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active); virtual bool UpdateActiveBroadcastAudioHalClient( LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active); virtual void UpdateActiveAudioConfig( const types::BidirectionalPair<stream_parameters>& stream_params, types::BidirectionalPair<uint16_t> delays_ms, Loading