Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79acd643 authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk
Browse files

broadcaster: Handle suspend/resume request before controller setup

This change allows to not interrupt processing controller setup in case
if suspend callback and then resume would be called.

Bug: 358587027
Bug: 360435489
Test: atest --host bluetooth_test_broadcaster_state_machine
Test: manual test start/stop/enable/disable broadcast
Flag: TEST_ONLY
Change-Id: I3283145b0abe4006bb88aeed4e4a7d9b32c5ee00
parent 014c3999
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -1311,13 +1311,6 @@ private:
      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->UpdateAudioActiveStateInPublicAnnouncement();
        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);
          }
        }
      }
    }

+0 −71
Original line number Diff line number Diff line
@@ -1424,77 +1424,6 @@ TEST_F(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSuspend) {
  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:
// ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data)

+23 −24
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ const int kAdvertisingChannelAll =
class BroadcastStateMachineImpl : public BroadcastStateMachine {
public:
  BroadcastStateMachineImpl(BroadcastStateMachineConfig msg)
      : active_config_(std::nullopt), sm_config_(std::move(msg)), suspending_(false) {}
      : active_config_(std::nullopt), sm_config_(std::move(msg)) {}

  ~BroadcastStateMachineImpl() {
    if (GetState() == State::STREAMING) {
@@ -233,7 +233,6 @@ public:
private:
  std::optional<BigConfig> active_config_;
  BroadcastStateMachineConfig sm_config_;
  bool suspending_;

  /* Message handlers for each possible state */
  typedef std::function<void(const void*)> msg_handler_t;
@@ -247,11 +246,14 @@ private:
          /* in CONFIGURING state */
          [](const void*) { /* Do nothing */ },
          /* in CONFIGURED state */
          [this](const void*) { CreateBig(); },
          [this](const void*) {
            SetState(State::ENABLING);
            CreateBig();
          },
          /* in ENABLING state */
          [](const void*) { /* Do nothing */ },
          /* in DISABLING state */
          [](const void*) { /* Do nothing */ },
          [this](const void*) { SetState(State::ENABLING); },
          /* in STOPPING state */
          [](const void*) { /* Do nothing */ },
          /* in STREAMING state */
@@ -276,12 +278,9 @@ private:
          [](const void*) { /* Do nothing */ },
          /* in STREAMING state */
          [this](const void*) {
            if ((active_config_ != std::nullopt) && !suspending_) {
              suspending_ = false;
            SetState(State::STOPPING);
            callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState());
            TriggerIsoDatapathTeardown(active_config_->connection_handles[0]);
            }
          }};

  const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> suspend_msg_handlers{
@@ -290,26 +289,23 @@ private:
          /* in CONFIGURING state */
          [](const void*) { /* Do nothing */ },
          /* in CONFIGURED state */
          [](const void*) { /* Already suspended */ },
          /* in ENABLING state */
          [this](const void*) {
            suspending_ = true;
            SetState(State::DISABLING);

            /* Terminate BIG if suspend happens before setting STREAMING state */
            if (active_config_ != std::nullopt) {
              TerminateBig();
            }
          },
          /* in ENABLING state */
          [](const void*) { /* Do nothing */ },
          /* in DISABLING state */
          [](const void*) { /* Do nothing */ },
          /* in STOPPING state */
          [](const void*) { /* Do nothing */ },
          /* in STREAMING state */
          [this](const void*) {
            if ((active_config_ != std::nullopt) && !suspending_) {
              suspending_ = true;
            SetState(State::DISABLING);
            TriggerIsoDatapathTeardown(active_config_->connection_handles[0]);
            }
          }};

  const std::array<msg_handler_t, BroadcastStateMachine::STATE_COUNT> resume_msg_handlers{
@@ -318,7 +314,10 @@ private:
          /* in CONFIGURING state */
          [](const void*) { /* Do nothing */ },
          /* in CONFIGURED state */
          [this](const void*) { CreateBig(); },
          [this](const void*) {
            SetState(State::ENABLING);
            CreateBig();
          },
          /* in ENABLING state */
          [](const void*) { /* Do nothing */ },
          /* in DISABLING state */
@@ -422,7 +421,7 @@ private:
  }

  void TerminateBig() {
    log::info("suspending={}", suspending_);
    log::info("disabling={}", GetState() == BroadcastStateMachine::State::DISABLING);
    /* Terminate with reason: Connection Terminated By Local Host */
    IsoManager::GetInstance()->TerminateBig(GetAdvertisingSid(), 0x16);
  }
@@ -433,7 +432,7 @@ private:

    if (status != 0) {
      log::error("Failure creating data path. Tearing down the BIG now.");
      suspending_ = true;
      SetState(State::DISABLING);
      TerminateBig();
      return;
    }
@@ -554,7 +553,7 @@ private:
                  .connection_handles = evt->conn_handles,
          };

          if (suspending_) {
          if (GetState() == BroadcastStateMachine::State::DISABLING) {
            log::info("Terminating BIG due to stream suspending, big_id={}", evt->big_id);
            TerminateBig();
          } else {
@@ -578,14 +577,14 @@ private:
        }

        active_config_ = std::nullopt;
        bool disabling = GetState() == BroadcastStateMachine::State::DISABLING;

        /* Go back to configured if BIG is inactive (we are still announcing) */
        SetState(State::CONFIGURED);

        /* Check if we got this HCI event due to STOP or SUSPEND message. */
        if (suspending_) {
        if (disabling) {
          callbacks_->OnStateMachineEvent(GetBroadcastId(), GetState(), evt);
          suspending_ = false;
        } else {
          DisableAnnouncement();
        }
+44 −2
Original line number Diff line number Diff line
@@ -656,7 +656,7 @@ TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateBigCreateComplet
  EXPECT_CALL(*mock_iso_manager_, CreateBig(_, _)).WillOnce(Return());
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START);

  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED);
  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::ENABLING);

  EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1);
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND);
@@ -684,7 +684,7 @@ TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateIsoDataPathSetUp
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).WillOnce(Return());
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START);

  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED);
  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::ENABLING);

  EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(1);
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND);
@@ -695,6 +695,48 @@ TEST_F(StateMachineTest, ProcessMessageSuspendWhenConfiguredLateIsoDataPathSetUp
  // There shall be no change in state
  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::CONFIGURED);
}

