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

Commit 867af60a authored by Bao Do's avatar Bao Do
Browse files

Default implementation of getLeAudioAseConfiguration

for LE Audio Offload provider

Bug: 306225778
Test: mmm hardware/interfaces/bluetooth/audio/aidl/default
Change-Id: Ic09a1cbd8c85aa33375e8e1521ec03964117ac3a
parent 6112bda4
Loading
Loading
Loading
Loading
+407 −6
Original line number Diff line number Diff line
@@ -28,6 +28,60 @@ namespace hardware {
namespace bluetooth {
namespace audio {

constexpr uint8_t kLeAudioDirectionSink = 0x01;
constexpr uint8_t kLeAudioDirectionSource = 0x02;

const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t>
    freq_to_support_bitmask_map = {
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ8000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ11025,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ11025},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ22050,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ22050},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ32000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ88200,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ88200},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ96000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ176400,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ176400},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ192000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ192000},
        {CodecSpecificConfigurationLtv::SamplingFrequency::HZ384000,
         CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ384000},
};

// Helper map from capability's tag to configuration's tag
std::map<CodecSpecificCapabilitiesLtv::Tag, CodecSpecificConfigurationLtv::Tag>
    cap_to_cfg_tag_map = {
        {CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies,
         CodecSpecificConfigurationLtv::Tag::samplingFrequency},
        {CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU,
         CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU},
        {CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations,
         CodecSpecificConfigurationLtv::Tag::frameDuration},
        {CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts,
         CodecSpecificConfigurationLtv::Tag::audioChannelAllocation},
        {CodecSpecificCapabilitiesLtv::Tag::supportedOctetsPerCodecFrame,
         CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame},
};

const std::map<CodecSpecificConfigurationLtv::FrameDuration, uint32_t>
    fduration_to_support_fduration_map = {
        {CodecSpecificConfigurationLtv::FrameDuration::US7500,
         CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US7500},
        {CodecSpecificConfigurationLtv::FrameDuration::US10000,
         CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US10000},
};

LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
    : LeAudioOffloadAudioProvider() {
  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
@@ -87,6 +141,310 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::setCodecPriority(
  return ndk::ScopedAStatus::ok();
};

bool LeAudioOffloadAudioProvider::isMatchedValidCodec(CodecId cfg_codec,
                                                      CodecId req_codec) {
  auto priority = codec_priority_map_.find(cfg_codec);
  if (priority != codec_priority_map_.end() && priority->second == -1)
    return false;
  return cfg_codec == req_codec;
}

bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext(
    AudioContext setting_context,
    const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
  // If has no metadata, assume match
  if (!capabilities.metadata.has_value()) return true;

  for (auto metadata : capabilities.metadata.value()) {
    if (!metadata.has_value()) continue;
    if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
      // Check all pref audio context to see if anything matched
      auto& context = metadata.value()
                          .get<MetadataLtv::Tag::preferredAudioContexts>()
                          .values;
      if (setting_context.bitmask & context.bitmask) return true;
    }
  }

  return false;
}

bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq(
    CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
    CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
        capability_freq) {
  for (auto [freq, bitmask] : freq_to_support_bitmask_map)
    if (cfg_freq == freq) return (capability_freq.bitmask & bitmask);
  return false;
}

bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
    CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
    CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
        capability_fduration) {
  for (auto [fduration, bitmask] : fduration_to_support_fduration_map)
    if (cfg_fduration == fduration)
      return (capability_fduration.bitmask & bitmask);
  return false;
}

bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
    CodecSpecificConfigurationLtv::AudioChannelAllocation&
    /*cfg_channel*/,
    CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
    /*capability_channel*/) {
  bool isMatched = true;
  // TODO: how to match?
  return isMatched;
}

bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU(
    CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu,
    CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU&
        capability_frame_sdu) {
  return cfg_frame_sdu.value <= capability_frame_sdu.value;
}

bool LeAudioOffloadAudioProvider::isMatchedOctetsPerCodecFrame(
    CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets,
    CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame&
        capability_octets) {
  return cfg_octets.value >= capability_octets.minimum &&
         cfg_octets.value <= capability_octets.maximum;
}

bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
    std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
    std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities) {
  // Convert all codec_cfg into a map of tags -> correct data
  std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
      cfg_tag_map;
  for (auto codec_cfg_data : codec_cfg)
    cfg_tag_map[codec_cfg_data.getTag()] = codec_cfg_data;

  for (auto& codec_capability : codec_capabilities) {
    auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]);
    // Cannot find tag for the capability:
    if (cfg == cfg_tag_map.end()) return false;

    // Matching logic for sampling frequency
    if (codec_capability.getTag() ==
        CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies) {
      if (!isMatchedSamplingFreq(
              cfg->second
                  .get<CodecSpecificConfigurationLtv::Tag::samplingFrequency>(),
              codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                       supportedSamplingFrequencies>()))
        return false;
    } else if (codec_capability.getTag() ==
               CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations) {
      if (!isMatchedFrameDuration(
              cfg->second
                  .get<CodecSpecificConfigurationLtv::Tag::frameDuration>(),
              codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                       supportedFrameDurations>()))
        return false;
    } else if (codec_capability.getTag() ==
               CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts) {
      if (!isMatchedAudioChannel(
              cfg->second.get<
                  CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>(),
              codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                       supportedAudioChannelCounts>()))
        return false;
    } else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
                                                supportedMaxCodecFramesPerSDU) {
      if (!isMatchedCodecFramesPerSDU(
              cfg->second.get<
                  CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU>(),
              codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                       supportedMaxCodecFramesPerSDU>()))
        return false;
    } else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
                                                supportedOctetsPerCodecFrame) {
      if (!isMatchedOctetsPerCodecFrame(
              cfg->second.get<
                  CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(),
              codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                       supportedOctetsPerCodecFrame>()))
        return false;
    }
  }

  return true;
}

bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
    LeAudioAseConfiguration setting_cfg,
    LeAudioAseConfiguration requirement_cfg) {
  // Check matching for codec configuration <=> requirement ASE codec
  // Also match if no CodecId requirement
  if (requirement_cfg.codecId.has_value()) {
    if (!setting_cfg.codecId.has_value()) return false;
    if (!isMatchedValidCodec(setting_cfg.codecId.value(),
                             requirement_cfg.codecId.value()))
      return false;
  }

  if (setting_cfg.targetLatency != requirement_cfg.targetLatency) return false;
  // Ignore PHY requirement

  // Check all codec configuration
  std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
      cfg_tag_map;
  for (auto cfg : setting_cfg.codecConfiguration)
    cfg_tag_map[cfg.getTag()] = cfg;

  for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
    // Directly compare CodecSpecificConfigurationLtv
    auto cfg = cfg_tag_map.find(requirement_cfg.getTag());
    if (cfg == cfg_tag_map.end()) return false;

    if (cfg->second != requirement_cfg) return false;
  }
  // Ignore vendor configuration and metadata requirement

  return true;
}

void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration(
    std::vector<std::optional<AseDirectionConfiguration>>&
        direction_configurations,
    const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
    std::vector<std::optional<AseDirectionConfiguration>>&
        valid_direction_configurations) {
  for (auto direction_configuration : direction_configurations) {
    if (!direction_configuration.has_value()) continue;
    if (!direction_configuration.value().aseConfiguration.codecId.has_value())
      continue;
    if (!isMatchedValidCodec(
            direction_configuration.value().aseConfiguration.codecId.value(),
            capabilities.codecId))
      continue;
    // Check matching for codec configuration <=> codec capabilities
    if (!isCapabilitiesMatchedCodecConfiguration(
            direction_configuration.value().aseConfiguration.codecConfiguration,
            capabilities.codecSpecificCapabilities))
      continue;
    valid_direction_configurations.push_back(direction_configuration);
  }
}

void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
    std::vector<std::optional<AseDirectionConfiguration>>&
        direction_configurations,
    const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
        requirements,
    std::vector<std::optional<AseDirectionConfiguration>>&
        valid_direction_configurations) {
  for (auto direction_configuration : direction_configurations) {
    if (!requirements.has_value()) {
      // If there's no requirement, all are valid
      valid_direction_configurations.push_back(direction_configuration);
      continue;
    }
    if (!direction_configuration.has_value()) continue;

    for (auto& requirement : requirements.value()) {
      if (!requirement.has_value()) continue;
      if (!isMatchedAseConfiguration(
              direction_configuration.value().aseConfiguration,
              requirement.value().aseConfiguration))
        continue;
      // Valid if match any requirement.
      valid_direction_configurations.push_back(direction_configuration);
      break;
    }
  }
}

/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
 * capabilities. The new setting will have a filtered list of
 * AseDirectionConfiguration that matched the capabilities */
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
    const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
    uint8_t direction) {
  // Try to match context in metadata.
  if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities))
    return std::nullopt;

  // Get a list of all matched AseDirectionConfiguration
  // for the input direction
  std::vector<std::optional<AseDirectionConfiguration>>*
      direction_configuration = nullptr;
  if (direction == kLeAudioDirectionSink) {
    if (!setting.sinkAseConfiguration.has_value()) return std::nullopt;
    direction_configuration = &setting.sinkAseConfiguration.value();
  } else {
    if (!setting.sourceAseConfiguration.has_value()) return std::nullopt;
    direction_configuration = &setting.sourceAseConfiguration.value();
  }
  std::vector<std::optional<AseDirectionConfiguration>>
      valid_direction_configuration;
  filterCapabilitiesAseDirectionConfiguration(
      *direction_configuration, capabilities, valid_direction_configuration);
  if (valid_direction_configuration.empty()) return std::nullopt;

  // Create a new LeAudioAseConfigurationSetting and return
  LeAudioAseConfigurationSetting filtered_setting;
  filtered_setting.audioContext = setting.audioContext;
  filtered_setting.packing = setting.packing;
  if (direction == kLeAudioDirectionSink) {
    filtered_setting.sinkAseConfiguration = valid_direction_configuration;
  } else {
    filtered_setting.sourceAseConfiguration = valid_direction_configuration;
  }
  filtered_setting.flags = setting.flags;

  return filtered_setting;
}

/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
 * requirement. The new setting will have a filtered list of
 * AseDirectionConfiguration that matched the requirement */
std::optional<LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
    IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
        requirement) {
  // Try to match context in metadata.
  if (setting.audioContext != requirement.audioContext) return std::nullopt;

  // Check requirement for the correct direction
  const std::optional<std::vector<std::optional<AseDirectionRequirement>>>*
      direction_requirement;
  std::vector<std::optional<AseDirectionConfiguration>>*
      direction_configuration;
  if (setting.sinkAseConfiguration.has_value()) {
    direction_configuration = &setting.sinkAseConfiguration.value();
    direction_requirement = &requirement.sinkAseRequirement;
  } else {
    direction_configuration = &setting.sourceAseConfiguration.value();
    direction_requirement = &requirement.sourceAseRequirement;
  }

  std::vector<std::optional<AseDirectionConfiguration>>
      valid_direction_configuration;
  filterRequirementAseDirectionConfiguration(*direction_configuration,
                                             *direction_requirement,
                                             valid_direction_configuration);
  if (valid_direction_configuration.empty()) return std::nullopt;

  // Create a new LeAudioAseConfigurationSetting and return
  LeAudioAseConfigurationSetting filtered_setting;
  filtered_setting.audioContext = setting.audioContext;
  filtered_setting.packing = setting.packing;
  if (setting.sinkAseConfiguration.has_value())
    filtered_setting.sinkAseConfiguration = valid_direction_configuration;
  else
    filtered_setting.sourceAseConfiguration = valid_direction_configuration;
  filtered_setting.flags = setting.flags;

  return filtered_setting;
}

ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
    const std::optional<std::vector<
        std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
@@ -98,12 +456,55 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
        in_requirements,
    std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
        _aidl_return) {
  /* TODO: Implement */
  (void)in_remoteSinkAudioCapabilities;
  (void)in_remoteSourceAudioCapabilities;
  (void)in_requirements;
  (void)_aidl_return;
  return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
  // Get all configuration settings
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      ase_configuration_settings =
          BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();

  // Currently won't handle case where both sink and source capabilities
  // are passed in. Only handle one of them.
  const std::optional<std::vector<
      std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>*
      in_remoteAudioCapabilities;
  uint8_t direction = 0;
  if (in_remoteSinkAudioCapabilities.has_value()) {
    direction = kLeAudioDirectionSink;
    in_remoteAudioCapabilities = &in_remoteSinkAudioCapabilities;
  } else {
    direction = kLeAudioDirectionSource;
    in_remoteAudioCapabilities = &in_remoteSourceAudioCapabilities;
  }

  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      capability_matched_ase_configuration_settings;
  // Matching with remote capabilities
  for (auto& setting : ase_configuration_settings) {
    for (auto& capability : in_remoteAudioCapabilities->value()) {
      if (!capability.has_value()) continue;
      auto filtered_ase_configuration_setting =
          getCapabilitiesMatchedAseConfigurationSettings(
              setting, capability.value(), direction);
      if (filtered_ase_configuration_setting.has_value()) {
        capability_matched_ase_configuration_settings.push_back(
            filtered_ase_configuration_setting.value());
      }
    }
  }

  // Matching with requirements
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
  for (auto& setting : capability_matched_ase_configuration_settings) {
    for (auto& requirement : in_requirements) {
      auto filtered_ase_configuration_setting =
          getRequirementMatchedAseConfigurationSettings(setting, requirement);
      if (filtered_ase_configuration_setting.has_value()) {
        result.push_back(filtered_ase_configuration_setting.value());
      }
    }
  }

  *_aidl_return = result;
  return ndk::ScopedAStatus::ok();
};

ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
+3 −4
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {

  // Private matching function definitions
  bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec);
  bool isMatchedContext(
  bool isCapabilitiesMatchedContext(
      AudioContext setting_context,
      const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
  bool isMatchedSamplingFreq(
@@ -113,8 +113,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
  bool isCapabilitiesMatchedCodecConfiguration(
      std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
      std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
  bool isRequirementAseConfigurationMatched(
      LeAudioAseConfiguration setting_cfg,
  bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg,
                                 LeAudioAseConfiguration requirement_cfg);
  void filterCapabilitiesAseDirectionConfiguration(
      std::vector<std::optional<AseDirectionConfiguration>>&