Loading system/bta/le_audio/broadcaster/broadcaster.cc +7 −0 Original line number Original line Diff line number Diff line Loading @@ -1307,6 +1307,13 @@ private: 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(); instance->setBroadcastTimers(); for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; if (broadcast->GetState() == BroadcastStateMachine::State::CONFIGURED) { broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } } } } Loading system/bta/le_audio/broadcaster/broadcaster_test.cc +72 −0 Original line number Original line Diff line number Diff line Loading @@ -65,6 +65,7 @@ using bluetooth::le_audio::DsaMode; using bluetooth::le_audio::LeAudioCodecConfiguration; using bluetooth::le_audio::LeAudioCodecConfiguration; using bluetooth::le_audio::LeAudioSourceAudioHalClient; using bluetooth::le_audio::LeAudioSourceAudioHalClient; using bluetooth::le_audio::broadcaster::BigConfig; using bluetooth::le_audio::broadcaster::BigConfig; using bluetooth::le_audio::broadcaster::BroadcastStateMachine; using bluetooth::le_audio::broadcaster::BroadcastSubgroupCodecConfig; using bluetooth::le_audio::broadcaster::BroadcastSubgroupCodecConfig; // Disables most likely false-positives from base::SplitString() // Disables most likely false-positives from base::SplitString() Loading Loading @@ -1423,6 +1424,77 @@ TEST_F(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend) { ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); } } TEST_F(BroadcasterTest, AudioSuspendBeforeBigCreateCallback) { com::android::bluetooth::flags::provider_->leaudio_big_depends_on_audio_state(true); // 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); 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); // 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); auto mock_state_machine = MockBroadcastStateMachine::GetLastInstance(); /* Prepare method that would not proceed BIG created event */ EXPECT_CALL(*mock_state_machine, ProcessMessage(BroadcastStateMachine::Message::START, _)) .WillOnce(Return()); // onAudioResume cause state machine go to STREAMING state so BIG creation EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .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 EXPECT_CALL(*mock_state_machine, ProcessMessage(BroadcastStateMachine::Message::SUSPEND, _)) .WillOnce(Return()); 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); } // TODO: Add tests for: // TODO: Add tests for: // ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data) // ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data) Loading system/bta/le_audio/broadcaster/state_machine.cc +16 −3 Original line number Original line Diff line number Diff line Loading @@ -282,7 +282,14 @@ private: /* in CONFIGURING state */ /* in CONFIGURING state */ [](const void*) { /* Do nothing */ }, [](const void*) { /* Do nothing */ }, /* in CONFIGURED state */ /* in CONFIGURED state */ [](const void*) { /* Already suspended */ }, [this](const void*) { suspending_ = true; /* Terminate BIG if suspend happens before setting STREAMING state */ if (active_config_ != std::nullopt) { TerminateBig(); } }, /* in STOPPING state */ /* in STOPPING state */ [](const void*) { /* Do nothing */ }, [](const void*) { /* Do nothing */ }, /* in STREAMING state */ /* in STREAMING state */ Loading Loading @@ -530,8 +537,14 @@ private: .iso_interval = evt->iso_interval, .iso_interval = evt->iso_interval, .connection_handles = evt->conn_handles, .connection_handles = evt->conn_handles, }; }; if (suspending_) { log::info("Terminating BIG due to stream suspending, big_id={}", evt->big_id); TerminateBig(); } else { callbacks_->OnBigCreated(evt->conn_handles); callbacks_->OnBigCreated(evt->conn_handles); TriggerIsoDatapathSetup(evt->conn_handles[0]); TriggerIsoDatapathSetup(evt->conn_handles[0]); } } else { } else { log::error("State={} Event={}. Unable to create big, big_id={}, status={}", log::error("State={} Event={}. Unable to create big, big_id={}, status={}", ToString(GetState()), event, evt->big_id, evt->status); ToString(GetState()), event, evt->big_id, evt->status); Loading system/bta/le_audio/broadcaster/state_machine_test.cc +51 −0 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ using bluetooth::hci::IsoManager; using bluetooth::le_audio::BasicAudioAnnouncementData; using bluetooth::le_audio::BasicAudioAnnouncementData; using testing::_; using testing::_; using testing::Mock; using testing::Mock; using testing::Return; using testing::SaveArg; using testing::SaveArg; using testing::Test; using testing::Test; Loading Loading @@ -644,6 +645,56 @@ TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfigured) { ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } } TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateBigCreateCompleteEvent) { EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true)).Times(1); auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); /* Hold start process on BIG create */ EXPECT_CALL(*mock_iso_manager_, CreateBig(_, _)).WillOnce(Return()); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND); /* Inject late BIG create complete event */ big_create_cmpl_evt evt; evt.big_id = broadcasts_[broadcast_id]->GetAdvertisingSid(); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_CREATE_BIG_CPL_EVT, &evt); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath).Times(0); EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineEvent(broadcast_id, _, _)).Times(0); // There shall be no change in state ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateIsoDataPathSetUp) { EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true)).Times(1); auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); /* Hold start process on Setup Iso Data Path BIG create */ EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).WillOnce(Return()); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath).Times(0); EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineEvent(broadcast_id, _, _)).Times(0); // There shall be no change in state ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) { TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) { auto broadcast_id = auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); Loading Loading
system/bta/le_audio/broadcaster/broadcaster.cc +7 −0 Original line number Original line Diff line number Diff line Loading @@ -1307,6 +1307,13 @@ private: 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(); instance->setBroadcastTimers(); for (auto& broadcast_pair : instance->broadcasts_) { auto& broadcast = broadcast_pair.second; if (broadcast->GetState() == BroadcastStateMachine::State::CONFIGURED) { broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr); } } } } } } Loading
system/bta/le_audio/broadcaster/broadcaster_test.cc +72 −0 Original line number Original line Diff line number Diff line Loading @@ -65,6 +65,7 @@ using bluetooth::le_audio::DsaMode; using bluetooth::le_audio::LeAudioCodecConfiguration; using bluetooth::le_audio::LeAudioCodecConfiguration; using bluetooth::le_audio::LeAudioSourceAudioHalClient; using bluetooth::le_audio::LeAudioSourceAudioHalClient; using bluetooth::le_audio::broadcaster::BigConfig; using bluetooth::le_audio::broadcaster::BigConfig; using bluetooth::le_audio::broadcaster::BroadcastStateMachine; using bluetooth::le_audio::broadcaster::BroadcastSubgroupCodecConfig; using bluetooth::le_audio::broadcaster::BroadcastSubgroupCodecConfig; // Disables most likely false-positives from base::SplitString() // Disables most likely false-positives from base::SplitString() Loading Loading @@ -1423,6 +1424,77 @@ TEST_F(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend) { ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); ASSERT_TRUE(broadcast_stop_timer_->cb == nullptr); } } TEST_F(BroadcasterTest, AudioSuspendBeforeBigCreateCallback) { com::android::bluetooth::flags::provider_->leaudio_big_depends_on_audio_state(true); // 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); 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); // 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); auto mock_state_machine = MockBroadcastStateMachine::GetLastInstance(); /* Prepare method that would not proceed BIG created event */ EXPECT_CALL(*mock_state_machine, ProcessMessage(BroadcastStateMachine::Message::START, _)) .WillOnce(Return()); // onAudioResume cause state machine go to STREAMING state so BIG creation EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING)) .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 EXPECT_CALL(*mock_state_machine, ProcessMessage(BroadcastStateMachine::Message::SUSPEND, _)) .WillOnce(Return()); 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); } // TODO: Add tests for: // TODO: Add tests for: // ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data) // ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data) Loading
system/bta/le_audio/broadcaster/state_machine.cc +16 −3 Original line number Original line Diff line number Diff line Loading @@ -282,7 +282,14 @@ private: /* in CONFIGURING state */ /* in CONFIGURING state */ [](const void*) { /* Do nothing */ }, [](const void*) { /* Do nothing */ }, /* in CONFIGURED state */ /* in CONFIGURED state */ [](const void*) { /* Already suspended */ }, [this](const void*) { suspending_ = true; /* Terminate BIG if suspend happens before setting STREAMING state */ if (active_config_ != std::nullopt) { TerminateBig(); } }, /* in STOPPING state */ /* in STOPPING state */ [](const void*) { /* Do nothing */ }, [](const void*) { /* Do nothing */ }, /* in STREAMING state */ /* in STREAMING state */ Loading Loading @@ -530,8 +537,14 @@ private: .iso_interval = evt->iso_interval, .iso_interval = evt->iso_interval, .connection_handles = evt->conn_handles, .connection_handles = evt->conn_handles, }; }; if (suspending_) { log::info("Terminating BIG due to stream suspending, big_id={}", evt->big_id); TerminateBig(); } else { callbacks_->OnBigCreated(evt->conn_handles); callbacks_->OnBigCreated(evt->conn_handles); TriggerIsoDatapathSetup(evt->conn_handles[0]); TriggerIsoDatapathSetup(evt->conn_handles[0]); } } else { } else { log::error("State={} Event={}. Unable to create big, big_id={}, status={}", log::error("State={} Event={}. Unable to create big, big_id={}, status={}", ToString(GetState()), event, evt->big_id, evt->status); ToString(GetState()), event, evt->big_id, evt->status); Loading
system/bta/le_audio/broadcaster/state_machine_test.cc +51 −0 Original line number Original line Diff line number Diff line Loading @@ -41,6 +41,7 @@ using bluetooth::hci::IsoManager; using bluetooth::le_audio::BasicAudioAnnouncementData; using bluetooth::le_audio::BasicAudioAnnouncementData; using testing::_; using testing::_; using testing::Mock; using testing::Mock; using testing::Return; using testing::SaveArg; using testing::SaveArg; using testing::Test; using testing::Test; Loading Loading @@ -644,6 +645,56 @@ TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfigured) { ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } } TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateBigCreateCompleteEvent) { EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true)).Times(1); auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); /* Hold start process on BIG create */ EXPECT_CALL(*mock_iso_manager_, CreateBig(_, _)).WillOnce(Return()); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND); /* Inject late BIG create complete event */ big_create_cmpl_evt evt; evt.big_id = broadcasts_[broadcast_id]->GetAdvertisingSid(); broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_CREATE_BIG_CPL_EVT, &evt); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath).Times(0); EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineEvent(broadcast_id, _, _)).Times(0); // There shall be no change in state ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateIsoDataPathSetUp) { EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true)).Times(1); auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); /* Hold start process on Setup Iso Data Path BIG create */ EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).WillOnce(Return()); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START); ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1); broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath).Times(0); EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath).Times(0); EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineEvent(broadcast_id, _, _)).Times(0); // There shall be no change in state ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED); } TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) { TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) { auto broadcast_id = auto broadcast_id = InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA); Loading