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

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

Merge changes Id755e424,I8d1c8a89,Ib35accf3,I4bebe861,I83d0f40f into main

* changes:
  LeAudio: Add LIVE config. for one mic banded headphones
  LeAudio: Fix updating current scenario context
  LeAudio: Align context mixing policy
  LeAudio: Fix checking for valid configs
  LeAudio: Minor configuration update cleanup and unit test
parents 8c2a38c6 dd94b3fb
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -280,6 +280,12 @@
                "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
                "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
+134 −135
Original line number Diff line number Diff line
@@ -93,10 +93,13 @@ using le_audio::types::AudioStreamDataPathState;
using le_audio::types::BidirectionalPair;
using le_audio::types::hdl_pair;
using le_audio::types::kDefaultScanDurationS;
using le_audio::types::kLeAudioContextAllBidir;
using le_audio::types::kLeAudioContextAllRemoteSinkOnly;
using le_audio::types::kLeAudioContextAllRemoteSource;
using le_audio::types::kLeAudioContextAllTypesArray;
using le_audio::types::LeAudioContextType;
using le_audio::utils::GetAllowedAudioContextsFromSinkMetadata;
using le_audio::utils::GetAllowedAudioContextsFromSourceMetadata;
using le_audio::utils::IsContextForAudioSource;

using le_audio::client_parser::ascs::
    kCtpResponseCodeInvalidConfigurationParameterValue;
@@ -534,22 +537,38 @@ class LeAudioClientImpl : public LeAudioClient {
    } while (leAudioDevice);
  }

  void UpdateContextAndLocations(LeAudioDeviceGroup* group,
                                 LeAudioDevice* leAudioDevice) {
    if (leAudioDevice->GetConnectionState() != DeviceConnectState::CONNECTED) {
      LOG_DEBUG("%s not yet connected ",
                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      return;
  void UpdateLocationsAndContextsAvailability(LeAudioDeviceGroup* group,
                                              AudioContexts contexts) {
    bool group_conf_changed = group->ReloadAudioLocations();
    group_conf_changed |= group->ReloadAudioDirections();
    group_conf_changed |= group->UpdateAudioSetConfigurationCache(contexts);
    if (group_conf_changed) {
      callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                              group->snk_audio_locations_.to_ulong(),
                              group->src_audio_locations_.to_ulong(),
                              group->GetAvailableContexts().value());
    }
  }

    /* Make sure location and direction are updated for the group. */
    auto location_update = group->ReloadAudioLocations();
    group->ReloadAudioDirections();
  void UpdateLocationsAndContextsAvailability(int group_id) {
    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
    if (group) {
      UpdateLocationsAndContextsAvailability(group,
                                             group->GetAvailableContexts());
    }
  }

    auto contexts_updated = group->UpdateAudioContextTypeAvailability(
        leAudioDevice->GetAvailableContexts());
  void UpdateLocationsAndContextsAvailability(int group_id,
                                              AudioContexts contexts) {
    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
    if (group) {
      UpdateLocationsAndContextsAvailability(group, contexts);
    }
  }

    if (contexts_updated || location_update) {
  void UpdateContexts(LeAudioDeviceGroup* group, AudioContexts contexts) {
    bool group_conf_changed = group->UpdateAudioSetConfigurationCache(contexts);
    if (group_conf_changed) {
      callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                              group->snk_audio_locations_.to_ulong(),
                              group->src_audio_locations_.to_ulong(),
@@ -722,25 +741,16 @@ class LeAudioClientImpl : public LeAudioClient {
    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID)
      AseInitialStateReadRequest(leAudioDevice);

    /* Group may be destroyed once moved its last node to new group */
    if (aseGroups_.FindById(old_group_id) != nullptr) {
      /* Removing node from group may touch its context integrity */
      auto contexts_updated = old_group->UpdateAudioContextTypeAvailability(
          old_group->GetAvailableContexts());
    /* Group may be destroyed once moved its last node to new group, so don't
     * use `old_group` pointer anymore.
     * Removing node from group requires updating group context availability */
    UpdateLocationsAndContextsAvailability(old_group_id);

      bool group_conf_changed = old_group->ReloadAudioLocations();
      group_conf_changed |= old_group->ReloadAudioDirections();
      group_conf_changed |= contexts_updated;

      if (group_conf_changed) {
        callbacks_->OnAudioConf(old_group->audio_directions_, old_group_id,
                                old_group->snk_audio_locations_.to_ulong(),
                                old_group->src_audio_locations_.to_ulong(),
                                old_group->GetAvailableContexts().value());
      }
    if (leAudioDevice->GetConnectionState() == DeviceConnectState::CONNECTED) {
      UpdateLocationsAndContextsAvailability(
          new_group, new_group->GetAvailableContexts() |
                         leAudioDevice->GetAvailableContexts());
    }

    UpdateContextAndLocations(new_group, leAudioDevice);
  }

  void GroupAddNode(const int group_id, const RawAddress& address) override {
@@ -791,19 +801,9 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    /* Removing node from group touch its context integrity */
    bool contexts_updated = group->UpdateAudioContextTypeAvailability(
    /* Removing node from group requires updating group context availability */
    UpdateLocationsAndContextsAvailability(group,
                                           group->GetAvailableContexts());

    bool group_conf_changed = group->ReloadAudioLocations();
    group_conf_changed |= group->ReloadAudioDirections();
    group_conf_changed |= contexts_updated;

    if (group_conf_changed)
      callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                              group->snk_audio_locations_.to_ulong(),
                              group->src_audio_locations_.to_ulong(),
                              group->GetAvailableContexts().value());
  }

  void GroupRemoveNode(const int group_id, const RawAddress& address) override {
@@ -942,13 +942,22 @@ class LeAudioClientImpl : public LeAudioClient {
          bluetooth::common::time_get_os_boottime_us();
    }

    /* Make sure we do not take the local sink metadata when only the local
     * source scenario is about to be started (e.g. MEDIA).
     */
    auto direction_aligned_metadata_context_types = metadata_context_types;
    if (!kLeAudioContextAllBidir.test(final_context_type)) {
      direction_aligned_metadata_context_types.source.clear();
    }

    BidirectionalPair<std::vector<uint8_t>> ccids = {
        .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            metadata_context_types.sink),
        .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            metadata_context_types.source)};
    bool result = groupStateMachine_->StartStream(
        group, final_context_type, metadata_context_types, ccids);
        group, final_context_type, direction_aligned_metadata_context_types,
        ccids);

    return result;
  }
@@ -1218,13 +1227,12 @@ class LeAudioClientImpl : public LeAudioClient {
     * If most recent scenario is not supported, try to find first supported.
     */
    LeAudioContextType default_context_type = configuration_context_type_;
    if (!group->IsContextSupported(default_context_type)) {
      if (group->IsContextSupported(LeAudioContextType::MEDIA)) {
    if (!group->IsAudioSetConfigurationAvailable(default_context_type)) {
      if (group->IsAudioSetConfigurationAvailable(LeAudioContextType::MEDIA)) {
        default_context_type = LeAudioContextType::MEDIA;
      } else {
        for (LeAudioContextType context_type :
             le_audio::types::kLeAudioContextAllTypesArray) {
          if (group->IsContextSupported(context_type)) {
        for (LeAudioContextType context_type : kLeAudioContextAllTypesArray) {
          if (group->IsAudioSetConfigurationAvailable(context_type)) {
            default_context_type = context_type;
            break;
          }
@@ -1699,12 +1707,8 @@ class LeAudioClientImpl : public LeAudioClient {
       * Read of available context during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
      if (group && group->UpdateAudioContextTypeAvailability(
                       leAudioDevice->GetAvailableContexts())) {
        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                group->snk_audio_locations_.to_ulong(),
                                group->src_audio_locations_.to_ulong(),
                                group->GetAvailableContexts().value());
      if (group) {
        UpdateContexts(group, group->GetAvailableContexts());
      }
      if (notify) {
        btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
@@ -1733,14 +1737,9 @@ class LeAudioClientImpl : public LeAudioClient {
       * Read of available context during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
      if (group && group->UpdateAudioContextTypeAvailability(
                       leAudioDevice->GetAvailableContexts())) {
        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                group->snk_audio_locations_.to_ulong(),
                                group->src_audio_locations_.to_ulong(),
                                group->GetAvailableContexts().value());
      if (group) {
        UpdateContexts(group, group->GetAvailableContexts());
      }

      if (notify) {
        btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
      }
@@ -1766,7 +1765,6 @@ class LeAudioClientImpl : public LeAudioClient {
          le_audio::types::kLeAudioDirectionSink;
      leAudioDevice->snk_audio_locations_ = snk_audio_locations;

      LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);
      callbacks_->OnSinkAudioLocationAvailable(leAudioDevice->address_,
                                               snk_audio_locations.to_ulong());

@@ -1780,17 +1778,7 @@ class LeAudioClientImpl : public LeAudioClient {
      /* Read of source audio locations during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
      if (!group) return;

      bool group_conf_changed = group->ReloadAudioLocations();
      group_conf_changed |= group->ReloadAudioDirections();

      if (group_conf_changed) {
        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                group->snk_audio_locations_.to_ulong(),
                                group->src_audio_locations_.to_ulong(),
                                group->GetAvailableContexts().value());
      }
      UpdateLocationsAndContextsAvailability(leAudioDevice->group_id_);
    } else if (hdl == leAudioDevice->src_audio_locations_hdls_.val_hdl) {
      AudioLocations src_audio_locations;

@@ -1810,8 +1798,6 @@ class LeAudioClientImpl : public LeAudioClient {
          le_audio::types::kLeAudioDirectionSource;
      leAudioDevice->src_audio_locations_ = src_audio_locations;

      LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);

      if (notify) {
        btif_storage_set_leaudio_audio_location(
            leAudioDevice->address_,
@@ -1822,28 +1808,18 @@ class LeAudioClientImpl : public LeAudioClient {
      /* Read of source audio locations during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
      if (!group) return;

      bool group_conf_changed = group->ReloadAudioLocations();
      group_conf_changed |= group->ReloadAudioDirections();

      if (group_conf_changed) {
        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                group->snk_audio_locations_.to_ulong(),
                                group->src_audio_locations_.to_ulong(),
                                group->GetAvailableContexts().value());
      }
      UpdateLocationsAndContextsAvailability(leAudioDevice->group_id_);
    } else if (hdl == leAudioDevice->audio_avail_hdls_.val_hdl) {
      le_audio::client_parser::pacs::acs_available_audio_contexts
          avail_audio_contexts;
      le_audio::client_parser::pacs::ParseAvailableAudioContexts(
          avail_audio_contexts, len, value);

      auto updated_avail_contexts = leAudioDevice->SetAvailableContexts(
      auto updated_context_bits = leAudioDevice->SetAvailableContexts(
          avail_audio_contexts.snk_avail_cont,
          avail_audio_contexts.src_avail_cont);

      if (updated_avail_contexts.any()) {
      if (updated_context_bits.any()) {
        /* Update scenario map considering changed available context types */
        LeAudioDeviceGroup* group =
            aseGroups_.FindById(leAudioDevice->group_id_);
@@ -1858,18 +1834,12 @@ class LeAudioClientImpl : public LeAudioClient {
          if (group->IsInTransition() ||
              (group->GetState() ==
               AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
            group->SetPendingAvailableContextsChange(updated_avail_contexts);
            group->SetPendingAvailableContextsChange(updated_context_bits);
            return;
          }

          auto contexts_updated =
              group->UpdateAudioContextTypeAvailability(updated_avail_contexts);
          if (contexts_updated) {
            callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                    group->snk_audio_locations_.to_ulong(),
                                    group->src_audio_locations_.to_ulong(),
                                    group->GetAvailableContexts().value());
          }
          /* Available contexts change requires audio set config invalidation */
          UpdateContexts(group, updated_context_bits);
        }
      }
    } else if (hdl == leAudioDevice->audio_supp_cont_hdls_.val_hdl) {
@@ -2993,7 +2963,8 @@ class LeAudioClientImpl : public LeAudioClient {
        get_num_of_devices_in_configuration(stream_conf->conf);

    if (num_of_devices < group->NumOfConnected() &&
        !group->IsConfigurationSupported(leAudioDevice, stream_conf->conf)) {
        !group->IsAudioSetConfigurationSupported(leAudioDevice,
                                                 stream_conf->conf)) {
      /* Reconfigure if newly connected member device cannot support current
       * codec configuration */
      group->SetPendingConfiguration();
@@ -3060,7 +3031,11 @@ class LeAudioClientImpl : public LeAudioClient {

    if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) {
      LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);
      UpdateContextAndLocations(group, leAudioDevice);
      if (group) {
        UpdateLocationsAndContextsAvailability(
            group, group->GetAvailableContexts() |
                       leAudioDevice->GetAvailableContexts());
      }
      AttachToStreamingGroupIfNeeded(leAudioDevice);
    }
  }
@@ -3805,6 +3780,13 @@ class LeAudioClientImpl : public LeAudioClient {
        sink_cfg_available, source_cfg_available);

    if (!reconfiguration_needed) {
      // Assign the new configuration context as it reprents the current
      // use case even when it eventually ends up being the exact same
      // codec and qos configuration.
      if (configuration_context_type_ != context_type) {
        configuration_context_type_ = context_type;
        group->SetConfigurationContextType(context_type);
      }
      return AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED;
    }

@@ -4491,12 +4473,16 @@ class LeAudioClientImpl : public LeAudioClient {
    metadata_context_types_.sink = GetAllowedAudioContextsFromSourceMetadata(
        source_metadata, current_available_contexts);

    /* Make sure we have CONVERSATIONAL when in a call */
    /* Make sure we have CONVERSATIONAL when in a call and it is not mixed
     * with any other bidirectional context
     */
    if (in_call_) {
      LOG_DEBUG(" In Call preference used.");
      metadata_context_types_.sink |=
      metadata_context_types_.sink =
          (metadata_context_types_.sink & ~kLeAudioContextAllBidir) |
          AudioContexts(LeAudioContextType::CONVERSATIONAL);
      metadata_context_types_.source |=
      metadata_context_types_.source =
          (metadata_context_types_.source & ~kLeAudioContextAllBidir) |
          AudioContexts(LeAudioContextType::CONVERSATIONAL);
    }

@@ -4536,18 +4522,25 @@ class LeAudioClientImpl : public LeAudioClient {
    /* Start with only this direction context metadata */
    auto configuration_context_candidates = metadata_context_types_.sink;

    auto is_streaming_other_direction =
        (audio_receiver_state_ == AudioState::STARTED) ||
        (audio_receiver_state_ == AudioState::READY_TO_START);

    /* Mixed contexts in the voiceback channel scenarios can confuse the remote
     * on how to configure each channel. We should align both direction
     * metadata.
     */
    auto bidir_contexts = LeAudioContextType::GAME | LeAudioContextType::LIVE |
                          LeAudioContextType::CONVERSATIONAL |
                          LeAudioContextType::VOICEASSISTANTS;
    if (metadata_context_types_.sink.test_any(bidir_contexts)) {
    if (metadata_context_types_.sink.test_any(kLeAudioContextAllBidir)) {
      if (osi_property_get_bool(kAllowMultipleContextsInMetadata, true)) {
        LOG_DEBUG("Aligning remote source metadata to add the sink context");
        if (!is_streaming_other_direction) {
          // Do not take the obsolete metadata
          metadata_context_types_.source.clear();
        }
        metadata_context_types_.source =
            metadata_context_types_.source | metadata_context_types_.sink;
            (metadata_context_types_.source & ~kLeAudioContextAllBidir) |
            (metadata_context_types_.sink & ~kLeAudioContextAllRemoteSinkOnly);
        metadata_context_types_.sink |= metadata_context_types_.source;
      } else {
        LOG_DEBUG("Replacing remote source metadata to match the sink context");
        metadata_context_types_.source = metadata_context_types_.sink;
@@ -4565,20 +4558,21 @@ class LeAudioClientImpl : public LeAudioClient {
         group->IsPendingConfiguration() &&
         IsDirectionAvailableForCurrentConfiguration(
             group, le_audio::types::kLeAudioDirectionSource));
    if (is_releasing_for_reconfiguration ||
        (audio_receiver_state_ == AudioState::STARTED) ||
        (audio_receiver_state_ == AudioState::READY_TO_START)) {
    if (is_releasing_for_reconfiguration || is_streaming_other_direction) {
      LOG_DEBUG("Other direction is streaming. Taking its contexts %s",
                ToString(metadata_context_types_.source).c_str());
      // If current direction has no valid context or we are in the
      // bidirectional scenario, take the other direction context
      if ((metadata_context_types_.sink.none() &&
           metadata_context_types_.source.any()) ||
          metadata_context_types_.source.test_any(bidir_contexts)) {
          metadata_context_types_.source.test_any(kLeAudioContextAllBidir)) {
        if (osi_property_get_bool(kAllowMultipleContextsInMetadata, true)) {
          LOG_DEBUG("Aligning remote sink metadata to add the source context");
          /* Turn off bidirectional contexts on sink to avoid mixing with the
           * bidirectionals for source */
          metadata_context_types_.sink =
              metadata_context_types_.sink | metadata_context_types_.source;
              (metadata_context_types_.sink & ~kLeAudioContextAllBidir) |
              metadata_context_types_.source;
        } else {
          LOG_DEBUG(
              "Replacing remote sink metadata to match the source context");
@@ -4741,12 +4735,16 @@ class LeAudioClientImpl : public LeAudioClient {
    metadata_context_types_.source = GetAllowedAudioContextsFromSinkMetadata(
        sink_metadata, current_available_contexts);

    /* Make sure we have CONVERSATIONAL when in a call */
    /* Make sure we have CONVERSATIONAL when in a call and it is not mixed
     * with any other bidirectional context
     */
    if (in_call_) {
      LOG_INFO(" In Call preference used.");
      metadata_context_types_.sink |=
      metadata_context_types_.sink =
          (metadata_context_types_.sink & ~kLeAudioContextAllBidir) |
          AudioContexts(LeAudioContextType::CONVERSATIONAL);
      metadata_context_types_.source |=
      metadata_context_types_.source =
          (metadata_context_types_.source & ~kLeAudioContextAllBidir) |
          AudioContexts(LeAudioContextType::CONVERSATIONAL);
    }

@@ -4789,18 +4787,25 @@ class LeAudioClientImpl : public LeAudioClient {
    /* Start with only this direction context metadata */
    auto configuration_context_candidates = metadata_context_types_.source;

    auto is_streaming_other_direction =
        (audio_sender_state_ == AudioState::STARTED) ||
        (audio_sender_state_ == AudioState::READY_TO_START);

    /* Mixed contexts in the voiceback channel scenarios can confuse the remote
     * on how to configure each channel. We should align both direction
     * metadata.
     */
    auto bidir_contexts = LeAudioContextType::GAME | LeAudioContextType::LIVE |
                          LeAudioContextType::CONVERSATIONAL |
                          LeAudioContextType::VOICEASSISTANTS;
    if (metadata_context_types_.source.test_any(bidir_contexts)) {
    if (metadata_context_types_.source.test_any(kLeAudioContextAllBidir)) {
      if (osi_property_get_bool(kAllowMultipleContextsInMetadata, true)) {
        LOG_DEBUG("Aligning remote sink metadata to add the source context");
        if (!is_streaming_other_direction) {
          // Do not take the obsolete metadata
          metadata_context_types_.sink.clear();
        }
        metadata_context_types_.sink =
            metadata_context_types_.sink | metadata_context_types_.source;
            (metadata_context_types_.sink & ~kLeAudioContextAllBidir) |
            metadata_context_types_.source;
        metadata_context_types_.source = metadata_context_types_.sink;
      } else {
        LOG_DEBUG("Replacing remote sink metadata to match the source context");
        metadata_context_types_.sink = metadata_context_types_.source;
@@ -4849,7 +4854,7 @@ class LeAudioClientImpl : public LeAudioClient {

    /* Do nothing if audio source is not valid for the new configuration */
    const auto is_audio_source_context =
        IsContextForAudioSource(new_configuration_context);
        kLeAudioContextAllRemoteSource.test(new_configuration_context);
    if (!is_audio_source_context) {
      LOG_WARN(
          "No valid remote audio source configuration context in %s, staying "
@@ -5121,21 +5126,15 @@ class LeAudioClientImpl : public LeAudioClient {
  }

  void HandlePendingAvailableContextsChange(LeAudioDeviceGroup* group) {
    if (!group) return;
    if (!group) {
      LOG_ERROR("Invalid group: %d", active_group_id_);
      return;
    }

    /* Update group configuration with pending available context change */
    auto contexts = group->GetPendingAvailableContextsChange();
    if (contexts.any()) {
      auto success = group->UpdateAudioContextTypeAvailability(contexts);
      if (success) {
        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                group->snk_audio_locations_.to_ulong(),
                                group->src_audio_locations_.to_ulong(),
                                group->GetAvailableContexts().value());
      }
    UpdateContexts(group, group->GetPendingAvailableContextsChange());
    group->ClearPendingAvailableContextsChange();
  }
  }

  void HandlePendingDeviceRemove(LeAudioDeviceGroup* group) {
    for (auto device = group->GetFirstDevice(); device != nullptr;
Loading