/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "BTAudioProviderLeAudioHW"

#include "LeAudioOffloadAudioProvider.h"

#include <BluetoothAudioCodecs.h>
#include <BluetoothAudioSessionReport.h>
#include <android-base/logging.h>

namespace aidl {
namespace android {
namespace hardware {
namespace bluetooth {
namespace audio {

constexpr uint8_t kLeAudioDirectionSink = 0x01;
constexpr uint8_t kLeAudioDirectionSource = 0x02;
constexpr uint8_t kIsoDataPathHci = 0x00;
constexpr uint8_t kIsoDataPathPlatformDefault = 0x01;

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},
        {CodecSpecificConfigurationLtv::FrameDuration::US20000,
         CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US20000},
};

std::map<int32_t, CodecSpecificConfigurationLtv::SamplingFrequency>
    sampling_freq_map = {
        {16000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
        {24000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000},
        {48000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000},
        {96000, CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000},
};

std::map<int32_t, CodecSpecificConfigurationLtv::FrameDuration>
    frame_duration_map = {
        {7500, CodecSpecificConfigurationLtv::FrameDuration::US7500},
        {10000, CodecSpecificConfigurationLtv::FrameDuration::US10000},
};

LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
    : LeAudioOffloadAudioProvider() {
  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}

LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider()
    : LeAudioOffloadAudioProvider() {
  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
}

LeAudioOffloadBroadcastAudioProvider::LeAudioOffloadBroadcastAudioProvider()
    : LeAudioOffloadAudioProvider() {
  session_type_ =
      SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
}

LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider()
    : BluetoothAudioProvider() {}

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,
    const std::vector<LatencyMode>& latency_modes, DataMQDesc* _aidl_return) {
  if (session_type_ ==
      SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
    if (audio_config.getTag() != AudioConfiguration::leAudioBroadcastConfig) {
      LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
                   << audio_config.toString();
      *_aidl_return = DataMQDesc();
      return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
  } else if (audio_config.getTag() != AudioConfiguration::leAudioConfig) {
    LOG(WARNING) << __func__ << " - Invalid Audio Configuration="
                 << audio_config.toString();
    *_aidl_return = DataMQDesc();
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
  }

  return BluetoothAudioProvider::startSession(host_if, audio_config,
                                              latency_modes, _aidl_return);
}

ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSessionReady(
    DataMQDesc* _aidl_return) {
  BluetoothAudioSessionReport::OnSessionStarted(
      session_type_, stack_iface_, nullptr, *audio_config_, latency_modes_);
  *_aidl_return = DataMQDesc();
  return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus LeAudioOffloadAudioProvider::setCodecPriority(
    const CodecId& in_codecId, int32_t in_priority) {
  codec_priority_map_[in_codecId] = in_priority;
  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 ==
          LeAudioOffloadAudioProvider::CODEC_PRIORITY_DISABLED) {
    return false;
  }
  return cfg_codec == req_codec;
}

bool LeAudioOffloadAudioProvider::filterCapabilitiesMatchedContext(
    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& prefer_context =
          metadata.value()
              .get<MetadataLtv::Tag::preferredAudioContexts>()
              .values;
      if (setting_context.bitmask & prefer_context.bitmask) {
        // New mask with matched capability
        setting_context.bitmask &= prefer_context.bitmask;
        return true;
      }
    }
  }

  return false;
}

bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq(
    CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
    CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
        capability_freq) {
  auto p = freq_to_support_bitmask_map.find(cfg_freq);
  if (p != freq_to_support_bitmask_map.end()) {
    if (capability_freq.bitmask & p->second) {
      return true;
    }
  }
  return false;
}

bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
    CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
    CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
        capability_fduration) {
  auto p = fduration_to_support_fduration_map.find(cfg_fduration);
  if (p != fduration_to_support_fduration_map.end())
    if (capability_fduration.bitmask & p->second) {
      return true;
    }
  return false;
}

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

bool LeAudioOffloadAudioProvider::isMatchedAudioChannel(
    CodecSpecificConfigurationLtv::AudioChannelAllocation& cfg_channel,
    CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
        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(
    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.min &&
         cfg_octets.value <= capability_octets.max;
}

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()]);
    // If capability has this tag, but our configuration doesn't
    // Then we will assume it is matched
    if (cfg == cfg_tag_map.end()) {
      continue;
    }

    switch (codec_capability.getTag()) {
      case CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies: {
        if (!isMatchedSamplingFreq(
                cfg->second.get<
                    CodecSpecificConfigurationLtv::Tag::samplingFrequency>(),
                codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                         supportedSamplingFrequencies>())) {
          return false;
        }
        break;
      }

      case CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations: {
        if (!isMatchedFrameDuration(
                cfg->second
                    .get<CodecSpecificConfigurationLtv::Tag::frameDuration>(),
                codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                         supportedFrameDurations>())) {
          return false;
        }
        break;
      }

      case CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts: {
        if (!isMatchedAudioChannel(
                cfg->second.get<CodecSpecificConfigurationLtv::Tag::
                                    audioChannelAllocation>(),
                codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                         supportedAudioChannelCounts>())) {
          return false;
        }
        break;
      }

      case CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU: {
        if (!isMatchedCodecFramesPerSDU(
                cfg->second.get<CodecSpecificConfigurationLtv::Tag::
                                    codecFrameBlocksPerSDU>(),
                codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                         supportedMaxCodecFramesPerSDU>())) {
          return false;
        }
        break;
      }

      case CodecSpecificCapabilitiesLtv::Tag::supportedOctetsPerCodecFrame: {
        if (!isMatchedOctetsPerCodecFrame(
                cfg->second.get<
                    CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(),
                codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
                                         supportedOctetsPerCodecFrame>())) {
          return false;
        }
        break;
      }
    }
  }

  return true;
}

bool LeAudioOffloadAudioProvider::filterMatchedAseConfiguration(
    LeAudioAseConfiguration& setting_cfg,
    const 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 (requirement_cfg.targetLatency !=
          LeAudioAseConfiguration::TargetLatency::UNDEFINED &&
      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());
    // Config not found for this requirement, cannot match
    if (cfg == cfg_tag_map.end()) {
      return false;
    }

    // Ignore matching for audio channel allocation
    // since the rule is complicated. Match outside instead
    if (requirement_cfg.getTag() ==
        CodecSpecificConfigurationLtv::Tag::audioChannelAllocation)
      continue;

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

  return true;
}

bool LeAudioOffloadAudioProvider::isMatchedBISConfiguration(
    LeAudioBisConfiguration bis_cfg,
    const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
  if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) {
    return false;
  }
  if (!isCapabilitiesMatchedCodecConfiguration(
          bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities)) {
    return false;
  }
  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);
  }
}

int getLeAudioAseConfigurationAllocationBitmask(LeAudioAseConfiguration cfg) {
  for (auto cfg_ltv : cfg.codecConfiguration) {
    if (cfg_ltv.getTag() ==
        CodecSpecificConfigurationLtv::Tag::audioChannelAllocation) {
      return cfg_ltv
          .get<CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>()
          .bitmask;
    }
  }
  return 0;
}

std::optional<AseDirectionConfiguration> findValidMonoConfig(
    std::vector<AseDirectionConfiguration>& valid_direction_configurations,
    int bitmask) {
  for (auto& cfg : valid_direction_configurations) {
    int cfg_bitmask =
        getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
    if (getCountFromBitmask(cfg_bitmask) <= 1) {
      // 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 = bitmask;
          return cfg;
        }
      }
    }
  }
  return std::nullopt;
}

std::vector<AseDirectionConfiguration> getValidConfigurationsFromAllocation(
    int req_allocation_bitmask,
    std::vector<AseDirectionConfiguration>& valid_direction_configurations,
    bool is_exact) {
  // Prefer the same allocation_bitmask
  int channel_count = getCountFromBitmask(req_allocation_bitmask);

  if (is_exact) {
    for (auto& cfg : valid_direction_configurations) {
      int cfg_bitmask =
          getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
      if (cfg_bitmask == req_allocation_bitmask) {
        LOG(DEBUG)
            << __func__
            << ": Found an exact match for the requirement allocation of "
            << cfg_bitmask;
        return {cfg};
      }
    }
    return {};
  }
  // Not using exact match strategy
  if (channel_count <= 1) {
    // Mono requirement matched if cfg is a mono config
    auto cfg = findValidMonoConfig(valid_direction_configurations,
                                   req_allocation_bitmask);
    if (cfg.has_value()) return {cfg.value()};
  } else {
    // Stereo requirement returns 2 mono configs
    // that has a combined bitmask equal to the stereo config
    std::vector<AseDirectionConfiguration> temp;
    for (int bit = 0; bit < 32; ++bit)
      if (req_allocation_bitmask & (1 << bit)) {
        auto cfg =
            findValidMonoConfig(valid_direction_configurations, (1 << bit));
        if (cfg.has_value()) temp.push_back(cfg.value());
      }
    if (temp.size() == channel_count) return temp;
  }
  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) {
  if (!direction_configurations.has_value()) return;

  if (!valid_direction_configurations.has_value()) {
    valid_direction_configurations =
        std::vector<std::optional<AseDirectionConfiguration>>();
  }

  // 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);
    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;
        }
      }
    } else {
      // STEREO case, is a match if same allocation
      if (req_allocation_bitmask != cfg_bitmask) {
        valid_direction_configurations = std::nullopt;
        return;  // Not a match
      }
    }
    // Push to list if valid
    valid_direction_configurations.value().push_back(cfg);
  }
}

/* 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) {
  // Create a new LeAudioAseConfigurationSetting and return
  LeAudioAseConfigurationSetting filtered_setting{
      .audioContext = setting.audioContext,
      .sinkAseConfiguration = setting.sinkAseConfiguration,
      .sourceAseConfiguration = setting.sourceAseConfiguration,
      .flags = setting.flags,
      .packing = setting.packing,
  };

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

  // No valid configuration for this direction
  if (valid_direction_configuration.empty()) {
    return std::nullopt;
  }

  // Create a new LeAudioAseConfigurationSetting and return
  // For other direction will contain all settings
  if (direction == kLeAudioDirectionSink) {
    filtered_setting.sinkAseConfiguration = valid_direction_configuration;
  } else {
    filtered_setting.sourceAseConfiguration = valid_direction_configuration;
  }

  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) {
  // Create a new LeAudioAseConfigurationSetting to return
  // Make context the same as the requirement
  LeAudioAseConfigurationSetting filtered_setting{
      .audioContext = requirement.audioContext,
      .packing = setting.packing,
      .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);
    if (!filtered_setting.sinkAseConfiguration.has_value()) {
      return std::nullopt;
    }
  }

  if (requirement.sourceAseRequirement.has_value()) {
    filterRequirementAseDirectionConfiguration(
        setting.sourceAseConfiguration,
        requirement.sourceAseRequirement.value(),
        filtered_setting.sourceAseConfiguration);
    if (!filtered_setting.sourceAseConfiguration.has_value()) {
      return std::nullopt;
    }
  }

  return filtered_setting;
}

std::optional<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
LeAudioOffloadAudioProvider::matchWithRequirement(
    std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
        matched_ase_configuration_settings,
    const IBluetoothAudioProvider::LeAudioConfigurationRequirement& requirement,
    bool isMatchContext) {
  LOG(INFO) << __func__ << ": Trying to match for the requirement "
            << requirement.toString() << ", match context = " << isMatchContext;
  for (auto& setting : matched_ase_configuration_settings) {
    // Try to match context in metadata.
    if (isMatchContext) {
      if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
          requirement.audioContext.bitmask)
        continue;
      LOG(DEBUG) << __func__ << ": Setting with matched context: "
                 << getSettingOutputString(setting);
    }

    auto filtered_ase_configuration_setting =
        getRequirementMatchedAseConfigurationSettings(setting, requirement);
    if (filtered_ase_configuration_setting.has_value()) {
      LOG(INFO) << __func__ << ": Result found: "
                << getSettingOutputString(
                       filtered_ase_configuration_setting.value());
      // Found a matched setting, ignore other settings
      return filtered_ase_configuration_setting;
    }
  }
  // If cannot satisfy this requirement, return nullopt
  LOG(WARNING) << __func__ << ": Cannot match the requirement "
               << requirement.toString()
               << ", match context = " << isMatchContext;
  return std::nullopt;
}

// For each requirement, a valid ASE configuration will satify:
// - 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<
        std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
        in_remoteSinkAudioCapabilities,
    const std::optional<std::vector<
        std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
        in_remoteSourceAudioCapabilities,
    const std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
        in_requirements,
    std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>*
        _aidl_return) {
  // Get all configuration settings
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      ase_configuration_settings =
          BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();

  if (!in_remoteSinkAudioCapabilities.has_value() &&
      !in_remoteSourceAudioCapabilities.has_value()) {
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
  }

  // Matched ASE configuration with ignored audio context
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      sink_matched_ase_configuration_settings;
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      matched_ase_configuration_settings;

  // 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;
        auto filtered_ase_configuration_setting =
            getCapabilitiesMatchedAseConfigurationSettings(
                setting, capability.value(), kLeAudioDirectionSink);
        if (filtered_ase_configuration_setting.has_value()) {
          sink_matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
        }
      }
    }
  } else {
    sink_matched_ase_configuration_settings = ase_configuration_settings;
  }

  // Combine filter every source capability
  if (in_remoteSourceAudioCapabilities.has_value()) {
    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()) {
          matched_ase_configuration_settings.push_back(
              filtered_ase_configuration_setting.value());
        }
      }
  } else {
    matched_ase_configuration_settings =
        sink_matched_ase_configuration_settings;
  }

  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
  for (auto& requirement : in_requirements) {
    // For each requirement, try to match with a setting.
    // If we cannot match, return an empty result.

    // Matching priority list:
    // Preferred context - exact match with allocation
    // Any context - exact match with allocation
    auto matched_setting_with_context = matchWithRequirement(
        matched_ase_configuration_settings, requirement, true);
    if (matched_setting_with_context.has_value()) {
      result.push_back(matched_setting_with_context.value());
    } else {
      auto matched_setting = matchWithRequirement(
          matched_ase_configuration_settings, requirement, false);
      if (matched_setting.has_value()) {
        result.push_back(matched_setting.value());
      } else {
        // Cannot find a match for this requirement
        // Immediately return
        LOG(ERROR)
            << __func__
            << ": Cannot find any match for this requirement, exitting...";
        result.clear();
        *_aidl_return = result;
        return ndk::ScopedAStatus::ok();
      }
    }
  }

  LOG(INFO) << __func__ << ": Found matches for all requirements!";
  *_aidl_return = result;
  return ndk::ScopedAStatus::ok();
};

bool LeAudioOffloadAudioProvider::isMatchedQosRequirement(
    LeAudioAseQosConfiguration setting_qos,
    AseQosDirectionRequirement requirement_qos) {
  if (setting_qos.retransmissionNum !=
      requirement_qos.preferredRetransmissionNum) {
    return false;
  }
  if (setting_qos.maxTransportLatencyMs >
      requirement_qos.maxTransportLatencyMs) {
    return false;
  }
  return true;
}

bool isValidQosRequirement(AseQosDirectionRequirement qosRequirement) {
  return ((qosRequirement.maxTransportLatencyMs > 0) &&
          (qosRequirement.presentationDelayMaxUs > 0) &&
          (qosRequirement.presentationDelayMaxUs >=
           qosRequirement.presentationDelayMinUs));
}

std::optional<LeAudioAseQosConfiguration>
LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
    uint8_t direction,
    const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
        qosRequirement,
    std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings,
    bool is_exact) {
  std::optional<AseQosDirectionRequirement> direction_qos_requirement =
      std::nullopt;

  // Get the correct direction
  if (direction == kLeAudioDirectionSink) {
    direction_qos_requirement = qosRequirement.sinkAseQosRequirement.value();
  } else {
    direction_qos_requirement = qosRequirement.sourceAseQosRequirement.value();
  }

  for (auto& setting : ase_configuration_settings) {
    // Context matching
    if ((setting.audioContext.bitmask & qosRequirement.audioContext.bitmask) !=
        qosRequirement.audioContext.bitmask)
      continue;

    // Match configuration flags
    // Currently configuration flags are not populated, ignore.

    // Get a list of all matched AseDirectionConfiguration
    // for the input direction
    std::optional<std::vector<std::optional<AseDirectionConfiguration>>>
        direction_configuration = std::nullopt;
    if (direction == kLeAudioDirectionSink) {
      if (!setting.sinkAseConfiguration.has_value()) continue;
      direction_configuration.emplace(setting.sinkAseConfiguration.value());
    } else {
      if (!setting.sourceAseConfiguration.has_value()) continue;
      direction_configuration.emplace(setting.sourceAseConfiguration.value());
    }

    if (!direction_configuration.has_value()) {
      return std::nullopt;
    }

    // Collect all valid cfg into a vector
    // Then try to get the best match for audio allocation

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

    for (auto& cfg : direction_configuration.value()) {
      if (!cfg.has_value()) continue;
      // If no requirement, return the first QoS
      if (!direction_qos_requirement.has_value()) {
        return cfg.value().qosConfiguration;
      }

      // If has requirement, return the first matched QoS
      // Try to match the ASE configuration
      // and QoS with requirement
      if (!cfg.value().qosConfiguration.has_value()) continue;
      if (filterMatchedAseConfiguration(
              cfg.value().aseConfiguration,
              direction_qos_requirement.value().aseConfiguration) &&
          isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
                                  direction_qos_requirement.value())) {
        temp.push_back(cfg.value());
      }
    }
    LOG(WARNING) << __func__ << ": Got " << temp.size()
                 << " configs, start matching allocation";

    int qos_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask(
        direction_qos_requirement.value().aseConfiguration);
    // Get the best matching config based on channel allocation
    auto req_valid_configs = getValidConfigurationsFromAllocation(
        qos_allocation_bitmask, temp, is_exact);
    if (req_valid_configs.empty()) {
      LOG(WARNING) << __func__
                   << ": Cannot find matching allocation for bitmask "
                   << qos_allocation_bitmask;

    } else {
      return req_valid_configs[0].qosConfiguration;
    }
  }

  return std::nullopt;
}

ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
    const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
        in_qosRequirement,
    IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return) {
  IBluetoothAudioProvider::LeAudioAseQosConfigurationPair result;

  // Get all configuration settings
  std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
      ase_configuration_settings =
          BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();

  // Direction QoS matching
  // Only handle one direction input case
  if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
    if (!isValidQosRequirement(in_qosRequirement.sinkAseQosRequirement.value()))
      return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    {
      // Try exact match first
      result.sinkQosConfiguration =
          getDirectionQosConfiguration(kLeAudioDirectionSink, in_qosRequirement,
                                       ase_configuration_settings, true);
      if (!result.sinkQosConfiguration.has_value()) {
        result.sinkQosConfiguration = getDirectionQosConfiguration(
            kLeAudioDirectionSink, in_qosRequirement,
            ase_configuration_settings, false);
      }
    }
  }
  if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
    if (!isValidQosRequirement(
            in_qosRequirement.sourceAseQosRequirement.value()))
      return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    result.sourceQosConfiguration =
        getDirectionQosConfiguration(kLeAudioDirectionSource, in_qosRequirement,
                                     ase_configuration_settings, true);
    if (!result.sourceQosConfiguration.has_value()) {
      result.sourceQosConfiguration = getDirectionQosConfiguration(
          kLeAudioDirectionSource, in_qosRequirement,
          ase_configuration_settings, false);
    }
  }

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

ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSinkAseMetadataChanged(
    IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
    int32_t /*in_cisId*/,
    const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
  (void)in_state;
  (void)in_metadata;
  return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};

