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

Commit 81659657 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Gerrit Code Review
Browse files

Merge "leaudio: Provide audio sessions to codec manager" into main

parents a2e77ea8 1587048c
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -141,6 +141,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {

    if (le_audio_source_hal_client_) {
      le_audio_source_hal_client_->Stop();
      auto result =
          CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
              le_audio_source_hal_client_.get(), false);
      log::assert_that(result, "Could not update session in codec manager");
      le_audio_source_hal_client_.reset();
    }
  }
@@ -575,8 +579,21 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
      requirements.subgroup_quality.push_back(
          {ChooseConfigurationContextType(context_type), idx});
    }
    auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements);

    if (!le_audio_source_hal_client_) {
      le_audio_source_hal_client_ =
          LeAudioSourceAudioHalClient::AcquireBroadcast();
      if (!le_audio_source_hal_client_) {
        log::error("Could not acquire le audio");
        return;
      }
      auto result =
          CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
              le_audio_source_hal_client_.get(), true);
      log::assert_that(result, "Could not update session in codec manager");
    }

    auto config = CodecManager::GetInstance()->GetBroadcastConfig(requirements);
    if (!config) {
      log::error("No valid broadcast offload config");
      callbacks_->OnBroadcastCreated(bluetooth::le_audio::kBroadcastIdInvalid,
@@ -689,6 +706,11 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
          log::error("Could not acquire le audio");
          return;
        }

        auto result =
            CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
                le_audio_source_hal_client_.get(), true);
        log::assert_that(result, "Could not update session in codec manager");
      }

      broadcasts_[broadcast_id]->ProcessMessage(
@@ -719,6 +741,14 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
  void DestroyAudioBroadcast(uint32_t broadcast_id) override {
    log::info("Destroying broadcast_id={}", broadcast_id);
    broadcasts_.erase(broadcast_id);

    if (broadcasts_.empty() && le_audio_source_hal_client_) {
      auto result =
          CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
              le_audio_source_hal_client_.get(), false);
      log::assert_that(result, "Could not update session in codec manager");
      le_audio_source_hal_client_.reset();
    }
  }

  std::optional<bluetooth::le_audio::BroadcastMetadata> GetBroadcastMetadataOpt(
@@ -845,6 +875,10 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
                         "assert failed: broadcasts_.count(broadcast_id) != 0");
        broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT,
                                                  evt);
        auto result =
            CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
                le_audio_source_hal_client_.get(), false);
        log::assert_that(result, "Could not update session in codec manager");
        le_audio_source_hal_client_.reset();
      } break;
      default:
+126 −9
Original line number Diff line number Diff line
@@ -261,13 +261,11 @@ class BroadcasterTest : public Test {
    ASSERT_NE(iso_manager_, nullptr);
    iso_manager_->Start();

    is_audio_hal_acquired = false;
    mock_audio_source_ = new MockAudioHalClientEndpoint();
    ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true));
    ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() {
      mock_audio_source_ = nullptr;
      is_audio_hal_acquired = false;
    });
    mock_iso_manager_ = MockIsoManager::GetInstance();
    ON_CALL(*mock_iso_manager_, RegisterBigCallbacks(_))
        .WillByDefault(SaveArg<0>(&big_callbacks_));

    ConfigAudioHalClientMock();

    EXPECT_CALL(*MockIsoManager::GetInstance(),
                RegisterOnIsoTrafficActiveCallbacks)
@@ -287,6 +285,10 @@ class BroadcasterTest : public Test {

    ConfigCodecManagerMock(types::CodecLocation::HOST);

    ON_CALL(*mock_codec_manager_, UpdateActiveUnicastAudioHalClient(_, _, _))
        .WillByDefault(Return(true));
    ON_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(_, _))
        .WillByDefault(Return(true));
    ON_CALL(*mock_codec_manager_, GetBroadcastConfig)
        .WillByDefault(
            Invoke([](const bluetooth::le_audio::CodecManager::
@@ -297,6 +299,16 @@ class BroadcasterTest : public Test {
            }));
  }

  void ConfigAudioHalClientMock() {
    is_audio_hal_acquired = false;
    mock_audio_source_ = new MockAudioHalClientEndpoint();
    ON_CALL(*mock_audio_source_, Start).WillByDefault(Return(true));
    ON_CALL(*mock_audio_source_, OnDestroyed).WillByDefault([]() {
      mock_audio_source_ = nullptr;
      is_audio_hal_acquired = false;
    });
  }

  void ConfigCodecManagerMock(types::CodecLocation location) {
    codec_manager_ = le_audio::CodecManager::GetInstance();
    ASSERT_NE(codec_manager_, nullptr);
@@ -363,10 +375,41 @@ class BroadcasterTest : public Test {
    return broadcast_id;
  }

  void InjectBigCreateComplete(uint8_t big_id, uint8_t status) {
    std::vector<uint16_t> conn_handles = {0x10, 0x12};

    hci::iso_manager::big_create_cmpl_evt evt = {
        .status = status,
        .big_id = big_id,
        .big_sync_delay = 1231,
        .transport_latency_big = 1234,
        .phy = 2,
        .nse = 3,
        .bn = 2,
        .pto = 2,
        .irc = 2,
        .max_pdu = 128,
        .iso_interval = 10,
        .conn_handles = conn_handles,
    };

    big_callbacks_->OnBigEvent(
        bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, &evt);
  }

  void InjectBigTerminateComplete(uint8_t big_id, uint8_t reason) {
    hci::iso_manager::big_terminate_cmpl_evt evt = {.big_id = big_id,
                                                    .reason = reason};
    big_callbacks_->OnBigEvent(
        bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, &evt);
  }

 protected:
  MockLeAudioBroadcasterCallbacks mock_broadcaster_callbacks_;
  bluetooth::hci::testing::MockControllerInterface mock_controller_;
  bluetooth::hci::IsoManager* iso_manager_;
  MockIsoManager* mock_iso_manager_;
  bluetooth::hci::iso_manager::BigCallbacks* big_callbacks_ = nullptr;

  le_audio::CodecManager* codec_manager_ = nullptr;
  MockCodecManager* mock_codec_manager_ = nullptr;
@@ -419,19 +462,38 @@ TEST_F(BroadcasterTest, CreateAudioBroadcastMultiGroups) {
}

TEST_F(BroadcasterTest, SuspendAudioBroadcast) {
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(1);
  auto broadcast_id = InstantiateBroadcast();
  LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id);
  Mock::VerifyAndClearExpectations(mock_codec_manager_);

  EXPECT_CALL(mock_broadcaster_callbacks_,
              OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED))
      .Times(1);

  EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1));
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _))
      .Times(0);
  LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id);
  Mock::VerifyAndClearExpectations(mock_codec_manager_);
}

TEST_F(BroadcasterTest, StartAudioBroadcast) {
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(1);
  auto broadcast_id = InstantiateBroadcast();
  Mock::VerifyAndClearExpectations(mock_codec_manager_);

  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(0);
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false))
      .Times(0);
  LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id);

  EXPECT_CALL(mock_broadcaster_callbacks_,
@@ -460,11 +522,18 @@ TEST_F(BroadcasterTest, StartAudioBroadcast) {
  EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(1);
  std::vector<uint8_t> sample_data(320, 0);
  audio_receiver->OnAudioDataReady(sample_data);

  Mock::VerifyAndClearExpectations(mock_codec_manager_);
}

TEST_F(BroadcasterTest, StartAudioBroadcastMedia) {
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(1);
  auto broadcast_id = InstantiateBroadcast(media_metadata, default_code,
                                           {bluetooth::le_audio::QUALITY_HIGH});
  Mock::VerifyAndClearExpectations(mock_codec_manager_);

  LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id);

  EXPECT_CALL(mock_broadcaster_callbacks_,
@@ -474,6 +543,12 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) {
  LeAudioSourceAudioHalClient::Callbacks* audio_receiver;
  EXPECT_CALL(*mock_audio_source_, Start)
      .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true)));
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(0);
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false))
      .Times(0);

  LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id);
  ASSERT_NE(audio_receiver, nullptr);
@@ -494,27 +569,68 @@ TEST_F(BroadcasterTest, StartAudioBroadcastMedia) {
  EXPECT_CALL(*MockIsoManager::GetInstance(), SendIsoData).Times(2);
  std::vector<uint8_t> sample_data(1920, 0);
  audio_receiver->OnAudioDataReady(sample_data);
  Mock::VerifyAndClearExpectations(mock_codec_manager_);
}

TEST_F(BroadcasterTest, StopAudioBroadcast) {
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(1);
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false))
      .Times(1);
  auto broadcast_id = InstantiateBroadcast();
  LeAudioBroadcaster::Get()->StartAudioBroadcast(broadcast_id);

  // NOTICE: This is really an implementation specific part, we fake the BIG
  //         config as the mocked state machine does not even call the
  //         IsoManager to prepare one (and that's good since IsoManager is also
  //         a mocked one).

  auto mock_state_machine = MockBroadcastStateMachine::GetLastInstance();
  BigConfig big_cfg;
  big_cfg.big_id = mock_state_machine->GetAdvertisingSid();
  big_cfg.connection_handles = {0x10, 0x12};
  big_cfg.max_pdu = 128;
  mock_state_machine->SetExpectedBigConfig(big_cfg);

  InjectBigCreateComplete(big_cfg.big_id, 0x00);
  EXPECT_CALL(mock_broadcaster_callbacks_,
              OnBroadcastStateChanged(broadcast_id, BroadcastState::STOPPED))
      .Times(1);

  EXPECT_CALL(*mock_audio_source_, Stop).Times(AtLeast(1));
  LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id);
  InjectBigTerminateComplete(big_cfg.big_id, 0x16);
  Mock::VerifyAndClearExpectations(mock_codec_manager_);
}

TEST_F(BroadcasterTest, DestroyAudioBroadcast) {
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
      .Times(1);

  auto broadcast_id = InstantiateBroadcast();

  Mock::VerifyAndClearExpectations(mock_codec_manager_);

  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, false))
      .Times(1);
  EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastDestroyed(broadcast_id))
      .Times(1);
  LeAudioBroadcaster::Get()->DestroyAudioBroadcast(broadcast_id);

  Mock::VerifyAndClearExpectations(mock_codec_manager_);
  ASSERT_EQ(mock_audio_source_, nullptr);

  /* Create a mock again for the test purpose */
  ConfigAudioHalClientMock();

  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveBroadcastAudioHalClient(mock_audio_source_, _))
      .Times(0);

  // Expect not being able to interact with this Broadcast
  EXPECT_CALL(mock_broadcaster_callbacks_,
              OnBroadcastStateChanged(broadcast_id, _))
@@ -528,6 +644,9 @@ TEST_F(BroadcasterTest, DestroyAudioBroadcast) {

  EXPECT_CALL(*mock_audio_source_, Stop).Times(0);
  LeAudioBroadcaster::Get()->SuspendAudioBroadcast(broadcast_id);

  Mock::VerifyAndClearExpectations(mock_codec_manager_);
  Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_);
}

TEST_F(BroadcasterTest, GetBroadcastAllStates) {
@@ -945,8 +1064,6 @@ TEST_F(BroadcasterTest, SanityTest) {
}

TEST_F(BroadcasterTest, VendorCodecConfig) {
  ConfigCodecManagerMock(types::CodecLocation::HOST);

  ON_CALL(*mock_codec_manager_, GetBroadcastConfig)
      .WillByDefault(Invoke([](const bluetooth::le_audio::CodecManager::
                                   BroadcastConfigurationRequirements&) {
+12 −0
Original line number Diff line number Diff line
@@ -1295,6 +1295,12 @@ class LeAudioClientImpl : public LeAudioClient {
      }
    }

    auto result =
        CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient(
            le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(),
            true);
    log::assert_that(result, "Could not update session to codec manager");

    /* Mini policy: Try configure audio HAL sessions with most recent context.
     * If reconfiguration is not needed it means, context type is not supported.
     * If most recent scenario is not supported, try to find first supported.
@@ -5900,6 +5906,12 @@ class LeAudioClientImpl : public LeAudioClient {
      log::info("ClientAudioInterfaceRelease - cleanup");
    }

    auto result =
        CodecManager::GetInstance()->UpdateActiveUnicastAudioHalClient(
            le_audio_source_hal_client_.get(), le_audio_sink_hal_client_.get(),
            false);
    log::assert_that(result, "Could not update session to codec manager");

    if (le_audio_source_hal_client_) {
      le_audio_source_hal_client_->Stop();
      le_audio_source_hal_client_.reset();
+116 −0
Original line number Diff line number Diff line
@@ -204,6 +204,99 @@ struct codec_manager_impl {
    }
  }

  bool UpdateActiveUnicastAudioHalClient(
      LeAudioSourceAudioHalClient* source_unicast_client,
      LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) {
    log::debug("local_source: {}, local_sink: {}, is_active: {}",
               fmt::ptr(source_unicast_client), fmt::ptr(sink_unicast_client),
               is_active);

    if (source_unicast_client == nullptr && sink_unicast_client == nullptr) {
      return false;
    }

    if (is_active) {
      if (source_unicast_client && unicast_local_source_hal_client != nullptr) {
        log::error("Trying to override previous source hal client {}",
                   fmt::ptr(unicast_local_source_hal_client));
        return false;
      }

      if (sink_unicast_client && unicast_local_sink_hal_client != nullptr) {
        log::error("Trying to override previous sink hal client {}",
                   fmt::ptr(unicast_local_sink_hal_client));
        return false;
      }

      if (source_unicast_client) {
        unicast_local_source_hal_client = source_unicast_client;
      }

      if (sink_unicast_client) {
        unicast_local_sink_hal_client = sink_unicast_client;
      }

      return true;
    }

    if (source_unicast_client &&
        source_unicast_client != unicast_local_source_hal_client) {
      log::error("local source session does not match {} != {}",
                 fmt::ptr(source_unicast_client),
                 fmt::ptr(unicast_local_source_hal_client));
      return false;
    }

    if (sink_unicast_client &&
        sink_unicast_client != unicast_local_sink_hal_client) {
      log::error("local source session does not match {} != {}",
                 fmt::ptr(sink_unicast_client),
                 fmt::ptr(unicast_local_sink_hal_client));
      return false;
    }

    if (source_unicast_client) {
      unicast_local_source_hal_client = nullptr;
    }

    if (sink_unicast_client) {
      unicast_local_sink_hal_client = nullptr;
    }

    return true;
  }

  bool UpdateActiveBroadcastAudioHalClient(
      LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) {
    log::debug("local_source: {},is_active: {}",
               fmt::ptr(source_broadcast_client), is_active);

    if (source_broadcast_client == nullptr) {
      return false;
    }

    if (is_active) {
      if (broadcast_local_source_hal_client != nullptr) {
        log::error("Trying to override previous source hal client {}",
                   fmt::ptr(broadcast_local_source_hal_client));
        return false;
      }
      broadcast_local_source_hal_client = source_broadcast_client;
      return true;
    }

    if (source_broadcast_client != broadcast_local_source_hal_client) {
      log::error("local source session does not match {} != {}",
                 fmt::ptr(source_broadcast_client),
                 fmt::ptr(broadcast_local_source_hal_client));
      return false;
    }

    broadcast_local_source_hal_client = nullptr;

    return true;
  }

  AudioSetConfigurations GetSupportedCodecConfigurations(
      const CodecManager::UnicastConfigurationRequirements& requirements)
      const {
@@ -945,6 +1038,10 @@ struct codec_manager_impl {
  std::vector<btle_audio_codec_config_t> codec_input_capa = {};
  std::vector<btle_audio_codec_config_t> codec_output_capa = {};
  int broadcast_target_config = -1;

  LeAudioSourceAudioHalClient* unicast_local_source_hal_client = nullptr;
  LeAudioSinkAudioHalClient* unicast_local_sink_hal_client = nullptr;
  LeAudioSourceAudioHalClient* broadcast_local_source_hal_client = nullptr;
};

std::ostream& operator<<(
@@ -1033,6 +1130,25 @@ void CodecManager::UpdateActiveAudioConfig(
        stream_params, delays_ms, update_receiver);
}

bool CodecManager::UpdateActiveUnicastAudioHalClient(
    LeAudioSourceAudioHalClient* source_unicast_client,
    LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active) {
  if (pimpl_->IsRunning()) {
    return pimpl_->codec_manager_impl_->UpdateActiveUnicastAudioHalClient(
        source_unicast_client, sink_unicast_client, is_active);
  }
  return false;
}

bool CodecManager::UpdateActiveBroadcastAudioHalClient(
    LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active) {
  if (pimpl_->IsRunning()) {
    return pimpl_->codec_manager_impl_->UpdateActiveBroadcastAudioHalClient(
        source_broadcast_client, is_active);
  }
  return false;
}

std::unique_ptr<AudioSetConfiguration> CodecManager::GetCodecConfig(
    const CodecManager::UnicastConfigurationRequirements& requirements,
    CodecManager::UnicastConfigurationVerifier verifier) {
+8 −0
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@

namespace bluetooth::le_audio {

class LeAudioSinkAudioHalClient;
class LeAudioSourceAudioHalClient;

struct stream_map_info {
  stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation,
                  bool is_stream_active)
@@ -106,6 +109,11 @@ class CodecManager {
      const std::vector<struct types::cis>& cises,
      const stream_parameters& stream_params, uint8_t direction);
  virtual void ClearCisConfiguration(uint8_t direction);
  virtual bool UpdateActiveUnicastAudioHalClient(
      LeAudioSourceAudioHalClient* source_unicast_client,
      LeAudioSinkAudioHalClient* sink_unicast_client, bool is_active);
  virtual bool UpdateActiveBroadcastAudioHalClient(
      LeAudioSourceAudioHalClient* source_broadcast_client, bool is_active);
  virtual void UpdateActiveAudioConfig(
      const types::BidirectionalPair<stream_parameters>& stream_params,
      types::BidirectionalPair<uint16_t> delays_ms,
Loading