TEST_F(StateMachineTest, ProcessMessageDoubleResumeWhenConfiguredLateBigCreateCompleteEvent) {
  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::ENABLING);

  EXPECT_CALL(*mock_iso_manager_, TerminateBig(_, _)).Times(0);
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::SUSPEND);

  /* Broadcast is resumed again before getting BIG created event */
  EXPECT_CALL(*(sm_callbacks_.get()),
              OnStateMachineEvent(broadcast_id, BroadcastStateMachine::State::STREAMING, _))
          .Times(1);
  broadcasts_[broadcast_id]->ProcessMessage(BroadcastStateMachine::Message::START);

  /* Inject late BIG create complete event */
  // For test convenience lets encode big_id into conn_hdl MSB.
  // NOTE: In current implementation big_id is equal to advertising SID.
  //       This is an important detail exploited by the IsoManager mock
  static uint8_t conn_lsb = 1;
  uint16_t conn_msb = ((uint16_t)broadcasts_[broadcast_id]->GetAdvertisingSid()) << 8;

  big_create_cmpl_evt evt;
  evt.big_id = broadcasts_[broadcast_id]->GetAdvertisingSid();
  evt.conn_handles.push_back(conn_msb | conn_lsb++);
  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);

  ASSERT_EQ(broadcasts_[broadcast_id]->GetState(), BroadcastStateMachine::State::STREAMING);
}

TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) {
  auto broadcast_id =
          InstantiateStateMachine(bluetooth::le_audio::types::LeAudioContextType::MEDIA);