Loading system/bta/Android.bp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1170,10 +1170,10 @@ cc_test { srcs: [ srcs: [ ":TestCommonMockFunctions", ":TestCommonMockFunctions", ":TestCommonStackConfig", ":TestCommonStackConfig", ":TestFakeOsi", ":TestMockMainShim", ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockMainShimEntry", ":TestMockStackBtmIso", ":TestMockStackBtmIso", ":TestStubOsi", "le_audio/broadcaster/broadcaster.cc", "le_audio/broadcaster/broadcaster.cc", "le_audio/broadcaster/broadcaster_test.cc", "le_audio/broadcaster/broadcaster_test.cc", "le_audio/broadcaster/broadcaster_types.cc", "le_audio/broadcaster/broadcaster_types.cc", Loading system/bta/le_audio/broadcaster/broadcaster.cc +70 −9 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include "hci/controller_interface.h" #include "hci/controller_interface.h" #include "internal_include/stack_config.h" #include "internal_include/stack_config.h" #include "main/shim/entry.h" #include "main/shim/entry.h" #include "osi/include/alarm.h" #include "osi/include/properties.h" #include "osi/include/properties.h" #include "stack/include/bt_types.h" #include "stack/include/bt_types.h" #include "stack/include/btm_api_types.h" #include "stack/include/btm_api_types.h" Loading Loading @@ -90,7 +91,9 @@ public: : callbacks_(callbacks_), : callbacks_(callbacks_), current_phy_(PHY_LE_2M), current_phy_(PHY_LE_2M), le_audio_source_hal_client_(nullptr), le_audio_source_hal_client_(nullptr), audio_state_(AudioState::SUSPENDED) { audio_state_(AudioState::SUSPENDED), big_terminate_timer_(alarm_new("BigTerminateTimer")), broadcast_stop_timer_(alarm_new("BroadcastStopTimer")) { log::info(""); log::info(""); /* Register State machine callbacks */ /* Register State machine callbacks */ Loading @@ -100,7 +103,10 @@ public: GenerateBroadcastIds(); GenerateBroadcastIds(); } } ~LeAudioBroadcasterImpl() override = default; ~LeAudioBroadcasterImpl() override { alarm_free(big_terminate_timer_); alarm_free(broadcast_stop_timer_); } void GenerateBroadcastIds(void) { void GenerateBroadcastIds(void) { btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) { btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) { Loading Loading @@ -141,6 +147,7 @@ public: le_audio_source_hal_client_.reset(); le_audio_source_hal_client_.reset(); } } audio_state_ = AudioState::SUSPENDED; audio_state_ = AudioState::SUSPENDED; cancelBroadcastTimers(); } } void Stop() { void Stop() { Loading Loading @@ -977,6 +984,57 @@ public: } } private: private: void SuspendAudioBroadcasts() { log::info(""); for (auto& broadcast_pair : broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } void StopAudioBroadcasts() { log::info(""); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); } for (auto& broadcast_pair : broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::STOP, nullptr); } bluetooth::le_audio::MetricsCollector::Get()->OnBroadcastStateChanged(false); } void setBroadcastTimers() { if (audio_state_ == AudioState::SUSPENDED) { log::info(" Started"); alarm_set_on_mloop( big_terminate_timer_, kBigTerminateTimeoutMs, [](void*) { if (instance) { instance->SuspendAudioBroadcasts(); } }, nullptr); alarm_set_on_mloop( broadcast_stop_timer_, kBroadcastStopTimeoutMs, [](void*) { if (instance) { instance->StopAudioBroadcasts(); } }, nullptr); } } void cancelBroadcastTimers() { log::info(""); alarm_cancel(big_terminate_timer_); alarm_cancel(broadcast_stop_timer_); } static class BroadcastStateMachineCallbacks static class BroadcastStateMachineCallbacks : public IBroadcastStateMachineCallbacks { : public IBroadcastStateMachineCallbacks { void OnStateMachineCreateStatus(uint32_t broadcast_id, void OnStateMachineCreateStatus(uint32_t broadcast_id, Loading Loading @@ -1067,6 +1125,9 @@ public: instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); } } } } if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->setBroadcastTimers(); } } } break; break; }; }; Loading Loading @@ -1336,13 +1397,7 @@ public: instance->audio_state_ = AudioState::SUSPENDED; instance->audio_state_ = AudioState::SUSPENDED; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->setBroadcastTimers(); // TODO: add some timeout to execute below for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } } } Loading @@ -1352,6 +1407,7 @@ public: instance->audio_state_ = AudioState::ACTIVE; instance->audio_state_ = AudioState::ACTIVE; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->cancelBroadcastTimers(); instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); for (auto& broadcast_pair : instance->broadcasts_) { for (auto& broadcast_pair : instance->broadcasts_) { Loading Loading @@ -1410,6 +1466,11 @@ public: // Flag to track iso state // Flag to track iso state bool is_iso_running_ = false; bool is_iso_running_ = false; static constexpr uint64_t kBigTerminateTimeoutMs = 10 * 1000; static constexpr uint64_t kBroadcastStopTimeoutMs = 30 * 60 * 1000; alarm_t* big_terminate_timer_; alarm_t* broadcast_stop_timer_; }; }; /* Static members definitions */ /* Static members definitions */ Loading system/bta/le_audio/broadcaster/broadcaster_test.cc +211 −3 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include "stack/include/btm_iso_api.h" #include "stack/include/btm_iso_api.h" #include "test/common/mock_functions.h" #include "test/common/mock_functions.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_osi_alarm.h" #include "test/mock/mock_stack_btm_iso.h" #include "test/mock/mock_stack_btm_iso.h" #define TEST_BT com::android::bluetooth::flags #define TEST_BT com::android::bluetooth::flags Loading Loading @@ -71,6 +72,16 @@ extern "C" const char* __asan_default_options() { return "detect_container_overflow=0"; return "detect_container_overflow=0"; } } struct alarm_t { alarm_callback_t cb; void* data; alarm_t(const char* /* name */) { cb = nullptr; data = nullptr; } }; static base::Callback<void(BT_OCTET8)> generator_cb; static base::Callback<void(BT_OCTET8)> generator_cb; void btsnd_hcic_ble_rand(base::Callback<void(BT_OCTET8)> cb) { void btsnd_hcic_ble_rand(base::Callback<void(BT_OCTET8)> cb) { Loading Loading @@ -222,6 +233,9 @@ static const std::vector<uint8_t> media_metadata = { media_context & 0x00FF, (media_context & 0xFF00) >> 8}; media_context & 0x00FF, (media_context & 0xFF00) >> 8}; static const std::string test_broadcast_name = "Test"; static const std::string test_broadcast_name = "Test"; static const std::string big_terminate_timer_name = "BigTerminateTimer"; static const std::string broadcast_stop_timer_name = "BroadcastStopTimer"; class MockLeAudioBroadcasterCallbacks class MockLeAudioBroadcasterCallbacks : public bluetooth::le_audio::LeAudioBroadcasterCallbacks { : public bluetooth::le_audio::LeAudioBroadcasterCallbacks { public: public: Loading Loading @@ -277,6 +291,37 @@ class MockAudioHalClientEndpoint : public LeAudioSourceAudioHalClient { class BroadcasterTest : public Test { class BroadcasterTest : public Test { protected: protected: void SetUp() override { void SetUp() override { test::mock::osi_alarm::alarm_free.body = [](alarm_t* alarm) { if (alarm) { delete alarm; } }; test::mock::osi_alarm::alarm_new.body = [this](const char* name) { if (std::string(name) == big_terminate_timer_name) { this->big_terminate_timer_ = new alarm_t(name); return this->big_terminate_timer_; } else if (std::string(name) == broadcast_stop_timer_name) { this->broadcast_stop_timer_ = new alarm_t(name); return this->broadcast_stop_timer_; } else { return (alarm_t*)(nullptr); } }; test::mock::osi_alarm::alarm_set_on_mloop.body = [](alarm_t* alarm, uint64_t interval_ms, alarm_callback_t cb, void* data) { alarm->cb = cb; alarm->data = data; }; test::mock::osi_alarm::alarm_cancel.body = [](alarm_t* alarm) { if (alarm) { alarm->cb = nullptr; alarm->data = nullptr; } }; init_message_loop_thread(); init_message_loop_thread(); reset_mock_function_count_map(); reset_mock_function_count_map(); Loading Loading @@ -375,6 +420,13 @@ class BroadcasterTest : public Test { codec_manager_->Stop(); codec_manager_->Stop(); mock_codec_manager_ = nullptr; mock_codec_manager_ = nullptr; } } test::mock::osi_alarm::alarm_free = {}; test::mock::osi_alarm::alarm_new = {}; test::mock::osi_alarm::alarm_set_on_mloop = {}; test::mock::osi_alarm::alarm_cancel = {}; big_terminate_timer_ = nullptr; broadcast_stop_timer_ = nullptr; } } uint32_t InstantiateBroadcast( uint32_t InstantiateBroadcast( Loading Loading @@ -440,6 +492,9 @@ class BroadcasterTest : public Test { le_audio::CodecManager* codec_manager_ = nullptr; le_audio::CodecManager* codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; alarm_t* big_terminate_timer_ = nullptr; alarm_t* broadcast_stop_timer_ = nullptr; }; }; TEST_F(BroadcasterTest, Initialize) { TEST_F(BroadcasterTest, Initialize) { Loading Loading @@ -1283,11 +1338,56 @@ TEST_F_WITH_FLAGS(BroadcasterTest, AudioActiveState, ASSERT_EQ(updated_public_meta, public_metadata_audio_false); ASSERT_EQ(updated_public_meta, public_metadata_audio_false); } } TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend, TEST_F_WITH_FLAGS(BroadcasterTest, BigTerminationAndBroadcastStopWhenNoSoundFromTheBeginning, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // Broadcast stop timer execution, state machine go to STOPPED state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); // Imitate execution of BIG termination timer broadcast_stop_timer_->cb(broadcast_stop_timer_->data); } TEST_F_WITH_FLAGS(BroadcasterTest, BigTerminationAndBroadcastStopWhenNoSoundAfterSuspend, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) Loading @@ -1295,24 +1395,132 @@ TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSus EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); ASSERT_NE(audio_receiver, nullptr); // First onAudioResume when BIG already created, not cause any action // First onAudioResume when BIG already created, not cause any state change EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(2, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(4, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // OnAudioSuspend cause state machine go to CONFIGURED state so BIG termination // OnAudioResume before timer execution cancel them and not change the broadcast state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(4, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(6, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // Broadcast stop timer execution, state machine go to STOPPED state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); // Imitate execution of BIG termination timer broadcast_stop_timer_->cb(broadcast_stop_timer_->data); } TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); // First onAudioResume when BIG already created, not cause any state change EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(2, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(4, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // OnAudioResume before timer execution cancel them and not change the broadcast state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(4, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); audio_receiver->OnAudioSuspend(); ASSERT_EQ(6, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // onAudioResume cause state machine go to STREAMING state so BIG creation // onAudioResume cause state machine go to STREAMING state so BIG creation EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); .Times(1); audio_receiver->OnAudioResume(); audio_receiver->OnAudioResume(); // Timers Cancelled ASSERT_EQ(6, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); } } // TODO: Add tests for: // TODO: Add tests for: Loading Loading
system/bta/Android.bp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1170,10 +1170,10 @@ cc_test { srcs: [ srcs: [ ":TestCommonMockFunctions", ":TestCommonMockFunctions", ":TestCommonStackConfig", ":TestCommonStackConfig", ":TestFakeOsi", ":TestMockMainShim", ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockMainShimEntry", ":TestMockStackBtmIso", ":TestMockStackBtmIso", ":TestStubOsi", "le_audio/broadcaster/broadcaster.cc", "le_audio/broadcaster/broadcaster.cc", "le_audio/broadcaster/broadcaster_test.cc", "le_audio/broadcaster/broadcaster_test.cc", "le_audio/broadcaster/broadcaster_types.cc", "le_audio/broadcaster/broadcaster_types.cc", Loading
system/bta/le_audio/broadcaster/broadcaster.cc +70 −9 Original line number Original line Diff line number Diff line Loading @@ -34,6 +34,7 @@ #include "hci/controller_interface.h" #include "hci/controller_interface.h" #include "internal_include/stack_config.h" #include "internal_include/stack_config.h" #include "main/shim/entry.h" #include "main/shim/entry.h" #include "osi/include/alarm.h" #include "osi/include/properties.h" #include "osi/include/properties.h" #include "stack/include/bt_types.h" #include "stack/include/bt_types.h" #include "stack/include/btm_api_types.h" #include "stack/include/btm_api_types.h" Loading Loading @@ -90,7 +91,9 @@ public: : callbacks_(callbacks_), : callbacks_(callbacks_), current_phy_(PHY_LE_2M), current_phy_(PHY_LE_2M), le_audio_source_hal_client_(nullptr), le_audio_source_hal_client_(nullptr), audio_state_(AudioState::SUSPENDED) { audio_state_(AudioState::SUSPENDED), big_terminate_timer_(alarm_new("BigTerminateTimer")), broadcast_stop_timer_(alarm_new("BroadcastStopTimer")) { log::info(""); log::info(""); /* Register State machine callbacks */ /* Register State machine callbacks */ Loading @@ -100,7 +103,10 @@ public: GenerateBroadcastIds(); GenerateBroadcastIds(); } } ~LeAudioBroadcasterImpl() override = default; ~LeAudioBroadcasterImpl() override { alarm_free(big_terminate_timer_); alarm_free(broadcast_stop_timer_); } void GenerateBroadcastIds(void) { void GenerateBroadcastIds(void) { btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) { btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) { Loading Loading @@ -141,6 +147,7 @@ public: le_audio_source_hal_client_.reset(); le_audio_source_hal_client_.reset(); } } audio_state_ = AudioState::SUSPENDED; audio_state_ = AudioState::SUSPENDED; cancelBroadcastTimers(); } } void Stop() { void Stop() { Loading Loading @@ -977,6 +984,57 @@ public: } } private: private: void SuspendAudioBroadcasts() { log::info(""); for (auto& broadcast_pair : broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } void StopAudioBroadcasts() { log::info(""); if (le_audio_source_hal_client_) { le_audio_source_hal_client_->Stop(); } for (auto& broadcast_pair : broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::STOP, nullptr); } bluetooth::le_audio::MetricsCollector::Get()->OnBroadcastStateChanged(false); } void setBroadcastTimers() { if (audio_state_ == AudioState::SUSPENDED) { log::info(" Started"); alarm_set_on_mloop( big_terminate_timer_, kBigTerminateTimeoutMs, [](void*) { if (instance) { instance->SuspendAudioBroadcasts(); } }, nullptr); alarm_set_on_mloop( broadcast_stop_timer_, kBroadcastStopTimeoutMs, [](void*) { if (instance) { instance->StopAudioBroadcasts(); } }, nullptr); } } void cancelBroadcastTimers() { log::info(""); alarm_cancel(big_terminate_timer_); alarm_cancel(broadcast_stop_timer_); } static class BroadcastStateMachineCallbacks static class BroadcastStateMachineCallbacks : public IBroadcastStateMachineCallbacks { : public IBroadcastStateMachineCallbacks { void OnStateMachineCreateStatus(uint32_t broadcast_id, void OnStateMachineCreateStatus(uint32_t broadcast_id, Loading Loading @@ -1067,6 +1125,9 @@ public: instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); } } } } if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->setBroadcastTimers(); } } } break; break; }; }; Loading Loading @@ -1336,13 +1397,7 @@ public: instance->audio_state_ = AudioState::SUSPENDED; instance->audio_state_ = AudioState::SUSPENDED; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->setBroadcastTimers(); // TODO: add some timeout to execute below for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; broadcast->SetMuted(true); broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } } } Loading @@ -1352,6 +1407,7 @@ public: instance->audio_state_ = AudioState::ACTIVE; instance->audio_state_ = AudioState::ACTIVE; if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) { instance->cancelBroadcastTimers(); instance->UpdateAudioActiveStateInPublicAnnouncement(); instance->UpdateAudioActiveStateInPublicAnnouncement(); for (auto& broadcast_pair : instance->broadcasts_) { for (auto& broadcast_pair : instance->broadcasts_) { Loading Loading @@ -1410,6 +1466,11 @@ public: // Flag to track iso state // Flag to track iso state bool is_iso_running_ = false; bool is_iso_running_ = false; static constexpr uint64_t kBigTerminateTimeoutMs = 10 * 1000; static constexpr uint64_t kBroadcastStopTimeoutMs = 30 * 60 * 1000; alarm_t* big_terminate_timer_; alarm_t* broadcast_stop_timer_; }; }; /* Static members definitions */ /* Static members definitions */ Loading
system/bta/le_audio/broadcaster/broadcaster_test.cc +211 −3 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include "stack/include/btm_iso_api.h" #include "stack/include/btm_iso_api.h" #include "test/common/mock_functions.h" #include "test/common/mock_functions.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_osi_alarm.h" #include "test/mock/mock_stack_btm_iso.h" #include "test/mock/mock_stack_btm_iso.h" #define TEST_BT com::android::bluetooth::flags #define TEST_BT com::android::bluetooth::flags Loading Loading @@ -71,6 +72,16 @@ extern "C" const char* __asan_default_options() { return "detect_container_overflow=0"; return "detect_container_overflow=0"; } } struct alarm_t { alarm_callback_t cb; void* data; alarm_t(const char* /* name */) { cb = nullptr; data = nullptr; } }; static base::Callback<void(BT_OCTET8)> generator_cb; static base::Callback<void(BT_OCTET8)> generator_cb; void btsnd_hcic_ble_rand(base::Callback<void(BT_OCTET8)> cb) { void btsnd_hcic_ble_rand(base::Callback<void(BT_OCTET8)> cb) { Loading Loading @@ -222,6 +233,9 @@ static const std::vector<uint8_t> media_metadata = { media_context & 0x00FF, (media_context & 0xFF00) >> 8}; media_context & 0x00FF, (media_context & 0xFF00) >> 8}; static const std::string test_broadcast_name = "Test"; static const std::string test_broadcast_name = "Test"; static const std::string big_terminate_timer_name = "BigTerminateTimer"; static const std::string broadcast_stop_timer_name = "BroadcastStopTimer"; class MockLeAudioBroadcasterCallbacks class MockLeAudioBroadcasterCallbacks : public bluetooth::le_audio::LeAudioBroadcasterCallbacks { : public bluetooth::le_audio::LeAudioBroadcasterCallbacks { public: public: Loading Loading @@ -277,6 +291,37 @@ class MockAudioHalClientEndpoint : public LeAudioSourceAudioHalClient { class BroadcasterTest : public Test { class BroadcasterTest : public Test { protected: protected: void SetUp() override { void SetUp() override { test::mock::osi_alarm::alarm_free.body = [](alarm_t* alarm) { if (alarm) { delete alarm; } }; test::mock::osi_alarm::alarm_new.body = [this](const char* name) { if (std::string(name) == big_terminate_timer_name) { this->big_terminate_timer_ = new alarm_t(name); return this->big_terminate_timer_; } else if (std::string(name) == broadcast_stop_timer_name) { this->broadcast_stop_timer_ = new alarm_t(name); return this->broadcast_stop_timer_; } else { return (alarm_t*)(nullptr); } }; test::mock::osi_alarm::alarm_set_on_mloop.body = [](alarm_t* alarm, uint64_t interval_ms, alarm_callback_t cb, void* data) { alarm->cb = cb; alarm->data = data; }; test::mock::osi_alarm::alarm_cancel.body = [](alarm_t* alarm) { if (alarm) { alarm->cb = nullptr; alarm->data = nullptr; } }; init_message_loop_thread(); init_message_loop_thread(); reset_mock_function_count_map(); reset_mock_function_count_map(); Loading Loading @@ -375,6 +420,13 @@ class BroadcasterTest : public Test { codec_manager_->Stop(); codec_manager_->Stop(); mock_codec_manager_ = nullptr; mock_codec_manager_ = nullptr; } } test::mock::osi_alarm::alarm_free = {}; test::mock::osi_alarm::alarm_new = {}; test::mock::osi_alarm::alarm_set_on_mloop = {}; test::mock::osi_alarm::alarm_cancel = {}; big_terminate_timer_ = nullptr; broadcast_stop_timer_ = nullptr; } } uint32_t InstantiateBroadcast( uint32_t InstantiateBroadcast( Loading Loading @@ -440,6 +492,9 @@ class BroadcasterTest : public Test { le_audio::CodecManager* codec_manager_ = nullptr; le_audio::CodecManager* codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; MockCodecManager* mock_codec_manager_ = nullptr; alarm_t* big_terminate_timer_ = nullptr; alarm_t* broadcast_stop_timer_ = nullptr; }; }; TEST_F(BroadcasterTest, Initialize) { TEST_F(BroadcasterTest, Initialize) { Loading Loading @@ -1283,11 +1338,56 @@ TEST_F_WITH_FLAGS(BroadcasterTest, AudioActiveState, ASSERT_EQ(updated_public_meta, public_metadata_audio_false); ASSERT_EQ(updated_public_meta, public_metadata_audio_false); } } TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend, TEST_F_WITH_FLAGS(BroadcasterTest, BigTerminationAndBroadcastStopWhenNoSoundFromTheBeginning, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // Broadcast stop timer execution, state machine go to STOPPED state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); // Imitate execution of BIG termination timer broadcast_stop_timer_->cb(broadcast_stop_timer_->data); } TEST_F_WITH_FLAGS(BroadcasterTest, BigTerminationAndBroadcastStopWhenNoSoundAfterSuspend, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) Loading @@ -1295,24 +1395,132 @@ TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSus EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); ASSERT_NE(audio_receiver, nullptr); // First onAudioResume when BIG already created, not cause any action // First onAudioResume when BIG already created, not cause any state change EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(2, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(4, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // OnAudioSuspend cause state machine go to CONFIGURED state so BIG termination // OnAudioResume before timer execution cancel them and not change the broadcast state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(4, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(6, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // Broadcast stop timer execution, state machine go to STOPPED state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED)) .Times(1); // Imitate execution of BIG termination timer broadcast_stop_timer_->cb(broadcast_stop_timer_->data); } TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, leaudio_big_depends_on_audio_state))) { // Timers created ASSERT_TRUE(big_terminate_timer_ != nullptr); ASSERT_TRUE(broadcast_stop_timer_ != nullptr); auto broadcast_id = InstantiateBroadcast(); LeAudioSourceAudioHalClient::Callbacks* audio_receiver; EXPECT_CALL(*mock_audio_source_, Start) .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true))) .WillRepeatedly(Return(false)); EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); // Timers not started ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // Start Broadcast LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id); // Timers started ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); ASSERT_NE(audio_receiver, nullptr); // First onAudioResume when BIG already created, not cause any state change EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(2, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); ASSERT_EQ(4, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // OnAudioResume before timer execution cancel them and not change the broadcast state EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, _)).Times(0); audio_receiver->OnAudioResume(); // Timers cancelled ASSERT_EQ(4, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); // OnAudioSuspend cause starting the BIG termination timer audio_receiver->OnAudioSuspend(); audio_receiver->OnAudioSuspend(); ASSERT_EQ(6, get_func_call_count("alarm_set_on_mloop")); ASSERT_TRUE(big_terminate_timer_->cb != nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr); // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED)) .Times(1); // Imitate execution of BIG termination timer big_terminate_timer_->cb(big_terminate_timer_->data); // onAudioResume cause state machine go to STREAMING state so BIG creation // onAudioResume cause state machine go to STREAMING state so BIG creation EXPECT_CALL(mock_broadcaster_callbacks_, EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .Times(1); .Times(1); audio_receiver->OnAudioResume(); audio_receiver->OnAudioResume(); // Timers Cancelled ASSERT_EQ(6, get_func_call_count("alarm_cancel")); ASSERT_TRUE(big_terminate_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); } } // TODO: Add tests for: // TODO: Add tests for: Loading