ndk::ScopedAStatus LeAudioOffloadAudioProvider::onSourceAseMetadataChanged(
    IBluetoothAudioProvider::AseState in_state, int32_t /*in_cigId*/,
    int32_t /*in_cisId*/,
    const std::optional<std::vector<std::optional<MetadataLtv>>>& in_metadata) {
  (void)in_state;
  (void)in_metadata;
  return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
};

LeAudioBroadcastConfigurationSetting getDefaultBroadcastSetting(
    int context_bitmask, IBluetoothAudioProvider::BroadcastQuality quality) {
  LeAudioBroadcastConfigurationSetting setting;
  setting.retransmitionNum = 4;
  setting.maxTransportLatencyMs = 60;
  setting.sduIntervalUs = 10000;
  setting.maxSduOctets = 40;

  if (quality == IBluetoothAudioProvider::BroadcastQuality::HIGH) {
    LOG(INFO) << __func__ << ": High quality, returning high quality settings";
    setting.retransmitionNum = 4;
    setting.maxTransportLatencyMs = 65;
    setting.maxSduOctets = 200;
    return setting;
  }

  // Populate other settings base on context
  // TODO: Populate with better design
  if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME)) {
    setting.retransmitionNum = 2;
    setting.maxTransportLatencyMs = 10;
    setting.maxSduOctets = 120;
  } else if (context_bitmask & (AudioContext::INSTRUCTIONAL)) {
    setting.retransmitionNum = 2;
    setting.maxTransportLatencyMs = 10;
    setting.maxSduOctets = 40;
  } else if (context_bitmask &
             (AudioContext::SOUND_EFFECTS | AudioContext::UNSPECIFIED)) {
    setting.retransmitionNum = 4;
    setting.maxTransportLatencyMs = 60;
    setting.maxSduOctets = 80;
  } else if (context_bitmask &
             (AudioContext::ALERTS | AudioContext::NOTIFICATIONS |
              AudioContext::EMERGENCY_ALARM)) {
    setting.retransmitionNum = 4;
    setting.maxTransportLatencyMs = 60;
    setting.maxSduOctets = 40;
  } else if (context_bitmask & AudioContext::MEDIA) {
    setting.retransmitionNum = 4;
    setting.maxTransportLatencyMs = 60;
    setting.maxSduOctets = 120;
  }

  return setting;
}
void modifySubBISConfigAllocation(
    IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration& sub_bis_cfg,
    int allocation_bitmask) {
  for (auto& codec_cfg : sub_bis_cfg.bisConfiguration.codecConfiguration) {
    if (codec_cfg.getTag() ==
        CodecSpecificConfigurationLtv::audioChannelAllocation) {
      codec_cfg.get<CodecSpecificConfigurationLtv::audioChannelAllocation>()
          .bitmask = allocation_bitmask;
      break;
    }
  }
}
void modifySubgroupConfiguration(
    IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration&
        subgroup_cfg,
    int context_bitmask) {
  // STEREO configs
  // Split into 2 sub BIS config, each has numBis = 1
  if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME |
                         AudioContext::SOUND_EFFECTS |
                         AudioContext::UNSPECIFIED | AudioContext::MEDIA)) {
    if (subgroup_cfg.bisConfigurations.size() == 1)
      subgroup_cfg.bisConfigurations.push_back(
          subgroup_cfg.bisConfigurations[0]);

    subgroup_cfg.bisConfigurations[0].numBis = 1;
    modifySubBISConfigAllocation(
        subgroup_cfg.bisConfigurations[0],
        CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT);

    subgroup_cfg.bisConfigurations[1].numBis = 1;
    modifySubBISConfigAllocation(
        subgroup_cfg.bisConfigurations[1],
        CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT);
    return;
  }

  // MONO configs
  for (auto& sub_bis_cfg : subgroup_cfg.bisConfigurations) {
    sub_bis_cfg.numBis = 1;
    modifySubBISConfigAllocation(
        sub_bis_cfg,
        CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER);
  }
}

void LeAudioOffloadAudioProvider::getBroadcastSettings() {
  if (!broadcast_settings.empty()) return;

  LOG(INFO) << __func__
            << ": Loading basic broadcast settings from provider info";

  std::vector<CodecInfo> db_codec_info =
      BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
          SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
  for (auto x : db_codec_info) {
    LOG(INFO) << __func__ << ": codec info = " << x.toString();
  }
  broadcast_settings.clear();

  // Default value population
  CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
  default_allocation.bitmask =
      CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
  CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU default_frame;
  default_frame.value = 1;

  for (auto& codec_info : db_codec_info) {
    if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
      continue;
    auto& transport = codec_info.transport.get<CodecInfo::Transport::leAudio>();
    LeAudioBroadcastConfigurationSetting setting;
    setting.retransmitionNum = 4;
    setting.maxTransportLatencyMs = 60;
    setting.sduIntervalUs = 10000;
    setting.maxSduOctets = 40;
    // Default setting
    setting.numBis = 1;
    setting.phy = {Phy::TWO_M};
    // Populate BIS configuration info using codec_info
    LeAudioBisConfiguration bis_cfg;
    bis_cfg.codecId = codec_info.id;

    CodecSpecificConfigurationLtv::OctetsPerCodecFrame octets;
    octets.value = transport.bitdepth[0];

    bis_cfg.codecConfiguration = {
        sampling_freq_map[transport.samplingFrequencyHz[0]],
        octets,
        frame_duration_map[transport.frameDurationUs[0]],
        default_allocation,
        default_frame,
    };

    // Ignore bis_cfg.metadata

    // Add information to structure
    IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
    sub_bis_cfg.numBis = 1;
    sub_bis_cfg.bisConfiguration = bis_cfg;
    IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
    // Populate the same sub config
    sub_cfg.bisConfigurations = {sub_bis_cfg};
    setting.subgroupsConfigurations = {sub_cfg};

    broadcast_settings.push_back(setting);
  }

  LOG(INFO) << __func__
            << ": Done loading broadcast settings from provider info";
}

