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

Commit 64956920 authored by Bao Do's avatar Bao Do
Browse files

New parsing logic and configuration matching for LE Audio

Match the supported allocation channels from the device's capability
and remove loose matching ability.
This helps with choosing the correct configuration respecting
the device's number of ASE.

We also add "name" field for easier debugging.

Bug: 331490291
Test: atest VtsHalBluetoothAudioTargetTest
Change-Id: Iefd406fcd913ce553a02242b0a2100cab431476e
parent f6ce19d6
Loading
Loading
Loading
Loading
+182 −112
Original line number Diff line number Diff line
@@ -121,6 +121,40 @@ bool LeAudioOffloadAudioProvider::isValid(const SessionType& sessionType) {
  return (sessionType == session_type_);
}

std::string getSettingOutputString(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting) {
  std::stringstream ss;
  std::string name = "";
  if (!setting.sinkAseConfiguration.has_value() &&
      !setting.sourceAseConfiguration.has_value())
    return "";
  std::vector<
      std::optional<LeAudioAseConfigurationSetting::AseDirectionConfiguration>>*
      directionAseConfiguration;
  if (setting.sinkAseConfiguration.has_value() &&
      !setting.sinkAseConfiguration.value().empty())
    directionAseConfiguration = &setting.sinkAseConfiguration.value();
  else
    directionAseConfiguration = &setting.sourceAseConfiguration.value();
  for (auto& aseConfiguration : *directionAseConfiguration) {
    if (aseConfiguration.has_value() &&
        aseConfiguration.value().aseConfiguration.metadata.has_value()) {
      for (auto& meta :
           aseConfiguration.value().aseConfiguration.metadata.value()) {
        if (meta.has_value() &&
            meta.value().getTag() == MetadataLtv::vendorSpecific) {
          auto k = meta.value().get<MetadataLtv::vendorSpecific>().opaqueValue;
          name = std::string(k.begin(), k.end());
          break;
        }
      }
    }
  }

  ss << "setting name: " << name << ", setting: " << setting.toString();
  return ss.str();
}

ndk::ScopedAStatus LeAudioOffloadAudioProvider::startSession(
    const std::shared_ptr<IBluetoothAudioPort>& host_if,
    const AudioConfiguration& audio_config,
@@ -218,15 +252,24 @@ bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
  return false;
}

int getCountFromBitmask(int bitmask) {
  return std::bitset<32>(bitmask).count();
}

bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
    CodecSpecificConfigurationLtv::AudioChannelAllocation&
    /*cfg_channel*/,
    CodecSpecificConfigurationLtv::AudioChannelAllocation& cfg_channel,
    CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
    /*capability_channel*/) {
  // Simply ignore.
  // Later can use additional capabilities to match requirement.
  bool isMatched = true;
  return isMatched;
        capability_channel) {
  int count = getCountFromBitmask(cfg_channel.bitmask);
  if (count == 1 &&
      !(capability_channel.bitmask &
        CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts::ONE))
    return false;
  if (count == 2 &&
      !(capability_channel.bitmask &
        CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts::TWO))
    return false;
  return true;
}

bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU(
@@ -322,12 +365,6 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
  return true;
}

bool isMonoConfig(
    CodecSpecificConfigurationLtv::AudioChannelAllocation allocation) {
  auto channel_count = std::bitset<32>(allocation.bitmask);
  return (channel_count.count() <= 1);
}

bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    LeAudioAseConfiguration& setting_cfg,
    const LeAudioAseConfiguration& requirement_cfg) {
@@ -337,9 +374,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    if (!setting_cfg.codecId.has_value()) return false;
    if (!isMatchedValidCodec(setting_cfg.codecId.value(),
                             requirement_cfg.codecId.value())) {
      LOG(WARNING) << __func__ << ": Doesn't match valid codec, cfg = "
                   << setting_cfg.codecId.value().toString()
                   << ", req = " << requirement_cfg.codecId.value().toString();
      return false;
    }
  }
@@ -347,9 +381,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
  if (requirement_cfg.targetLatency !=
          LeAudioAseConfiguration::TargetLatency::UNDEFINED &&
      setting_cfg.targetLatency != requirement_cfg.targetLatency) {
    LOG(WARNING) << __func__ << ": Doesn't match target latency, cfg = "
                 << int(setting_cfg.targetLatency)
                 << ", req = " << int(requirement_cfg.targetLatency);
    return false;
  }
  // Ignore PHY requirement
@@ -365,8 +396,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
    // Config not found for this requirement, cannot match
    if (cfg == cfg_tag_map.end()) {
      LOG(WARNING) << __func__ << ": Config not found for the requirement "
                   << requirement_cfg.toString();
      return false;
    }

@@ -377,10 +406,6 @@ bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
      continue;

    if (cfg->second != requirement_cfg) {
      LOG(WARNING) << __func__
                   << ": Config doesn't match the requirement, cfg = "
                   << cfg->second.toString()
                   << ", req = " << requirement_cfg.toString();
      return false;
    }
  }
@@ -437,10 +462,6 @@ int getLeAudioAseConfigurationAllocationBitmask(LeAudioAseConfiguration cfg) {
  return 0;
}

int getCountFromBitmask(int bitmask) {
  return std::bitset<32>(bitmask).count();
}

std::optional<AseDirectionConfiguration> findValidMonoConfig(
    std::vector<AseDirectionConfiguration>& valid_direction_configurations,
    int bitmask) {
@@ -505,14 +526,13 @@ std::vector<AseDirectionConfiguration> getValidConfigurationsFromAllocation(
  return {};
}

// Check and filter each index to see if it's a match.
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
    std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
        direction_configurations,
    const std::vector<std::optional<AseDirectionRequirement>>& requirements,
    std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
        valid_direction_configurations,
    bool is_exact) {
  // For every requirement, find the matched ase configuration
        valid_direction_configurations) {
  if (!direction_configurations.has_value()) return;

  if (!valid_direction_configurations.has_value()) {
@@ -520,38 +540,55 @@ void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
        std::vector<std::optional<AseDirectionConfiguration>>();
  }

  for (auto& requirement : requirements) {
    if (!requirement.has_value()) continue;
  // Exact matching process
  // Need to respect the number of device
  for (int i = 0; i < requirements.size(); ++i) {
    auto requirement = requirements[i];
    auto direction_configuration = direction_configurations.value()[i];
    if (!direction_configuration.has_value()) {
      valid_direction_configurations = std::nullopt;
      return;
    }
    auto cfg = direction_configuration.value();
    if (!filterMatchedAseConfiguration(cfg.aseConfiguration,
                                       requirement.value().aseConfiguration)) {
      valid_direction_configurations = std::nullopt;
      return;  // No way to match
    }
    // For exact match, we require this direction to have the same allocation.
    // If stereo, need stereo.
    // If mono, need mono (modified to the correct required allocation)
    auto req_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask(
        requirement.value().aseConfiguration);
    auto req_channel_count = getCountFromBitmask(req_allocation_bitmask);

    auto temp = std::vector<AseDirectionConfiguration>();

    for (auto direction_configuration : direction_configurations.value()) {
      if (!direction_configuration.has_value()) continue;
      if (!filterMatchedAseConfiguration(
              direction_configuration.value().aseConfiguration,
              requirement.value().aseConfiguration))
        continue;
      // Valid if match any requirement.
      temp.push_back(direction_configuration.value());
    int req_channel_count = getCountFromBitmask(req_allocation_bitmask);
    int cfg_bitmask =
        getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
    int cfg_channel_count = getCountFromBitmask(cfg_bitmask);
    if (req_channel_count <= 1) {
      // MONO case, is a match if also mono, modify to the same allocation
      if (cfg_channel_count > 1) {
        valid_direction_configurations = std::nullopt;
        return;  // Not a match
      }
      // Modify the bitmask to be the same as the requirement
      for (auto& codec_cfg : cfg.aseConfiguration.codecConfiguration) {
        if (codec_cfg.getTag() ==
            CodecSpecificConfigurationLtv::Tag::audioChannelAllocation) {
          codec_cfg
              .get<CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>()
              .bitmask = req_allocation_bitmask;
          break;
        }

    // Get the best matching config based on channel allocation
    auto total_cfg_channel_count = 0;
    auto req_valid_configs = getValidConfigurationsFromAllocation(
        req_allocation_bitmask, temp, is_exact);
    // Count and check required channel counts
    for (auto& cfg : req_valid_configs) {
      total_cfg_channel_count += getCountFromBitmask(
          getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration));
      valid_direction_configurations.value().push_back(cfg);
      }
    if (total_cfg_channel_count != req_channel_count) {
    } else {
      // STEREO case, is a match if same allocation
      if (req_allocation_bitmask != cfg_bitmask) {
        valid_direction_configurations = std::nullopt;
      return;
        return;  // Not a match
      }
    }
    // Push to list if valid
    valid_direction_configurations.value().push_back(cfg);
  }
}

@@ -612,8 +649,8 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement& requirement,
    bool is_exact) {
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
        requirement) {
  // Try to match context in metadata.
  if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
      requirement.audioContext.bitmask)
@@ -629,10 +666,29 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
      .flags = setting.flags,
  };

  // The number of AseDirectionRequirement in the requirement
  // is the number of device.

  // The exact matching process is as follow:
  // 1. Setting direction has the same number of cfg (ignore when null require)
  // 2. For each index, it's a 1-1 filter / mapping.

  if (requirement.sinkAseRequirement.has_value() &&
      requirement.sinkAseRequirement.value().size() !=
          setting.sinkAseConfiguration.value().size()) {
    return std::nullopt;
  }

  if (requirement.sourceAseRequirement.has_value() &&
      requirement.sourceAseRequirement.value().size() !=
          setting.sourceAseConfiguration.value().size()) {
    return std::nullopt;
  }

  if (requirement.sinkAseRequirement.has_value()) {
    filterRequirementAseDirectionConfiguration(
        setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
        filtered_setting.sinkAseConfiguration, is_exact);
        filtered_setting.sinkAseConfiguration);
    if (!filtered_setting.sinkAseConfiguration.has_value()) {
      return std::nullopt;
    }
@@ -642,7 +698,7 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
    filterRequirementAseDirectionConfiguration(
        setting.sourceAseConfiguration,
        requirement.sourceAseRequirement.value(),
        filtered_setting.sourceAseConfiguration, is_exact);
        filtered_setting.sourceAseConfiguration);
    if (!filtered_setting.sourceAseConfiguration.has_value()) {
      return std::nullopt;
    }
@@ -656,8 +712,7 @@ LeAudioOffloadAudioProvider::matchWithRequirement(
    std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
        matched_ase_configuration_settings,
    const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
        in_requirements,
    bool is_exact) {
        in_requirements) {
  // Each requirement will match with a valid setting
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
  for (auto& requirement : in_requirements) {
@@ -667,12 +722,12 @@ LeAudioOffloadAudioProvider::matchWithRequirement(

    for (auto& setting : matched_ase_configuration_settings) {
      auto filtered_ase_configuration_setting =
          getRequirementMatchedAseConfigurationSettings(setting, requirement,
                                                        is_exact);
          getRequirementMatchedAseConfigurationSettings(setting, requirement);
      if (filtered_ase_configuration_setting.has_value()) {
        result.push_back(filtered_ase_configuration_setting.value());
        LOG(INFO) << __func__ << ": Result = "
                  << filtered_ase_configuration_setting.value().toString();
        LOG(INFO) << __func__ << ": Result found: "
                  << getSettingOutputString(
                         filtered_ase_configuration_setting.value());
        // Found a matched setting, ignore other settings
        is_matched = true;
        break;
@@ -690,8 +745,8 @@ LeAudioOffloadAudioProvider::matchWithRequirement(
}

// For each requirement, a valid ASE configuration will satify:
// - matched with any sink capability (if presented)
// - OR matched with any source capability (if presented)
// - matched with the sink capability (if presented)
// - AND matched with the source capability (if presented)
// - and the setting need to pass the requirement
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
    const std::optional<std::vector<
@@ -719,49 +774,55 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
  // -> preferred list will have settings with MEDIA context
  // -> non-preferred list will have settings with any context
  // We want to match requirement with preferred context settings first
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      sink_matched_ase_configuration_settings;
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      matched_ase_configuration_settings;
  // Matched ASE configuration with non-preferred audio context
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      sink_non_prefer_matched_ase_configuration_settings;
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      non_prefer_matched_ase_configuration_settings;

  if (in_remoteSinkAudioCapabilities.has_value())
    // Matching each setting with any remote capabilities
  // A setting must match both source and sink.
  // First filter all setting matched with sink capability
  if (in_remoteSinkAudioCapabilities.has_value()) {
    for (auto& setting : ase_configuration_settings)
      for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
        if (!capability.has_value()) continue;
        // LOG(DEBUG) << __func__ << ": " << capability.value().toString();
        auto filtered_ase_configuration_setting =
            getCapabilitiesMatchedAseConfigurationSettings(
                setting, capability.value(), kLeAudioDirectionSink);
        if (filtered_ase_configuration_setting.has_value()) {
          // Push to non-prefer first for the broadest matching possible
          non_prefer_matched_ase_configuration_settings.push_back(
          sink_non_prefer_matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
          // Try to filter out prefer context to another vector.
          if (filterCapabilitiesMatchedContext(
                  filtered_ase_configuration_setting.value().audioContext,
                  capability.value())) {
            matched_ase_configuration_settings.push_back(
            sink_matched_ase_configuration_settings.push_back(
                filtered_ase_configuration_setting.value());
          }
        }
      }
  } else {
    sink_matched_ase_configuration_settings = ase_configuration_settings;
    sink_non_prefer_matched_ase_configuration_settings =
        ase_configuration_settings;
  }

  // Combine filter every source capability
  if (in_remoteSourceAudioCapabilities.has_value())
    // Matching each setting with any remote capabilities
    for (auto& setting : ase_configuration_settings)
  if (in_remoteSourceAudioCapabilities.has_value()) {
    // Prefer context
    for (auto& setting : sink_matched_ase_configuration_settings)
      for (auto& capability : in_remoteSourceAudioCapabilities.value()) {
        if (!capability.has_value()) continue;
        auto filtered_ase_configuration_setting =
            getCapabilitiesMatchedAseConfigurationSettings(
                setting, capability.value(), kLeAudioDirectionSource);
        if (filtered_ase_configuration_setting.has_value()) {
          // Put into the same list
          // possibly duplicated, filtered by requirement later
          // Push to non-prefer first for the broadest matching possible
          non_prefer_matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
          // Try to filter out prefer context to another vector.
          if (filterCapabilitiesMatchedContext(
                  filtered_ase_configuration_setting.value().audioContext,
@@ -772,43 +833,52 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
        }
      }

    // Non prefer context
    for (auto& setting : sink_non_prefer_matched_ase_configuration_settings)
      for (auto& capability : in_remoteSourceAudioCapabilities.value()) {
        if (!capability.has_value()) continue;
        auto filtered_ase_configuration_setting =
            getCapabilitiesMatchedAseConfigurationSettings(
                setting, capability.value(), kLeAudioDirectionSource);
        if (filtered_ase_configuration_setting.has_value()) {
          // Push to non-prefer first for the broadest matching possible
          non_prefer_matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
        }
      }
  } else {
    matched_ase_configuration_settings =
        sink_matched_ase_configuration_settings;
    non_prefer_matched_ase_configuration_settings =
        sink_non_prefer_matched_ase_configuration_settings;
  }

  // Matching priority list:
  // Preferred context - exact match with allocation
  // Any context - exact match with allocation
  // Preferred context - loose match with allocation
  // Any context - loose match with allocation

  // A loose match will attempt to return 2 settings with the
  // combined allocation bitmask equal the required allocation.
  // For example, we can return 2 link (left link and right link) when
  // the requirement required 1 (left + right) link.
  auto result = matchWithRequirement(matched_ase_configuration_settings,
                                     in_requirements, true);
  if (result.empty()) {
    LOG(WARNING)
        << __func__
        << ": Cannot match with preferred context settings - exact match";
    result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
                                  in_requirements, true);

  LOG(DEBUG) << __func__ << ": Called with requirement: ";
  for (auto& requirement : in_requirements) {
    LOG(DEBUG) << __func__ << " requirement: " << requirement.toString();
  }
  if (result.empty()) {
    LOG(WARNING)
        << __func__
        << ": Cannot match with non-preferred context settings - exact match";
    result = matchWithRequirement(matched_ase_configuration_settings,
                                  in_requirements, false);

  LOG(DEBUG) << __func__ << ": List of settings with the same context:";
  for (auto& setting : matched_ase_configuration_settings) {
    LOG(DEBUG) << __func__ << ": " << getSettingOutputString(setting);
  }

  auto result =
      matchWithRequirement(matched_ase_configuration_settings, in_requirements);
  if (result.empty()) {
    LOG(WARNING) << __func__
                 << ": Cannot match with preferred context settings - "
                    "non-exact match";
                 << ": Cannot match with preferred context settings";
    result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
                                  in_requirements, false);
                                  in_requirements);
  }
  if (result.empty())
  if (result.empty()) {
    LOG(ERROR) << __func__
               << ": Cannot match with non preferred context settings - "
                  "non-exact match";
               << ": Cannot match with non-preferred context settings";
  }
  *_aidl_return = result;
  return ndk::ScopedAStatus::ok();
};
+3 −6
Original line number Diff line number Diff line
@@ -139,8 +139,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
          direction_configurations,
      const std::vector<std::optional<AseDirectionRequirement>>& requirements,
      std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
          valid_direction_configurations,
      bool is_exact);
          valid_direction_configurations);
  std::optional<LeAudioAseConfigurationSetting>
  getCapabilitiesMatchedAseConfigurationSettings(
      IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
@@ -150,8 +149,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
  getRequirementMatchedAseConfigurationSettings(
      IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
      const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
          requirement,
      bool is_exact);
          requirement);
  bool isMatchedQosRequirement(LeAudioAseQosConfiguration setting_qos,
                               AseQosDirectionRequirement requirement_qos);
  std::optional<LeAudioBroadcastConfigurationSetting>
@@ -175,8 +173,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
          matched_ase_configuration_settings,
      const std::vector<
          IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
          in_requirements,
      bool is_exact);
          in_requirements);
};

class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {