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

Commit ca993c5a authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Decouple configuration mechanism from the available contexts

Current audio set configuration should be coupled with the current
audio use case, therefor coupled with the current call state, sink and
source metadata if any of these are activated. This will allow us to
properly configure for scenarios which are not supported on the remote
device and use the UNSPECIFIED context.

The existing configuration logic is coupled with the context
availability at many levels and had to be reworked.
As a result, the sink/source audio contexts serving as a base
for the configurations are now initially not filtered by the
context availability, but stored as local contexts, derived
from the local audio track metadata. Then, when the remote device
is about to be configured, we derive the remote side contexts
from the local contexts and we apply (among others) the remote device
context availability policy.

Additionally, the configuration cache map is now recomputed on
demand for the particular configuration context, when it is needed,
while previously we recomputed the entire array of configurations
every time the audio set conditions have changed, even if some of
them are rarelly or never used between these changes (eg. when the
device was disconnected or PACs have changed).

Bug: 285647765
Test: atest bluetooth_le_audio_test bluetooth_le_audio_client_test
Change-Id: Id2821ef1681e99f3c5afe109473c43abf91de670
parent 55c0719c
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ using le_audio::types::CodecLocation;
using le_audio::types::kLeAudioCodingFormatLC3;
using le_audio::types::LeAudioContextType;
using le_audio::types::LeAudioLtvMap;
using le_audio::utils::GetAllowedAudioContextsFromSourceMetadata;
using le_audio::utils::GetAudioContextsFromSourceMetadata;

namespace {
class LeAudioBroadcasterImpl;
@@ -979,9 +979,7 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
      if (!instance) return;

      /* TODO: Should we take supported contexts from ASCS? */
      auto supported_context_types = le_audio::types::kLeAudioContextAllTypes;
      auto contexts = GetAllowedAudioContextsFromSourceMetadata(
          source_metadata, supported_context_types);
      auto contexts = GetAudioContextsFromSourceMetadata(source_metadata);
      if (contexts.any()) {
        /* NOTICE: We probably don't want to change the stream configuration
         * on each metadata change, so just update the context type metadata.
+409 −507

File changed.

Preview size limit exceeded, changes collapsed.

+160 −125
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <base/strings/string_number_conversions.h>

#include <map>
#include <tuple>

#include "audio_hal_client/audio_hal_client.h"
#include "bta_csis_api.h"
@@ -143,14 +144,15 @@ int LeAudioDeviceGroup::NumOfConnected(
  return std::count_if(
      leAudioDevices_.begin(), leAudioDevices_.end(),
      [type_set, check_context_type](auto& iter) {
        if (iter.expired()) return false;
        if (iter.lock()->conn_id_ == GATT_INVALID_CONN_ID) return false;
        if (iter.lock()->GetConnectionState() != DeviceConnectState::CONNECTED)
        auto dev = iter.lock();
        if (dev) {
          if (dev->conn_id_ == GATT_INVALID_CONN_ID) return false;
          if (dev->GetConnectionState() != DeviceConnectState::CONNECTED)
            return false;

          if (!check_context_type) return true;

        return iter.lock()->GetAvailableContexts().test_any(type_set);
          return dev->GetSupportedContexts().test_any(type_set);
        }
        return false;
      });
}

@@ -795,94 +797,59 @@ uint16_t LeAudioDeviceGroup::GetRemoteDelay(uint8_t direction) const {
  return remote_delay_ms;
}

bool LeAudioDeviceGroup::UpdateAudioSetConfigurationCache(void) {
bool LeAudioDeviceGroup::UpdateAudioContextAvailability(void) {
  LOG_DEBUG(
      " group id: %d, available contexts sink: %s, available contexts source: "
      "%s",
      group_id_, group_available_contexts_.sink.to_string().c_str(),
      group_available_contexts_.source.to_string().c_str());
  return UpdateAudioSetConfigurationCache(
      get_bidirectional(group_available_contexts_));
  auto old_contexts = GetAvailableContexts();
  SetAvailableContexts(GetLatestAvailableContexts());
  return old_contexts != GetAvailableContexts();
}

/* Returns true if support for any type in the whole group has changed,
 * otherwise false. */
bool LeAudioDeviceGroup::UpdateAudioSetConfigurationCache(
    AudioContexts update_contexts) {
  auto new_contexts = AudioContexts();
  bool active_contexts_has_been_modified = false;

  if (update_contexts.none()) {
    LOG_DEBUG("No context updated");
    LeAudioContextType ctx_type) {
  const le_audio::set_configurations::AudioSetConfiguration* new_conf =
      FindFirstSupportedConfiguration(ctx_type);
  auto update_config = true;

  if (context_to_configuration_cache_map.count(ctx_type) != 0) {
    auto [is_valid, existing_conf] =
        context_to_configuration_cache_map.at(ctx_type);
    update_config = (new_conf != existing_conf);
    /* Just mark it as still valid */
    if (!update_config && !is_valid) {
      context_to_configuration_cache_map.at(ctx_type).first = true;
      return false;
    }

  LOG_DEBUG("Updated context: %s", update_contexts.to_string().c_str());

  for (LeAudioContextType ctx_type : types::kLeAudioContextAllTypesArray) {
    LOG_DEBUG("Checking context: %s", ToHexString(ctx_type).c_str());

    if (!update_contexts.test(ctx_type)) {
      LOG_DEBUG("%s config availability not updated for ",
                ToHexString(ctx_type).c_str());
      /* Fill context bitset for possible returned value if updated */
      if (available_context_to_configuration_map.count(ctx_type) > 0)
        new_contexts.set(ctx_type);

      continue;
  }

    auto new_conf = FindFirstSupportedConfiguration(ctx_type);

    bool ctx_previously_not_supported =
        (available_context_to_configuration_map.count(ctx_type) == 0 ||
         available_context_to_configuration_map[ctx_type] == nullptr);
    /* Check if support for context type has changed */
    if (ctx_previously_not_supported) {
      /* Current configuration for context type is empty */
      if (new_conf == nullptr) {
        /* Configuration remains empty */
        continue;
      } else {
        /* Configuration changes from empty to some */
        new_contexts.set(ctx_type);
        active_contexts_has_been_modified = true;
  if (update_config) {
    context_to_configuration_cache_map[ctx_type] = std::pair(true, new_conf);
    LOG_DEBUG("config: %s -> %s", ToHexString(ctx_type).c_str(),
              (new_conf ? new_conf->name.c_str() : "(none)"));
  }
    } else {
      /* Current configuration for context type is not empty */
      if (new_conf == nullptr) {
        /* Configuration changed to empty */
        new_contexts.unset(ctx_type);
        active_contexts_has_been_modified = true;
      } else if (new_conf != available_context_to_configuration_map[ctx_type]) {
        /* Configuration changed to any other */
        new_contexts.set(ctx_type);
        active_contexts_has_been_modified = true;
      } else {
        /* Configuration is the same */
        new_contexts.set(ctx_type);
        continue;
  return update_config;
}
    }

    LOG_INFO(
        "%s(%s), %s -> %s", types::contextTypeToStr(ctx_type).c_str(),
        ToHexString(ctx_type).c_str(),
        (ctx_previously_not_supported
             ? "empty"
             : available_context_to_configuration_map[ctx_type]->name.c_str()),
        (new_conf != nullptr ? new_conf->name.c_str() : "empty"));

    available_context_to_configuration_map[ctx_type] = new_conf;
void LeAudioDeviceGroup::InvalidateCachedConfigurations(void) {
  context_to_configuration_cache_map.clear();
}

  /* Some contexts have changed, return new available context bitset */
  if (active_contexts_has_been_modified) {
    // TODO: Set the correct directional context availability
    SetAvailableContexts({new_contexts, new_contexts});
types::BidirectionalPair<types::AudioContexts>
LeAudioDeviceGroup::GetLatestAvailableContexts() const {
  types::BidirectionalPair<types::AudioContexts> contexts;
  for (const auto& device : leAudioDevices_) {
    auto shared_ptr = device.lock();
    if (shared_ptr) {
      contexts.sink |=
          shared_ptr->GetAvailableContexts(types::kLeAudioDirectionSink);
      contexts.source |=
          shared_ptr->GetAvailableContexts(types::kLeAudioDirectionSource);
    }

  return active_contexts_has_been_modified;
  }
  return contexts;
}

bool LeAudioDeviceGroup::ReloadAudioLocations(void) {
@@ -1329,11 +1296,19 @@ bool LeAudioDeviceGroup::IsAudioSetConfigurationSupported(
    const set_configurations::AudioSetConfiguration* audio_set_conf,
    types::LeAudioContextType context_type,
    types::LeAudioConfigurationStrategy required_snk_strategy) const {
  if (!set_configurations::check_if_may_cover_scenario(
          audio_set_conf, NumOfConnected(context_type))) {
  /* When at least one device supports the configuration context, configure
   * for these devices only. Otherwise configure for all devices - we will
   * not put this context into the metadata if not supported.
   */
  auto num_of_connected = NumOfConnected(context_type);
  if (num_of_connected == 0) {
    num_of_connected = NumOfConnected();
  }
  if (!set_configurations::check_if_may_cover_scenario(audio_set_conf,
                                                       num_of_connected)) {
    LOG_DEBUG(" cannot cover scenario  %s, num. of connected: %d",
              bluetooth::common::ToString(context_type).c_str(),
              +NumOfConnected(context_type));
              +num_of_connected);
    return false;
  }

@@ -1369,9 +1344,9 @@ bool LeAudioDeviceGroup::IsAudioSetConfigurationSupported(
      return false;
    }

    for (auto* device = GetFirstDeviceWithAvailableContext(context_type);
    for (auto* device = GetFirstDevice();
         device != nullptr && required_device_cnt > 0;
         device = GetNextDeviceWithAvailableContext(device, context_type)) {
         device = GetNextDevice(device)) {
      /* Skip if device has ASE configured in this direction already */

      if (device->ases_.empty()) continue;
@@ -1685,9 +1660,18 @@ bool LeAudioDeviceGroup::ConfigureAses(
    types::LeAudioContextType context_type,
    const types::BidirectionalPair<AudioContexts>& metadata_context_types,
    const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) {
  if (!set_configurations::check_if_may_cover_scenario(
          audio_set_conf, NumOfConnected(context_type)))
  /* When at least one device supports the configuration context, configure
   * for these devices only. Otherwise configure for all devices - we will
   * not put this context into the metadata if not supported.
   */
  auto num_of_connected = NumOfConnected(context_type);
  if (num_of_connected == 0) {
    num_of_connected = NumOfConnected();
  }
  if (!set_configurations::check_if_may_cover_scenario(audio_set_conf,
                                                       num_of_connected)) {
    return false;
  }

  bool reuse_cis_id =
      GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
@@ -1723,27 +1707,42 @@ bool LeAudioDeviceGroup::ConfigureAses(
        required_device_cnt, ent.ase_cnt, max_required_ase_per_dev,
        (int)strategy);

    for (auto* device = GetFirstDeviceWithAvailableContext(context_type);
         device != nullptr && required_device_cnt > 0;
         device = GetNextDeviceWithAvailableContext(device, context_type)) {
    auto configuration_closure = [&](LeAudioDevice* dev) -> void {
      /* For the moment, we configure only connected devices and when it is
       * ready to stream i.e. All ASEs are discovered and device is reported as
       * ready to stream i.e. All ASEs are discovered and dev is reported as
       * connected
       */
      if (device->GetConnectionState() != DeviceConnectState::CONNECTED) {
      if (dev->GetConnectionState() != DeviceConnectState::CONNECTED) {
        LOG_WARN(
            "Device %s, in the state %s",
            ADDRESS_TO_LOGGABLE_CSTR(device->address_),
            bluetooth::common::ToString(device->GetConnectionState()).c_str());
        continue;
            ADDRESS_TO_LOGGABLE_CSTR(dev->address_),
            bluetooth::common::ToString(dev->GetConnectionState()).c_str());
        return;
      }

      if (!device->ConfigureAses(
              ent, context_type, &active_ase_num, group_audio_locations_memo,
      if (!dev->ConfigureAses(ent, context_type, &active_ase_num,
                              group_audio_locations_memo,
                              metadata_context_types, ccid_lists, reuse_cis_id))
        continue;
        return;

      required_device_cnt--;
    };

    // First use the devices claiming proper support
    for (auto* device = GetFirstDeviceWithAvailableContext(context_type);
         device != nullptr && required_device_cnt > 0;
         device = GetNextDeviceWithAvailableContext(device, context_type)) {
      configuration_closure(device);
    }
    // In case some devices do not support this scenario - us them anyway if
    // they are required for the scenario - we will not put this context into
    // their metadata anyway
    if (required_device_cnt > 0) {
      for (auto* device = GetFirstDevice();
           device != nullptr && required_device_cnt > 0;
           device = GetNextDevice(device)) {
        configuration_closure(device);
      }
    }

    if (required_device_cnt > 0) {
@@ -1762,30 +1761,49 @@ bool LeAudioDeviceGroup::ConfigureAses(
  return true;
}

const set_configurations::AudioSetConfiguration*
LeAudioDeviceGroup::GetCachedConfiguration(
    types::LeAudioContextType context_type) const {
  if (context_to_configuration_cache_map.count(context_type) != 0) {
    return context_to_configuration_cache_map.at(context_type).second;
  }
  return nullptr;
}

const set_configurations::AudioSetConfiguration*
LeAudioDeviceGroup::GetActiveConfiguration(void) const {
  if (available_context_to_configuration_map.count(
          configuration_context_type_)) {
    return available_context_to_configuration_map.at(
        configuration_context_type_);
  return GetCachedConfiguration(configuration_context_type_);
}

const set_configurations::AudioSetConfiguration*
LeAudioDeviceGroup::GetConfiguration(types::LeAudioContextType context_type) {
  if (context_type == types::LeAudioContextType::UNINITIALIZED) {
    return nullptr;
  }

std::optional<LeAudioCodecConfiguration>
LeAudioDeviceGroup::GetCodecConfigurationByDirection(
    types::LeAudioContextType group_context_type, uint8_t direction) const {
  if (available_context_to_configuration_map.count(group_context_type) == 0) {
    LOG_DEBUG("Context type %s, not supported",
              bluetooth::common::ToString(group_context_type).c_str());
    return std::nullopt;
  const set_configurations::AudioSetConfiguration* conf = nullptr;
  bool is_valid = false;

  /* Refresh the cache if there is no valid configuration */
  if (context_to_configuration_cache_map.count(context_type) != 0) {
    std::tie(is_valid, conf) =
        context_to_configuration_cache_map.at(context_type);
  }
  if (!is_valid || (conf == nullptr)) {
    UpdateAudioSetConfigurationCache(context_type);
  }

  return GetCachedConfiguration(context_type);
}

std::optional<LeAudioCodecConfiguration>
LeAudioDeviceGroup::GetCachedCodecConfigurationByDirection(
    types::LeAudioContextType context_type, uint8_t direction) const {
  const set_configurations::AudioSetConfiguration* audio_set_conf =
      available_context_to_configuration_map.at(group_context_type);
  LeAudioCodecConfiguration group_config = {0, 0, 0, 0};
      GetCachedConfiguration(context_type);
  if (!audio_set_conf) return std::nullopt;

  LeAudioCodecConfiguration group_config = {0, 0, 0, 0};
  for (const auto& conf : audio_set_conf->confs) {
    if (conf.direction != direction) continue;

@@ -1828,13 +1846,28 @@ LeAudioDeviceGroup::GetCodecConfigurationByDirection(
  return group_config;
}

bool LeAudioDeviceGroup::IsAudioSetConfigurationAvailable(
    types::LeAudioContextType group_context_type) const {
  if (available_context_to_configuration_map.count(group_context_type) != 0) {
    return available_context_to_configuration_map.at(group_context_type) !=
           nullptr;
std::optional<LeAudioCodecConfiguration>
LeAudioDeviceGroup::GetCodecConfigurationByDirection(
    types::LeAudioContextType context_type, uint8_t direction) {
  const set_configurations::AudioSetConfiguration* conf = nullptr;
  bool is_valid = false;

  /* Refresh the cache if there is no valid configuration */
  if (context_to_configuration_cache_map.count(context_type) != 0) {
    std::tie(is_valid, conf) =
        context_to_configuration_cache_map.at(context_type);
  }
  return false;
  if (!is_valid || (conf == nullptr)) {
    UpdateAudioSetConfigurationCache(context_type);
  }

  /* Return the cached value */
  return GetCachedCodecConfigurationByDirection(context_type, direction);
}

bool LeAudioDeviceGroup::IsAudioSetConfigurationAvailable(
    types::LeAudioContextType group_context_type) {
  return GetConfiguration(group_context_type) != nullptr;
}

bool LeAudioDeviceGroup::IsMetadataChanged(
@@ -2187,9 +2220,13 @@ LeAudioDeviceGroup::FindFirstSupportedConfiguration(
            bluetooth::common::ToString(context_type).c_str(),
            +NumOfConnected());

  auto num_of_connected = NumOfConnected(context_type);
  if (num_of_connected == 0) {
    num_of_connected = NumOfConnected();
  }
  /* Filter out device set for all scenarios */
  if (!set_configurations::check_if_may_cover_scenario(confs,
                                                       NumOfConnected())) {
                                                       num_of_connected)) {
    LOG_DEBUG(", group is unable to cover scenario");
    return nullptr;
  }
@@ -2215,9 +2252,7 @@ bool LeAudioDeviceGroup::Configure(
    LeAudioContextType context_type,
    const types::BidirectionalPair<AudioContexts>& metadata_context_types,
    types::BidirectionalPair<std::vector<uint8_t>> ccid_lists) {
  const set_configurations::AudioSetConfiguration* conf =
      available_context_to_configuration_map[context_type];

  auto conf = GetConfiguration(context_type);
  if (!conf) {
    LOG_ERROR(
        ", requested context type: %s , is in mismatch with cached available "
+19 −7
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <memory>
#include <optional>
#include <tuple>
#include <utility>  // for std::pair
#include <vector>

#ifdef __ANDROID__
@@ -373,14 +374,18 @@ class LeAudioDeviceGroup {
  uint8_t GetTargetPhy(uint8_t direction) const;
  bool GetPresentationDelay(uint32_t* delay, uint8_t direction) const;
  uint16_t GetRemoteDelay(uint8_t direction) const;
  bool UpdateAudioSetConfigurationCache(
      types::AudioContexts updated_context_bits);
  bool UpdateAudioSetConfigurationCache(void);
  bool UpdateAudioContextAvailability(void);
  bool UpdateAudioSetConfigurationCache(types::LeAudioContextType ctx_type);
  bool ReloadAudioLocations(void);
  bool ReloadAudioDirections(void);
  const set_configurations::AudioSetConfiguration* GetActiveConfiguration(
      void) const;
  bool IsPendingConfiguration(void) const;
  const set_configurations::AudioSetConfiguration* GetConfiguration(
      types::LeAudioContextType ctx_type);
  const set_configurations::AudioSetConfiguration* GetCachedConfiguration(
      types::LeAudioContextType ctx_type) const;
  void InvalidateCachedConfigurations(void);
  void SetPendingConfiguration(void);
  void ClearPendingConfiguration(void);
  void AddToAllowListNotConnectedGroupMembers(int gatt_if);
@@ -391,9 +396,12 @@ class LeAudioDeviceGroup {
      LeAudioDevice* leAudioDevice,
      const set_configurations::AudioSetConfiguration* audio_set_conf) const;
  std::optional<LeAudioCodecConfiguration> GetCodecConfigurationByDirection(
      types::LeAudioContextType group_context_type, uint8_t direction);
  std::optional<LeAudioCodecConfiguration>
  GetCachedCodecConfigurationByDirection(
      types::LeAudioContextType group_context_type, uint8_t direction) const;
  bool IsAudioSetConfigurationAvailable(
      types::LeAudioContextType group_context_type) const;
      types::LeAudioContextType group_context_type);
  bool IsMetadataChanged(
      const types::BidirectionalPair<types::AudioContexts>& context_types,
      const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) const;
@@ -470,6 +478,9 @@ class LeAudioDeviceGroup {
      int direction = (types::kLeAudioDirectionSink |
                       types::kLeAudioDirectionSource)) const;

  types::BidirectionalPair<types::AudioContexts> GetLatestAvailableContexts(
      void) const;

  bool IsInTransition(void) const;
  bool IsStreaming(void) const;
  bool IsReleasingOrIdle(void) const;
@@ -513,11 +524,12 @@ class LeAudioDeviceGroup {
  types::AudioContexts pending_group_available_contexts_change_;

  /* Possible configuration cache - refreshed on each group context availability
   * change
   * change. Stored as a pair of (is_valid_cache, configuration*). `pair.first`
   * being `false` means that the cached value should be refreshed.
   */
  std::map<types::LeAudioContextType,
           const set_configurations::AudioSetConfiguration*>
      available_context_to_configuration_map;
           std::pair<bool, const set_configurations::AudioSetConfiguration*>>
      context_to_configuration_cache_map;

  types::AseState target_state_;
  types::AseState current_state_;
+57 −38

File changed.

Preview size limit exceeded, changes collapsed.

Loading