/* 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<LeAudioBroadcastConfigurationSetting>
LeAudioOffloadAudioProvider::
    getCapabilitiesMatchedBroadcastConfigurationSettings(
        LeAudioBroadcastConfigurationSetting& setting,
        const IBluetoothAudioProvider::LeAudioDeviceCapabilities&
            capabilities) {
  std::vector<IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration>
      filter_subgroup;
  for (auto& sub_cfg : setting.subgroupsConfigurations) {
    std::vector<IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration>
        filtered_bis_cfg;
    for (auto& bis_cfg : sub_cfg.bisConfigurations)
      if (isMatchedBISConfiguration(bis_cfg.bisConfiguration, capabilities)) {
        filtered_bis_cfg.push_back(bis_cfg);
      }
    if (!filtered_bis_cfg.empty()) {
      IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration
          subgroup_cfg;
      subgroup_cfg.bisConfigurations = filtered_bis_cfg;
      filter_subgroup.push_back(subgroup_cfg);
    }
  }
  if (filter_subgroup.empty()) return std::nullopt;

  // Create a new LeAudioAseConfigurationSetting and return
  LeAudioBroadcastConfigurationSetting filtered_setting(setting);
  filtered_setting.subgroupsConfigurations = filter_subgroup;

  return filtered_setting;
}

std::vector<CodecSpecificConfigurationLtv> getCodecRequirementBasedOnContext(
    int context_bitmask, IBluetoothAudioProvider::BroadcastQuality quality) {
  // Default requirement: lc3_stereo_16_2
  std::vector<CodecSpecificConfigurationLtv> requirement = {
      CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
      CodecSpecificConfigurationLtv::FrameDuration::US10000,
  };

  if (quality == IBluetoothAudioProvider::BroadcastQuality::HIGH) {
    LOG(INFO) << __func__
              << ": High quality, returning high quality requirement";
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
    return requirement;
  }

  if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME)) {
    // lc3_stereo_24_2_1
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
  } else if (context_bitmask & (AudioContext::INSTRUCTIONAL)) {
    // lc3_mono_16_2
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
  } else if (context_bitmask &
             (AudioContext::SOUND_EFFECTS | AudioContext::UNSPECIFIED)) {
    // lc3_stereo_16_2
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
  } else if (context_bitmask &
             (AudioContext::ALERTS | AudioContext::NOTIFICATIONS |
              AudioContext::EMERGENCY_ALARM)) {
    // Default requirement: lc3_stereo_16_2
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
  } else if (context_bitmask & AudioContext::MEDIA) {
    // Default requirement: lc3_stereo_16_2
    // Return the 48k requirement
    requirement = {
        CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000,
        CodecSpecificConfigurationLtv::FrameDuration::US10000,
    };
  }
  return requirement;
}

bool LeAudioOffloadAudioProvider::isSubgroupConfigurationMatchedContext(
    AudioContext requirement_context,
    IBluetoothAudioProvider::BroadcastQuality quality,
    LeAudioBroadcastSubgroupConfiguration configuration) {
  // Find any valid context metadata in the bisConfigurations
  // assuming the bis configuration in the same bis subgroup
  // will have the same context metadata
  std::optional<AudioContext> config_context = std::nullopt;

  auto codec_requirement =
      getCodecRequirementBasedOnContext(requirement_context.bitmask, quality);
  std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv>
      req_tag_map;
  for (auto x : codec_requirement) req_tag_map[x.getTag()] = x;

  for (auto& bis_cfg : configuration.bisConfigurations) {
    // Check every sub_bis_cfg to see which match
    for (auto& x : bis_cfg.bisConfiguration.codecConfiguration) {
      auto p = req_tag_map.find(x.getTag());
      if (p == req_tag_map.end()) continue;
      if (p->second != x) {
        LOG(WARNING) << __func__ << ": does not match for context "
                     << requirement_context.toString()
                     << ", cfg = " << x.toString();
        return false;
      }
    }
  }
  return true;
}

ndk::ScopedAStatus
LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
    const std::optional<std::vector<
        std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
        in_remoteSinkAudioCapabilities,
    const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
        in_requirement,
    LeAudioBroadcastConfigurationSetting* _aidl_return) {
  if (in_requirement.subgroupConfigurationRequirements.empty()) {
    LOG(WARNING) << __func__ << ": Empty requirement";
    return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
  }

  // Broadcast setting are from provider info
  // We will allow empty capability input, match all settings with requirements.
  getBroadcastSettings();
  std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
  if (!in_remoteSinkAudioCapabilities.has_value() ||
      in_remoteSinkAudioCapabilities.value().empty()) {
    LOG(INFO) << __func__ << ": Empty capability, get all broadcast settings";
    filtered_settings = broadcast_settings;
  } else {
    for (auto& setting : broadcast_settings) {
      for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
        if (!capability.has_value()) continue;
        auto filtered_setting =
            getCapabilitiesMatchedBroadcastConfigurationSettings(
                setting, capability.value());
        if (filtered_setting.has_value())
          filtered_settings.push_back(filtered_setting.value());
      }
    }
  }

  if (filtered_settings.empty()) {
    LOG(WARNING) << __func__ << ": Cannot match any remote capability";
    return ndk::ScopedAStatus::ok();
  }

  if (in_requirement.subgroupConfigurationRequirements.empty()) {
    LOG(INFO) << __func__ << ": Empty requirement";
    *_aidl_return = filtered_settings[0];
    return ndk::ScopedAStatus::ok();
  }

  // For each subgroup config requirement, find a suitable subgroup config.
  // Gather these suitable subgroup config in an array.
  // If the setting can satisfy all requirement, we can return the setting
  // with the filtered array.

  auto context_bitmask =
      in_requirement.subgroupConfigurationRequirements[0].audioContext.bitmask;
  auto quality = in_requirement.subgroupConfigurationRequirements[0].quality;
  LeAudioBroadcastConfigurationSetting return_setting =
      getDefaultBroadcastSetting(context_bitmask, quality);
  // Default setting
  return_setting.numBis = 0;
  return_setting.subgroupsConfigurations = {};

  LeAudioDataPathConfiguration path;
  path.isoDataPathConfiguration.isTransparent = true;
  path.dataPathId = kIsoDataPathPlatformDefault;

  // Each subreq, find a setting that match
  for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
    bool is_setting_matched = false;
    for (auto setting : filtered_settings) {
      bool is_matched = true;
      // Check if every sub BIS config satisfy
      for (auto& sub_group_config : setting.subgroupsConfigurations) {
        if (!isSubgroupConfigurationMatchedContext(
                sub_req.audioContext, sub_req.quality, sub_group_config)) {
          is_matched = false;
          break;
        }
        path.isoDataPathConfiguration.codecId =
            sub_group_config.bisConfigurations[0].bisConfiguration.codecId;
        // Also modify the subgroup config to match the context
        modifySubgroupConfiguration(sub_group_config, context_bitmask);
      }

      if (is_matched) {
        is_setting_matched = true;
        for (auto& sub_group_config : setting.subgroupsConfigurations)
          return_setting.subgroupsConfigurations.push_back(sub_group_config);
        break;
      }
    }

    if (!is_setting_matched) {
      LOG(WARNING) << __func__
                   << ": Cannot find a setting that match requirement "
                   << sub_req.toString();
      return ndk::ScopedAStatus::ok();
    }
  }

  // Populate all numBis
  for (auto& sub_group_config : return_setting.subgroupsConfigurations) {
    for (auto& sub_bis_config : sub_group_config.bisConfigurations) {
      return_setting.numBis += sub_bis_config.numBis;
    }
  }
  return_setting.phy = std::vector<Phy>(return_setting.numBis, Phy::TWO_M);
  // Populate data path config
  return_setting.dataPathConfiguration = path;
  // TODO: Workaround for STEREO configs maxSduOctets being doubled
  if (context_bitmask & (AudioContext::LIVE_AUDIO | AudioContext::GAME |
                         AudioContext::SOUND_EFFECTS |
                         AudioContext::UNSPECIFIED | AudioContext::MEDIA)) {
    return_setting.maxSduOctets /= 2;
  }
  LOG(INFO) << __func__
            << ": Combined setting that match: " << return_setting.toString();
  *_aidl_return = return_setting;
  return ndk::ScopedAStatus::ok();
};

}  // namespace audio
}  // namespace bluetooth
}  // namespace hardware
}  // namespace android
}  // namespace aidl
