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

Commit 20cede83 authored by Michal Belusiak's avatar Michal Belusiak
Browse files

Broadcaster: Add timers to terminate BIG and stop broadcast

Terminate BIG after 10s from onAudioSuspend.
Stop broadcast after 30min from onAudioSuspend.

Bug: 273864658
Bug: 347204335
Test: atest --host bluetooth_test_broadcaster --no-bazel-mode
Change-Id: I4640ad750c0b3936f2159f53fd69a1080c3d8e09
parent 01abfd92
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1170,10 +1170,10 @@ cc_test {
    srcs: [
        ":TestCommonMockFunctions",
        ":TestCommonStackConfig",
        ":TestFakeOsi",
        ":TestMockMainShim",
        ":TestMockMainShimEntry",
        ":TestMockStackBtmIso",
        ":TestStubOsi",
        "le_audio/broadcaster/broadcaster.cc",
        "le_audio/broadcaster/broadcaster_test.cc",
        "le_audio/broadcaster/broadcaster_types.cc",
+70 −9
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include "hci/controller_interface.h"
#include "internal_include/stack_config.h"
#include "main/shim/entry.h"
#include "osi/include/alarm.h"
#include "osi/include/properties.h"
#include "stack/include/bt_types.h"
#include "stack/include/btm_api_types.h"
@@ -90,7 +91,9 @@ public:
      : callbacks_(callbacks_),
        current_phy_(PHY_LE_2M),
        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("");

    /* Register State machine callbacks */
@@ -100,7 +103,10 @@ public:
    GenerateBroadcastIds();
  }

  ~LeAudioBroadcasterImpl() override = default;
  ~LeAudioBroadcasterImpl() override {
    alarm_free(big_terminate_timer_);
    alarm_free(broadcast_stop_timer_);
  }

  void GenerateBroadcastIds(void) {
    btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) {
@@ -141,6 +147,7 @@ public:
      le_audio_source_hal_client_.reset();
    }
    audio_state_ = AudioState::SUSPENDED;
    cancelBroadcastTimers();
  }

  void Stop() {
@@ -977,6 +984,57 @@ public:
  }

 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
      : public IBroadcastStateMachineCallbacks {
    void OnStateMachineCreateStatus(uint32_t broadcast_id,
@@ -1067,6 +1125,9 @@ public:
                instance->UpdateAudioActiveStateInPublicAnnouncement();
              }
            }
            if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
              instance->setBroadcastTimers();
            }
          }
          break;
      };
@@ -1336,13 +1397,7 @@ public:
      instance->audio_state_ = AudioState::SUSPENDED;
      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->UpdateAudioActiveStateInPublicAnnouncement();

        // 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);
        }
        instance->setBroadcastTimers();
      }
    }

@@ -1352,6 +1407,7 @@ public:

      instance->audio_state_ = AudioState::ACTIVE;
      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->cancelBroadcastTimers();
        instance->UpdateAudioActiveStateInPublicAnnouncement();

        for (auto& broadcast_pair : instance->broadcasts_) {
@@ -1410,6 +1466,11 @@ public:

  // Flag to track iso state
  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 */
+211 −3
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include "stack/include/btm_iso_api.h"
#include "test/common/mock_functions.h"
#include "test/mock/mock_main_shim_entry.h"
#include "test/mock/mock_osi_alarm.h"
#include "test/mock/mock_stack_btm_iso.h"

#define TEST_BT com::android::bluetooth::flags
@@ -71,6 +72,16 @@ extern "C" const char* __asan_default_options() {
  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;

void btsnd_hcic_ble_rand(base::Callback<void(BT_OCTET8)> cb) {
@@ -222,6 +233,9 @@ static const std::vector<uint8_t> media_metadata = {
    media_context & 0x00FF, (media_context & 0xFF00) >> 8};
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
    : public bluetooth::le_audio::LeAudioBroadcasterCallbacks {
 public:
@@ -277,6 +291,37 @@ class MockAudioHalClientEndpoint : public LeAudioSourceAudioHalClient {
class BroadcasterTest : public Test {
 protected:
  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();

    reset_mock_function_count_map();
@@ -375,6 +420,13 @@ class BroadcasterTest : public Test {
      codec_manager_->Stop();
      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(
@@ -440,6 +492,9 @@ class BroadcasterTest : public Test {

  le_audio::CodecManager* codec_manager_ = nullptr;
  MockCodecManager* mock_codec_manager_ = nullptr;

  alarm_t* big_terminate_timer_ = nullptr;
  alarm_t* broadcast_stop_timer_ = nullptr;
};

TEST_F(BroadcasterTest, Initialize) {
@@ -1283,11 +1338,56 @@ TEST_F_WITH_FLAGS(BroadcasterTest, AudioActiveState,
  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,
                                                      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);

  // 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;
  EXPECT_CALL(*mock_audio_source_, Start)
          .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true)))
@@ -1295,24 +1395,132 @@ TEST_F_WITH_FLAGS(BroadcasterTest, BigCreationTerminationDependsOnAudioResumeSus
  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 action
  // 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);

  // 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_,
              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, 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();
  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
  EXPECT_CALL(mock_broadcaster_callbacks_,
              OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING))
          .Times(1);
  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: