Multi-directional support for matching ASE configuration
Bug: 320259251
Test: ateast VtsHalBluetoothAudioTargetTest
Tag: feature
Change-Id: I478fc99bb02debd78f660dc27e9600ef3afdc2a9
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
index dc36ac0..2a2cab0 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
@@ -184,12 +184,13 @@
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
std::vector<CodecInfo> db_codec_info =
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(session_type);
- if (!db_codec_info.empty()) {
- auto& provider_info = _aidl_return->emplace();
- provider_info.name = kLeAudioOffloadProviderName;
- provider_info.codecInfos = db_codec_info;
- return ndk::ScopedAStatus::ok();
- }
+ // Return provider info supports without checking db_codec_info
+ // This help with various flow implementation for multidirectional support.
+ auto& provider_info = _aidl_return->emplace();
+ provider_info.supportsMultidirectionalCapabilities = true;
+ provider_info.name = kLeAudioOffloadProviderName;
+ provider_info.codecInfos = db_codec_info;
+ return ndk::ScopedAStatus::ok();
}
if (session_type == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH) {
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index a692d84..5002a1a 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -165,8 +165,8 @@
return cfg_codec == req_codec;
}
-bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext(
- AudioContext setting_context,
+bool LeAudioOffloadAudioProvider::filterCapabilitiesMatchedContext(
+ AudioContext& setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
// If has no metadata, assume match
if (!capabilities.metadata.has_value()) return true;
@@ -178,7 +178,11 @@
auto& context = metadata.value()
.get<MetadataLtv::Tag::preferredAudioContexts>()
.values;
- if (setting_context.bitmask & context.bitmask) return true;
+ if (setting_context.bitmask & context.bitmask) {
+ // New mask with matched capability
+ setting_context.bitmask &= context.bitmask;
+ return true;
+ }
}
}
@@ -189,8 +193,12 @@
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);
+ 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;
}
@@ -198,9 +206,11 @@
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);
+ 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;
}
@@ -240,50 +250,67 @@
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;
+ // If capability has this tag, but our configuration doesn't
+ // Then we will assume it is matched
+ if (cfg == cfg_tag_map.end()) {
+ continue;
+ }
- // 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;
+ 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;
+ }
}
}
@@ -298,11 +325,16 @@
if (requirement_cfg.codecId.has_value()) {
if (!setting_cfg.codecId.has_value()) return false;
if (!isMatchedValidCodec(setting_cfg.codecId.value(),
- requirement_cfg.codecId.value()))
+ requirement_cfg.codecId.value())) {
return false;
+ }
}
- if (setting_cfg.targetLatency != requirement_cfg.targetLatency) return false;
+ if (requirement_cfg.targetLatency ==
+ LeAudioAseConfiguration::TargetLatency::UNDEFINED ||
+ setting_cfg.targetLatency != requirement_cfg.targetLatency) {
+ return false;
+ }
// Ignore PHY requirement
// Check all codec configuration
@@ -314,9 +346,13 @@
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 == cfg_tag_map.end()) {
+ return false;
+ }
- if (cfg->second != requirement_cfg) return false;
+ if (cfg->second != requirement_cfg) {
+ return false;
+ }
}
// Ignore vendor configuration and metadata requirement
@@ -326,10 +362,13 @@
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))
+ if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) {
return false;
+ }
+ if (!isCapabilitiesMatchedCodecConfiguration(
+ bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities)) {
+ return false;
+ }
return true;
}
@@ -357,31 +396,35 @@
}
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
- std::vector<std::optional<AseDirectionConfiguration>>&
+ std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
direction_configurations,
- const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
- requirements,
- std::vector<std::optional<AseDirectionConfiguration>>&
+ const std::vector<std::optional<AseDirectionRequirement>>& requirements,
+ std::optional<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 (!valid_direction_configurations.has_value()) {
+ valid_direction_configurations =
+ std::vector<std::optional<AseDirectionConfiguration>>();
+ }
+ // For every requirement, find the matched ase configuration
+ if (!direction_configurations.has_value()) return;
+ for (auto& requirement : requirements) {
+ if (!requirement.has_value()) continue;
+ for (auto direction_configuration : direction_configurations.value()) {
+ if (!direction_configuration.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);
+ valid_direction_configurations.value().push_back(direction_configuration);
break;
}
}
+ // Ensure that each requirement will have one direction configurations
+ if (valid_direction_configurations.value().empty() ||
+ (valid_direction_configurations.value().size() != requirements.size())) {
+ valid_direction_configurations = std::nullopt;
+ }
}
/* Get a new LeAudioAseConfigurationSetting by matching a setting with a
@@ -392,8 +435,18 @@
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
uint8_t direction) {
+ // Create a new LeAudioAseConfigurationSetting and return
+ // For other direction will contain all settings
+ LeAudioAseConfigurationSetting filtered_setting{
+ .audioContext = setting.audioContext,
+ .sinkAseConfiguration = setting.sinkAseConfiguration,
+ .sourceAseConfiguration = setting.sourceAseConfiguration,
+ .flags = setting.flags,
+ .packing = setting.packing,
+ };
// Try to match context in metadata.
- if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities))
+ if (!filterCapabilitiesMatchedContext(filtered_setting.audioContext,
+ capabilities))
return std::nullopt;
// Get a list of all matched AseDirectionConfiguration
@@ -401,28 +454,30 @@
std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration = nullptr;
if (direction == kLeAudioDirectionSink) {
- if (!setting.sinkAseConfiguration.has_value()) return std::nullopt;
- direction_configuration = &setting.sinkAseConfiguration.value();
+ if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt;
+ direction_configuration = &filtered_setting.sinkAseConfiguration.value();
} else {
- if (!setting.sourceAseConfiguration.has_value()) return std::nullopt;
- direction_configuration = &setting.sourceAseConfiguration.value();
+ 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);
- if (valid_direction_configuration.empty()) return std::nullopt;
+
+ // No valid configuration for this direction
+ 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;
+ // For other direction will contain all settings
if (direction == kLeAudioDirectionSink) {
filtered_setting.sinkAseConfiguration = valid_direction_configuration;
} else {
filtered_setting.sourceAseConfiguration = valid_direction_configuration;
}
- filtered_setting.flags = setting.flags;
return filtered_setting;
}
@@ -436,41 +491,49 @@
const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
requirement) {
// Try to match context in metadata.
- if (setting.audioContext != requirement.audioContext) return std::nullopt;
+ if ((setting.audioContext.bitmask & requirement.audioContext.bitmask) !=
+ requirement.audioContext.bitmask)
+ return std::nullopt;
+
+ // Further filter setting's context
+ setting.audioContext.bitmask &= requirement.audioContext.bitmask;
// 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;
+
+ // Create a new LeAudioAseConfigurationSetting to return
+ LeAudioAseConfigurationSetting filtered_setting{
+ .audioContext = setting.audioContext,
+ .packing = setting.packing,
+ .flags = setting.flags,
+ };
+
+ if (requirement.sinkAseRequirement.has_value()) {
+ filterRequirementAseDirectionConfiguration(
+ setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
+ filtered_setting.sinkAseConfiguration);
+ if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt;
}
- 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;
+ 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;
}
+// For each requirement, a valid ASE configuration will satify:
+// - matched with any sink capability (if presented)
+// - OR matched with any 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>>>&
@@ -487,46 +550,81 @@
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;
+ if (!in_remoteSinkAudioCapabilities.has_value() &&
+ !in_remoteSourceAudioCapabilities.has_value()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ // Each setting consist of source and sink AseDirectionConfiguration vector
+ // Filter every sink capability
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());
+ matched_ase_configuration_settings;
+
+ if (in_remoteSinkAudioCapabilities.has_value()) {
+ // Matching each setting with any remote capabilities
+ 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()) {
+ matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ }
}
}
}
- // Matching with requirements
+ // Combine filter every source capability
+ if (in_remoteSourceAudioCapabilities.has_value()) {
+ // Matching each setting with any remote capabilities
+ for (auto& setting : ase_configuration_settings) {
+ for (auto& capability : in_remoteSourceAudioCapabilities.value()) {
+ if (!capability.has_value()) continue;
+ auto filtered_ase_configuration_setting =
+ getCapabilitiesMatchedAseConfigurationSettings(
+ setting, capability.value(), kLeAudioDirectionSource);
+ if (filtered_ase_configuration_setting.has_value()) {
+ // Put into the same list
+ // possibly duplicated, filtered by requirement later
+ matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ }
+ }
+ }
+ }
+
+ if (matched_ase_configuration_settings.empty()) {
+ LOG(WARNING) << __func__ << ": No setting matched the capability";
+ return ndk::ScopedAStatus::ok();
+ }
+ // Each requirement will match with a valid setting
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
- for (auto& setting : capability_matched_ase_configuration_settings) {
- for (auto& requirement : in_requirements) {
+ for (auto& requirement : in_requirements) {
+ LOG(INFO) << __func__ << ": Trying to match for the requirement "
+ << requirement.toString();
+ bool is_matched = false;
+
+ for (auto& setting : matched_ase_configuration_settings) {
auto filtered_ase_configuration_setting =
getRequirementMatchedAseConfigurationSettings(setting, requirement);
if (filtered_ase_configuration_setting.has_value()) {
result.push_back(filtered_ase_configuration_setting.value());
+ LOG(INFO) << __func__ << ": Result = "
+ << filtered_ase_configuration_setting.value().toString();
+ // Found a matched setting, ignore other settings
+ is_matched = true;
+ break;
}
}
+ if (!is_matched) {
+ // If cannot satisfy this requirement, return an empty result
+ LOG(WARNING) << __func__ << ": Cannot match the requirement "
+ << requirement.toString();
+ result.clear();
+ break;
+ }
}
*_aidl_return = result;
@@ -537,41 +635,45 @@
LeAudioAseQosConfiguration setting_qos,
AseQosDirectionRequirement requirement_qos) {
if (setting_qos.retransmissionNum !=
- requirement_qos.preferredRetransmissionNum)
+ requirement_qos.preferredRetransmissionNum) {
return false;
- if (setting_qos.maxTransportLatencyMs > requirement_qos.maxTransportLatencyMs)
+ }
+ if (setting_qos.maxTransportLatencyMs >
+ requirement_qos.maxTransportLatencyMs) {
return false;
+ }
// Ignore other parameters, as they are not populated in the setting_qos
return true;
}
-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();
+bool isValidQosRequirement(AseQosDirectionRequirement qosRequirement) {
+ return ((qosRequirement.maxTransportLatencyMs > 0) &&
+ (qosRequirement.presentationDelayMaxUs > 0) &&
+ (qosRequirement.presentationDelayMaxUs >=
+ qosRequirement.presentationDelayMinUs));
+}
- // Direction QoS matching
- // Only handle one direction input case
- uint8_t direction = 0;
+std::optional<LeAudioAseQosConfiguration>
+LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
+ uint8_t direction,
+ const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
+ qosRequirement,
+ std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings) {
std::optional<AseQosDirectionRequirement> direction_qos_requirement =
std::nullopt;
- if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
- direction_qos_requirement = in_qosRequirement.sinkAseQosRequirement.value();
- direction = kLeAudioDirectionSink;
- } else if (in_qosRequirement.sourceAseQosRequirement.has_value()) {
- direction_qos_requirement =
- in_qosRequirement.sourceAseQosRequirement.value();
- direction = kLeAudioDirectionSource;
+
+ // 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 != in_qosRequirement.audioContext) continue;
+ if ((setting.audioContext.bitmask & qosRequirement.audioContext.bitmask) !=
+ qosRequirement.audioContext.bitmask)
+ continue;
// Match configuration flags
// Currently configuration flags are not populated, ignore.
@@ -592,10 +694,7 @@
if (!cfg.has_value()) continue;
// If no requirement, return the first QoS
if (!direction_qos_requirement.has_value()) {
- result.sinkQosConfiguration = cfg.value().qosConfiguration;
- result.sourceQosConfiguration = cfg.value().qosConfiguration;
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
+ return cfg.value().qosConfiguration;
}
// If has requirement, return the first matched QoS
@@ -607,17 +706,41 @@
direction_qos_requirement.value().aseConfiguration) &&
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
direction_qos_requirement.value())) {
- if (direction == kLeAudioDirectionSink)
- result.sinkQosConfiguration = cfg.value().qosConfiguration;
- else
- result.sourceQosConfiguration = cfg.value().qosConfiguration;
- *_aidl_return = result;
- return ndk::ScopedAStatus::ok();
+ return cfg.value().qosConfiguration;
}
}
}
- // No match, return empty QoS
+ 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);
+ result.sinkQosConfiguration = getDirectionQosConfiguration(
+ kLeAudioDirectionSink, in_qosRequirement, ase_configuration_settings);
+ }
+ 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);
+ }
+
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
};
@@ -649,9 +772,13 @@
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
broadcast_settings.clear();
+
+ // Default value for unmapped fields
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
default_allocation.bitmask =
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
+ CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU default_frame;
+ default_frame.value = 2;
for (auto& codec_info : db_codec_info) {
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
@@ -669,15 +796,20 @@
octets.value = transport.bitdepth[0];
bis_cfg.codecConfiguration = {
- sampling_freq_map[transport.samplingFrequencyHz[0]], octets,
- frame_duration_map[transport.frameDurationUs[0]], default_allocation};
+ sampling_freq_map[transport.samplingFrequencyHz[0]],
+ octets,
+ frame_duration_map[transport.frameDurationUs[0]],
+ default_allocation,
+ default_frame,
+ };
// Add information to structure
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
- sub_bis_cfg.numBis = 1;
+ sub_bis_cfg.numBis = 2;
sub_bis_cfg.bisConfiguration = bis_cfg;
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_cfg;
- sub_cfg.bisConfigurations = {sub_bis_cfg};
+ // Populate the same sub config
+ sub_cfg.bisConfigurations = {sub_bis_cfg, sub_bis_cfg};
setting.subgroupsConfigurations = {sub_cfg};
broadcast_settings.push_back(setting);
@@ -721,6 +853,36 @@
return filtered_setting;
}
+bool LeAudioOffloadAudioProvider::isSubgroupConfigurationMatchedContext(
+ AudioContext setting_context,
+ 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;
+
+ for (auto& p : configuration.bisConfigurations)
+ if (p.bisConfiguration.metadata.has_value()) {
+ bool is_context_found = false;
+ for (auto& metadata : p.bisConfiguration.metadata.value()) {
+ if (!metadata.has_value()) continue;
+ if (metadata.value().getTag() ==
+ MetadataLtv::Tag::preferredAudioContexts) {
+ config_context = metadata.value()
+ .get<MetadataLtv::Tag::preferredAudioContexts>()
+ .values;
+ is_context_found = true;
+ break;
+ }
+ }
+ if (is_context_found) break;
+ }
+
+ // Not found context metadata in any of the bis configuration, assume matched
+ if (!config_context.has_value()) return true;
+ return (setting_context.bitmask & config_context.value().bitmask);
+}
+
ndk::ScopedAStatus
LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
const std::optional<std::vector<
@@ -729,23 +891,28 @@
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement,
LeAudioBroadcastConfigurationSetting* _aidl_return) {
- getBroadcastSettings();
- _aidl_return = nullptr;
+ if (in_requirement.subgroupConfigurationRequirements.empty()) {
+ LOG(WARNING) << __func__ << ": Empty requirement";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
- // Match and filter capability
+ // 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()) {
- LOG(WARNING) << __func__ << ": Empty capability";
- return ndk::ScopedAStatus::ok();
- }
- 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());
+ 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());
+ }
}
}
@@ -754,36 +921,52 @@
return ndk::ScopedAStatus::ok();
}
- // Match and return the first matched requirement
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.
for (auto& setting : filtered_settings) {
- // Further filter out bis configuration
- LeAudioBroadcastConfigurationSetting filtered_setting(setting);
- filtered_setting.subgroupsConfigurations.clear();
- for (auto& sub_cfg : setting.subgroupsConfigurations) {
- bool isMatched = false;
- for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
+ LeAudioBroadcastConfigurationSetting matched_setting(setting);
+ matched_setting.subgroupsConfigurations.clear();
+ auto total_num_bis = 0;
+
+ bool matched_all_requirement = true;
+
+ for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
+ bool is_matched = false;
+ for (auto& sub_cfg : setting.subgroupsConfigurations) {
+ // Match the context
+ if (!isSubgroupConfigurationMatchedContext(sub_req.audioContext,
+ sub_cfg))
+ continue;
// Matching number of BIS
if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size())
continue;
- // Currently will ignore quality and context hint.
- isMatched = true;
+ // Currently will ignore quality matching.
+ matched_setting.subgroupsConfigurations.push_back(sub_cfg);
+ total_num_bis += sub_cfg.bisConfigurations.size();
+ is_matched = true;
break;
}
- if (isMatched)
- filtered_setting.subgroupsConfigurations.push_back(sub_cfg);
+ // There is an unmatched requirement, this setting cannot be used
+ if (!is_matched) {
+ matched_all_requirement = false;
+ break;
+ }
}
- // Return the first match
- if (!filtered_setting.subgroupsConfigurations.empty()) {
- LOG(INFO) << __func__ << ": Matched requirement";
- *_aidl_return = filtered_setting;
- return ndk::ScopedAStatus::ok();
- }
+
+ if (!matched_all_requirement) continue;
+
+ matched_setting.numBis = total_num_bis;
+ // Return the filtered setting if all requirement satified
+ *_aidl_return = matched_setting;
+ return ndk::ScopedAStatus::ok();
}
LOG(WARNING) << __func__ << ": Cannot match any requirement";
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
index 2785e7f..06cd405 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
@@ -96,8 +96,8 @@
// Private matching function definitions
bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec);
- bool isCapabilitiesMatchedContext(
- AudioContext setting_context,
+ bool filterCapabilitiesMatchedContext(
+ AudioContext& setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
bool isMatchedSamplingFreq(
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
@@ -134,11 +134,10 @@
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations);
void filterRequirementAseDirectionConfiguration(
- std::vector<std::optional<AseDirectionConfiguration>>&
+ std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
direction_configurations,
- const std::optional<std::vector<std::optional<AseDirectionRequirement>>>&
- requirements,
- std::vector<std::optional<AseDirectionConfiguration>>&
+ const std::vector<std::optional<AseDirectionRequirement>>& requirements,
+ std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
valid_direction_configurations);
std::optional<LeAudioAseConfigurationSetting>
getCapabilitiesMatchedAseConfigurationSettings(
@@ -157,6 +156,14 @@
LeAudioBroadcastConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
void getBroadcastSettings();
+ std::optional<LeAudioAseQosConfiguration> getDirectionQosConfiguration(
+ uint8_t direction,
+ const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
+ qosRequirement,
+ std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings);
+ bool isSubgroupConfigurationMatchedContext(
+ AudioContext requirement_context,
+ LeAudioBroadcastSubgroupConfiguration configuration);
};
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {