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

Commit de7f3474 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Update metadata after stream reconfiguration

When reconfiguring stream, state machine goes to CONFIGURED state with
certain configuration.
After that it is AF resuming stream and if stream configuration matches,
CIG is created and state machine moves to QoS Configured state.
With this path, the new applied context types and ccid is now properly used


Bug: 302481144
Test: atest bluetooth_le_audio_client_test bluetooth_le_audio_test
Tag: #feature

Change-Id: I6e1b8f09f5143a80243bc29624012526b23018f4
parent b698e4e1
Loading
Loading
Loading
Loading
+29 −15
Original line number Diff line number Diff line
@@ -262,12 +262,16 @@ void LeAudioDeviceGroup::SetCigState(le_audio::types::CigState state) {
  cig_state_ = state;
}

bool LeAudioDeviceGroup::Activate(LeAudioContextType context_type) {
bool LeAudioDeviceGroup::Activate(
    LeAudioContextType context_type,
    const BidirectionalPair<AudioContexts>& metadata_context_types,
    BidirectionalPair<std::vector<uint8_t>> ccid_lists) {
  bool is_activate = false;
  for (auto leAudioDevice : leAudioDevices_) {
    if (leAudioDevice.expired()) continue;

    bool activated = leAudioDevice.lock()->ActivateConfiguredAses(context_type);
    bool activated = leAudioDevice.lock()->ActivateConfiguredAses(
        context_type, metadata_context_types, ccid_lists);
    LOG_INFO("Device %s is %s",
             ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice.lock().get()->address_),
             activated ? "activated" : " not activated");
@@ -1656,18 +1660,7 @@ bool LeAudioDevice::ConfigureAses(
      ase->retrans_nb = ent.qos.retransmission_number;
      ase->max_transport_latency = ent.qos.max_transport_latency;

      /* Filter multidirectional audio context for each ase direction */
      auto directional_audio_context =
          metadata_context_types.get(ase->direction) &
          GetAvailableContexts(ase->direction);
      if (directional_audio_context.any()) {
        ase->metadata = GetMetadata(directional_audio_context,
                                    ccid_lists.get(ase->direction));
      } else {
        ase->metadata =
            GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED),
                        std::vector<uint8_t>());
      }
      SetMetadataToAse(ase, metadata_context_types, ccid_lists);
    }

    LOG_DEBUG(
@@ -2921,7 +2914,26 @@ void LeAudioDevice::SetAvailableContexts(
  avail_contexts_.source = contexts.source;
}

bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) {
void LeAudioDevice::SetMetadataToAse(
    struct types::ase* ase,
    const BidirectionalPair<AudioContexts>& metadata_context_types,
    BidirectionalPair<std::vector<uint8_t>> ccid_lists) {
  /* Filter multidirectional audio context for each ase direction */
  auto directional_audio_context = metadata_context_types.get(ase->direction) &
                                   GetAvailableContexts(ase->direction);
  if (directional_audio_context.any()) {
    ase->metadata =
        GetMetadata(directional_audio_context, ccid_lists.get(ase->direction));
  } else {
    ase->metadata = GetMetadata(AudioContexts(LeAudioContextType::UNSPECIFIED),
                                std::vector<uint8_t>());
  }
}

bool LeAudioDevice::ActivateConfiguredAses(
    LeAudioContextType context_type,
    const BidirectionalPair<AudioContexts>& metadata_context_types,
    BidirectionalPair<std::vector<uint8_t>> ccid_lists) {
  if (conn_id_ == GATT_INVALID_CONN_ID) {
    LOG_WARN(" Device %s is not connected ",
             ADDRESS_TO_LOGGABLE_CSTR(address_));
@@ -2939,6 +2951,8 @@ bool LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) {
          conn_id_, ase.id, ase.cis_id, ase.cis_conn_hdl);
      ase.active = true;
      ret = true;
      /* update metadata */
      SetMetadataToAse(&ase, metadata_context_types, ccid_lists);
    }
  }

+14 −2
Original line number Diff line number Diff line
@@ -216,7 +216,16 @@ class LeAudioDevice {
      types::BidirectionalPair<types::AudioContexts> cont_val);

  void DeactivateAllAses(void);
  bool ActivateConfiguredAses(types::LeAudioContextType context_type);
  bool ActivateConfiguredAses(
      types::LeAudioContextType context_type,
      const types::BidirectionalPair<types::AudioContexts>&
          metadata_context_types,
      types::BidirectionalPair<std::vector<uint8_t>> ccid_lists);
  void SetMetadataToAse(
      struct types::ase* ase,
      const types::BidirectionalPair<types::AudioContexts>&
          metadata_context_types,
      types::BidirectionalPair<std::vector<uint8_t>> ccid_lists);

  void PrintDebugState(void);
  void DumpPacsDebugState(std::stringstream& stream);
