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

Commit deff763f authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Automerger Merge Worker
Browse files

Merge "Broadcaster: Add timers to terminate BIG and stop broadcast" into main...

Merge "Broadcaster: Add timers to terminate BIG and stop broadcast" into main am: 45fde87b am: b217455a

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/3141398



Change-Id: I895232ceb737f670588ca85b8c6b0b8a5c182a06
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents ab6c2b30 b217455a
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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",
+70 −9
Original line number Original line Diff line number Diff line
@@ -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"
@@ -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 */
@@ -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) {
@@ -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() {
@@ -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,
@@ -1067,6 +1125,9 @@ public:
                instance->UpdateAudioActiveStateInPublicAnnouncement();
                instance->UpdateAudioActiveStateInPublicAnnouncement();
              }
              }
            }
            }
            if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
              instance->setBroadcastTimers();
            }
          }
          }
          break;
          break;
      };
      };
@@ -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);
        }
      }
      }
    }
    }


@@ -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_) {
@@ -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 */
+211 −3
Original line number Original line Diff line number Diff line
@@ -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
@@ -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) {
@@ -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:
@@ -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();
@@ -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(
@@ -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) {
@@ -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)))
@@ -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: