Default implementation of getLeAudioAseConfiguration
for LE Audio Offload provider

Bug: 306225778
Test: mmm hardware/interfaces/bluetooth/audio/aidl/default
Change-Id: Ic09a1cbd8c85aa33375e8e1521ec03964117ac3a
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index 91ede92..4068568 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -28,6 +28,60 @@
 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 @@
   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 @@
         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(