@@ -329,7 +338,10 @@ class LeAudioDeviceGroup {
  int Size(void) const;
  int NumOfConnected(types::LeAudioContextType context_type =
                         types::LeAudioContextType::RFU) const;
  bool Activate(types::LeAudioContextType context_type);
  bool Activate(types::LeAudioContextType context_type,
                const types::BidirectionalPair<types::AudioContexts>&
                    metadata_context_types,
                types::BidirectionalPair<std::vector<uint8_t>> ccid_lists);
  void Deactivate(void);
  types::CigState GetCigState(void) const;
  void SetCigState(le_audio::types::CigState state);
+2 −1
Original line number Diff line number Diff line
@@ -1590,7 +1590,8 @@ TEST_F(LeAudioAseConfigurationTest, test_reactivation_conversational) {
   * the same CIS ID can be used. This would lead to only activating group
   * without reconfiguring CIG.
   */
  group_->Activate(LeAudioContextType::CONVERSATIONAL);
  group_->Activate(LeAudioContextType::CONVERSATIONAL, audio_contexts,
                   ccid_lists);

  TestActiveAses();

+98 −0
Original line number Diff line number Diff line
@@ -5211,6 +5211,104 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {
  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
}

TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) {
  uint8_t group_size = 2;
  int group_id = 2;

  /* Scenario
   * 1. Earbuds streaming MEDIA
   * 2. Reconfigure to VOIP
   * 3. Check if Metadata in Enable command are set to CONVERSATIONAL
   */

  // Report working CSIS
  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
      .WillByDefault(Return(true));

  // First earbud
  const RawAddress test_address0 = GetTestAddress(0);
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
      .Times(1);
  ConnectCsisDevice(test_address0, 1 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontLeft,
                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
                    group_id, 1 /* rank*/);

  // Second earbud
  const RawAddress test_address1 = GetTestAddress(1);
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true))
      .Times(1);
  ConnectCsisDevice(test_address1, 2 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontRight,
                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                    group_id, 2 /* rank*/, true /*connect_through_csis*/);

  constexpr int gmcs_ccid = 1;
  constexpr int gtbs_ccid = 2;

  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
      .WillByDefault(Invoke([&](int group_id) { return 2; }));

  // Start streaming MEDIA
  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
  LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
  LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();

  // Update metadata on local audio sink
  UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC);

  types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                          .source = {}};
  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, ccids)).Times(1);
  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);

  // Verify Data transfer on two peer sinks
  uint8_t cis_count_out = 2;
  uint8_t cis_count_in = 0;
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);

  // Conversational is a bidirectional scenario so expect GTBS CCID
  // in the metadata for both directions. Can be called twice when one
  // direction resume after the other and metadata is updated.
  ccids = {.sink = {gtbs_ccid}, .source = {gtbs_ccid}};
  types::BidirectionalPair<types::AudioContexts> conversiational_contexts = {
      .sink = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL),
      .source =
          types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)};

  EXPECT_CALL(
      mock_state_machine_,
      ConfigureStream(_, types::LeAudioContextType::CONVERSATIONAL, _, _))
      .Times(AtLeast(1));

  // Update metadata and resume
  UpdateLocalSourceMetadata(AUDIO_USAGE_VOICE_COMMUNICATION,
                            AUDIO_CONTENT_TYPE_SPEECH, true);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);

  EXPECT_CALL(mock_state_machine_,
              StartStream(_, types::LeAudioContextType::CONVERSATIONAL,
                          conversiational_contexts, ccids))
      .Times(AtLeast(1));

  LocalAudioSourceResume(true);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_state_machine_);
}

TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
  uint8_t group_size = 2;
  int group_id = 2;
+3 −1
Original line number Diff line number Diff line
@@ -176,8 +176,10 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    switch (group->GetState()) {
      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
        if (group->IsConfiguredForContext(context_type)) {
          if (group->Activate(context_type)) {
          if (group->Activate(context_type, metadata_context_types,
                              ccid_lists)) {
            SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

            if (CigCreate(group)) {
              return true;
            }
Loading