Multi-directional support for matching ASE configuration

Bug: 320259251
Test: ateast VtsHalBluetoothAudioTargetTest
Tag: feature
Change-Id: I478fc99bb02debd78f660dc27e9600ef3afdc2a9
This commit is contained in:
Bao Do 2024-05-09 14:21:01 +08:00
parent 5a43cb90d8
commit 91d7ca2b67
3 changed files with 403 additions and 212 deletions

View file

@ -184,12 +184,13 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo(
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) { SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
std::vector<CodecInfo> db_codec_info = std::vector<CodecInfo> db_codec_info =
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(session_type); BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(session_type);
if (!db_codec_info.empty()) { // Return provider info supports without checking db_codec_info
auto& provider_info = _aidl_return->emplace(); // This help with various flow implementation for multidirectional support.
provider_info.name = kLeAudioOffloadProviderName; auto& provider_info = _aidl_return->emplace();
provider_info.codecInfos = db_codec_info; provider_info.supportsMultidirectionalCapabilities = true;
return ndk::ScopedAStatus::ok(); provider_info.name = kLeAudioOffloadProviderName;
} provider_info.codecInfos = db_codec_info;
return ndk::ScopedAStatus::ok();
} }
if (session_type == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH) { if (session_type == SessionType::HFP_HARDWARE_OFFLOAD_DATAPATH) {

View file

@ -165,8 +165,8 @@ bool LeAudioOffloadAudioProvider::isMatchedValidCodec(CodecId cfg_codec,
return cfg_codec == req_codec; return cfg_codec == req_codec;
} }
bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext( bool LeAudioOffloadAudioProvider::filterCapabilitiesMatchedContext(
AudioContext setting_context, AudioContext& setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) { const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
// If has no metadata, assume match // If has no metadata, assume match
if (!capabilities.metadata.has_value()) return true; if (!capabilities.metadata.has_value()) return true;
@ -178,7 +178,11 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext(
auto& context = metadata.value() auto& context = metadata.value()
.get<MetadataLtv::Tag::preferredAudioContexts>() .get<MetadataLtv::Tag::preferredAudioContexts>()
.values; .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 @@ bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq(
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq, CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies& CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies&
capability_freq) { capability_freq) {
for (auto [freq, bitmask] : freq_to_support_bitmask_map) auto p = freq_to_support_bitmask_map.find(cfg_freq);
if (cfg_freq == freq) return (capability_freq.bitmask & bitmask); if (p != freq_to_support_bitmask_map.end()) {
if (capability_freq.bitmask & p->second) {
return true;
}
}
return false; return false;
} }
@ -198,9 +206,11 @@ bool LeAudioOffloadAudioProvider::isMatchedFrameDuration(
CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration, CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration,
CodecSpecificCapabilitiesLtv::SupportedFrameDurations& CodecSpecificCapabilitiesLtv::SupportedFrameDurations&
capability_fduration) { capability_fduration) {
for (auto [fduration, bitmask] : fduration_to_support_fduration_map) auto p = fduration_to_support_fduration_map.find(cfg_fduration);
if (cfg_fduration == fduration) if (p != fduration_to_support_fduration_map.end())
return (capability_fduration.bitmask & bitmask); if (capability_fduration.bitmask & p->second) {
return true;
}
return false; return false;
} }
@ -240,50 +250,67 @@ bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration(
for (auto& codec_capability : codec_capabilities) { for (auto& codec_capability : codec_capabilities) {
auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]); auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]);
// Cannot find tag for the capability: // If capability has this tag, but our configuration doesn't
if (cfg == cfg_tag_map.end()) return false; // Then we will assume it is matched
if (cfg == cfg_tag_map.end()) {
continue;
}
// Matching logic for sampling frequency switch (codec_capability.getTag()) {
if (codec_capability.getTag() == case CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies: {
CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies) { if (!isMatchedSamplingFreq(
if (!isMatchedSamplingFreq( cfg->second.get<
cfg->second CodecSpecificConfigurationLtv::Tag::samplingFrequency>(),
.get<CodecSpecificConfigurationLtv::Tag::samplingFrequency>(), codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: supportedSamplingFrequencies>())) {
supportedSamplingFrequencies>())) return false;
return false; }
} else if (codec_capability.getTag() == break;
CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations) { }
if (!isMatchedFrameDuration(
cfg->second case CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations: {
.get<CodecSpecificConfigurationLtv::Tag::frameDuration>(), if (!isMatchedFrameDuration(
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: cfg->second
supportedFrameDurations>())) .get<CodecSpecificConfigurationLtv::Tag::frameDuration>(),
return false; codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
} else if (codec_capability.getTag() == supportedFrameDurations>())) {
CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts) { return false;
if (!isMatchedAudioChannel( }
cfg->second.get< break;
CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>(), }
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedAudioChannelCounts>())) case CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts: {
return false; if (!isMatchedAudioChannel(
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag:: cfg->second.get<CodecSpecificConfigurationLtv::Tag::
supportedMaxCodecFramesPerSDU) { audioChannelAllocation>(),
if (!isMatchedCodecFramesPerSDU( codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
cfg->second.get< supportedAudioChannelCounts>())) {
CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU>(), return false;
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: }
supportedMaxCodecFramesPerSDU>())) break;
return false; }
} else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag::
supportedOctetsPerCodecFrame) { case CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU: {
if (!isMatchedOctetsPerCodecFrame( if (!isMatchedCodecFramesPerSDU(
cfg->second.get< cfg->second.get<CodecSpecificConfigurationLtv::Tag::
CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(), codecFrameBlocksPerSDU>(),
codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: codec_capability.get<CodecSpecificCapabilitiesLtv::Tag::
supportedOctetsPerCodecFrame>())) supportedMaxCodecFramesPerSDU>())) {
return false; 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 @@ bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
if (requirement_cfg.codecId.has_value()) { if (requirement_cfg.codecId.has_value()) {
if (!setting_cfg.codecId.has_value()) return false; if (!setting_cfg.codecId.has_value()) return false;
if (!isMatchedValidCodec(setting_cfg.codecId.value(), if (!isMatchedValidCodec(setting_cfg.codecId.value(),
requirement_cfg.codecId.value())) requirement_cfg.codecId.value())) {
return false; 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 // Ignore PHY requirement
// Check all codec configuration // Check all codec configuration
@ -314,9 +346,13 @@ bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
for (auto requirement_cfg : requirement_cfg.codecConfiguration) { for (auto requirement_cfg : requirement_cfg.codecConfiguration) {
// Directly compare CodecSpecificConfigurationLtv // Directly compare CodecSpecificConfigurationLtv
auto cfg = cfg_tag_map.find(requirement_cfg.getTag()); 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 // Ignore vendor configuration and metadata requirement
@ -326,10 +362,13 @@ bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
bool LeAudioOffloadAudioProvider::isMatchedBISConfiguration( bool LeAudioOffloadAudioProvider::isMatchedBISConfiguration(
LeAudioBisConfiguration bis_cfg, LeAudioBisConfiguration bis_cfg,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) { const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) {
if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) return false; if (!isMatchedValidCodec(bis_cfg.codecId, capabilities.codecId)) {
if (!isCapabilitiesMatchedCodecConfiguration(
bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities))
return false; return false;
}
if (!isCapabilitiesMatchedCodecConfiguration(
bis_cfg.codecConfiguration, capabilities.codecSpecificCapabilities)) {
return false;
}
return true; return true;
} }
@ -357,31 +396,35 @@ void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration(
} }
void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration( void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>& std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
direction_configurations, direction_configurations,
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>& const std::vector<std::optional<AseDirectionRequirement>>& requirements,
requirements, std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations) { valid_direction_configurations) {
for (auto direction_configuration : direction_configurations) { if (!valid_direction_configurations.has_value()) {
if (!requirements.has_value()) { valid_direction_configurations =
// If there's no requirement, all are valid std::vector<std::optional<AseDirectionConfiguration>>();
valid_direction_configurations.push_back(direction_configuration); }
continue; // For every requirement, find the matched ase configuration
} if (!direction_configurations.has_value()) return;
if (!direction_configuration.has_value()) continue; for (auto& requirement : requirements) {
if (!requirement.has_value()) continue;
for (auto& requirement : requirements.value()) { for (auto direction_configuration : direction_configurations.value()) {
if (!requirement.has_value()) continue; if (!direction_configuration.has_value()) continue;
if (!isMatchedAseConfiguration( if (!isMatchedAseConfiguration(
direction_configuration.value().aseConfiguration, direction_configuration.value().aseConfiguration,
requirement.value().aseConfiguration)) requirement.value().aseConfiguration))
continue; continue;
// Valid if match any requirement. // Valid if match any requirement.
valid_direction_configurations.push_back(direction_configuration); valid_direction_configurations.value().push_back(direction_configuration);
break; 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 /* Get a new LeAudioAseConfigurationSetting by matching a setting with a
@ -392,8 +435,18 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting, IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities, const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities,
uint8_t direction) { 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. // Try to match context in metadata.
if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities)) if (!filterCapabilitiesMatchedContext(filtered_setting.audioContext,
capabilities))
return std::nullopt; return std::nullopt;
// Get a list of all matched AseDirectionConfiguration // Get a list of all matched AseDirectionConfiguration
@ -401,28 +454,30 @@ LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings(
std::vector<std::optional<AseDirectionConfiguration>>* std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration = nullptr; direction_configuration = nullptr;
if (direction == kLeAudioDirectionSink) { if (direction == kLeAudioDirectionSink) {
if (!setting.sinkAseConfiguration.has_value()) return std::nullopt; if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt;
direction_configuration = &setting.sinkAseConfiguration.value(); direction_configuration = &filtered_setting.sinkAseConfiguration.value();
} else { } else {
if (!setting.sourceAseConfiguration.has_value()) return std::nullopt; if (!filtered_setting.sourceAseConfiguration.has_value())
direction_configuration = &setting.sourceAseConfiguration.value(); return std::nullopt;
direction_configuration = &filtered_setting.sourceAseConfiguration.value();
} }
std::vector<std::optional<AseDirectionConfiguration>> std::vector<std::optional<AseDirectionConfiguration>>
valid_direction_configuration; valid_direction_configuration;
filterCapabilitiesAseDirectionConfiguration( filterCapabilitiesAseDirectionConfiguration(
*direction_configuration, capabilities, valid_direction_configuration); *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 // Create a new LeAudioAseConfigurationSetting and return
LeAudioAseConfigurationSetting filtered_setting; // For other direction will contain all settings
filtered_setting.audioContext = setting.audioContext;
filtered_setting.packing = setting.packing;
if (direction == kLeAudioDirectionSink) { if (direction == kLeAudioDirectionSink) {
filtered_setting.sinkAseConfiguration = valid_direction_configuration; filtered_setting.sinkAseConfiguration = valid_direction_configuration;
} else { } else {
filtered_setting.sourceAseConfiguration = valid_direction_configuration; filtered_setting.sourceAseConfiguration = valid_direction_configuration;
} }
filtered_setting.flags = setting.flags;
return filtered_setting; return filtered_setting;
} }
@ -436,41 +491,49 @@ LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings(
const IBluetoothAudioProvider::LeAudioConfigurationRequirement& const IBluetoothAudioProvider::LeAudioConfigurationRequirement&
requirement) { requirement) {
// Try to match context in metadata. // 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 // Check requirement for the correct direction
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>* const std::optional<std::vector<std::optional<AseDirectionRequirement>>>*
direction_requirement; direction_requirement;
std::vector<std::optional<AseDirectionConfiguration>>* std::vector<std::optional<AseDirectionConfiguration>>*
direction_configuration; direction_configuration;
if (setting.sinkAseConfiguration.has_value()) {
direction_configuration = &setting.sinkAseConfiguration.value(); // Create a new LeAudioAseConfigurationSetting to return
direction_requirement = &requirement.sinkAseRequirement; LeAudioAseConfigurationSetting filtered_setting{
} else { .audioContext = setting.audioContext,
direction_configuration = &setting.sourceAseConfiguration.value(); .packing = setting.packing,
direction_requirement = &requirement.sourceAseRequirement; .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>> if (requirement.sourceAseRequirement.has_value()) {
valid_direction_configuration; filterRequirementAseDirectionConfiguration(
filterRequirementAseDirectionConfiguration(*direction_configuration, setting.sourceAseConfiguration,
*direction_requirement, requirement.sourceAseRequirement.value(),
valid_direction_configuration); filtered_setting.sourceAseConfiguration);
if (valid_direction_configuration.empty()) return std::nullopt; if (!filtered_setting.sourceAseConfiguration.has_value())
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; 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( ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
const std::optional<std::vector< const std::optional<std::vector<
std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>& std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
@ -487,46 +550,81 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration(
ase_configuration_settings = ase_configuration_settings =
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings(); BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
// Currently won't handle case where both sink and source capabilities if (!in_remoteSinkAudioCapabilities.has_value() &&
// are passed in. Only handle one of them. !in_remoteSourceAudioCapabilities.has_value()) {
const std::optional<std::vector< return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
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;
} }
// Each setting consist of source and sink AseDirectionConfiguration vector
// Filter every sink capability
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
capability_matched_ase_configuration_settings; matched_ase_configuration_settings;
// Matching with remote capabilities
for (auto& setting : ase_configuration_settings) { if (in_remoteSinkAudioCapabilities.has_value()) {
for (auto& capability : in_remoteAudioCapabilities->value()) { // Matching each setting with any remote capabilities
if (!capability.has_value()) continue; for (auto& setting : ase_configuration_settings) {
auto filtered_ase_configuration_setting = for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
getCapabilitiesMatchedAseConfigurationSettings( if (!capability.has_value()) continue;
setting, capability.value(), direction); auto filtered_ase_configuration_setting =
if (filtered_ase_configuration_setting.has_value()) { getCapabilitiesMatchedAseConfigurationSettings(
capability_matched_ase_configuration_settings.push_back( setting, capability.value(), kLeAudioDirectionSink);
filtered_ase_configuration_setting.value()); 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; 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 = auto filtered_ase_configuration_setting =
getRequirementMatchedAseConfigurationSettings(setting, requirement); getRequirementMatchedAseConfigurationSettings(setting, requirement);
if (filtered_ase_configuration_setting.has_value()) { if (filtered_ase_configuration_setting.has_value()) {
result.push_back(filtered_ase_configuration_setting.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; *_aidl_return = result;
@ -537,41 +635,45 @@ bool LeAudioOffloadAudioProvider::isMatchedQosRequirement(
LeAudioAseQosConfiguration setting_qos, LeAudioAseQosConfiguration setting_qos,
AseQosDirectionRequirement requirement_qos) { AseQosDirectionRequirement requirement_qos) {
if (setting_qos.retransmissionNum != if (setting_qos.retransmissionNum !=
requirement_qos.preferredRetransmissionNum) requirement_qos.preferredRetransmissionNum) {
return false; return false;
if (setting_qos.maxTransportLatencyMs > requirement_qos.maxTransportLatencyMs) }
if (setting_qos.maxTransportLatencyMs >
requirement_qos.maxTransportLatencyMs) {
return false; return false;
}
// Ignore other parameters, as they are not populated in the setting_qos // Ignore other parameters, as they are not populated in the setting_qos
return true; return true;
} }
ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration( bool isValidQosRequirement(AseQosDirectionRequirement qosRequirement) {
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement& return ((qosRequirement.maxTransportLatencyMs > 0) &&
in_qosRequirement, (qosRequirement.presentationDelayMaxUs > 0) &&
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair* _aidl_return) { (qosRequirement.presentationDelayMaxUs >=
IBluetoothAudioProvider::LeAudioAseQosConfigurationPair result; qosRequirement.presentationDelayMinUs));
// Get all configuration settings }
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
ase_configuration_settings =
BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings();
// Direction QoS matching std::optional<LeAudioAseQosConfiguration>
// Only handle one direction input case LeAudioOffloadAudioProvider::getDirectionQosConfiguration(
uint8_t direction = 0; uint8_t direction,
const IBluetoothAudioProvider::LeAudioAseQosConfigurationRequirement&
qosRequirement,
std::vector<LeAudioAseConfigurationSetting>& ase_configuration_settings) {
std::optional<AseQosDirectionRequirement> direction_qos_requirement = std::optional<AseQosDirectionRequirement> direction_qos_requirement =
std::nullopt; std::nullopt;
if (in_qosRequirement.sinkAseQosRequirement.has_value()) {
direction_qos_requirement = in_qosRequirement.sinkAseQosRequirement.value(); // Get the correct direction
direction = kLeAudioDirectionSink; if (direction == kLeAudioDirectionSink) {
} else if (in_qosRequirement.sourceAseQosRequirement.has_value()) { direction_qos_requirement = qosRequirement.sinkAseQosRequirement.value();
direction_qos_requirement = } else {
in_qosRequirement.sourceAseQosRequirement.value(); direction_qos_requirement = qosRequirement.sourceAseQosRequirement.value();
direction = kLeAudioDirectionSource;
} }
for (auto& setting : ase_configuration_settings) { for (auto& setting : ase_configuration_settings) {
// Context matching // Context matching
if (setting.audioContext != in_qosRequirement.audioContext) continue; if ((setting.audioContext.bitmask & qosRequirement.audioContext.bitmask) !=
qosRequirement.audioContext.bitmask)
continue;
// Match configuration flags // Match configuration flags
// Currently configuration flags are not populated, ignore. // Currently configuration flags are not populated, ignore.
@ -592,10 +694,7 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
if (!cfg.has_value()) continue; if (!cfg.has_value()) continue;
// If no requirement, return the first QoS // If no requirement, return the first QoS
if (!direction_qos_requirement.has_value()) { if (!direction_qos_requirement.has_value()) {
result.sinkQosConfiguration = cfg.value().qosConfiguration; return cfg.value().qosConfiguration;
result.sourceQosConfiguration = cfg.value().qosConfiguration;
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
} }
// If has requirement, return the first matched QoS // If has requirement, return the first matched QoS
@ -607,17 +706,41 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration(
direction_qos_requirement.value().aseConfiguration) && direction_qos_requirement.value().aseConfiguration) &&
isMatchedQosRequirement(cfg.value().qosConfiguration.value(), isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
direction_qos_requirement.value())) { direction_qos_requirement.value())) {
if (direction == kLeAudioDirectionSink) return cfg.value().qosConfiguration;
result.sinkQosConfiguration = cfg.value().qosConfiguration;
else
result.sourceQosConfiguration = cfg.value().qosConfiguration;
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
} }
} }
} }
// 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; *_aidl_return = result;
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
}; };
@ -649,9 +772,13 @@ void LeAudioOffloadAudioProvider::getBroadcastSettings() {
BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo( BluetoothAudioCodecs::GetLeAudioOffloadCodecInfo(
SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH); SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH);
broadcast_settings.clear(); broadcast_settings.clear();
// Default value for unmapped fields
CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation; CodecSpecificConfigurationLtv::AudioChannelAllocation default_allocation;
default_allocation.bitmask = default_allocation.bitmask =
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER; CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU default_frame;
default_frame.value = 2;
for (auto& codec_info : db_codec_info) { for (auto& codec_info : db_codec_info) {
if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio) if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio)
@ -669,15 +796,20 @@ void LeAudioOffloadAudioProvider::getBroadcastSettings() {
octets.value = transport.bitdepth[0]; octets.value = transport.bitdepth[0];
bis_cfg.codecConfiguration = { bis_cfg.codecConfiguration = {
sampling_freq_map[transport.samplingFrequencyHz[0]], octets, sampling_freq_map[transport.samplingFrequencyHz[0]],
frame_duration_map[transport.frameDurationUs[0]], default_allocation}; octets,
frame_duration_map[transport.frameDurationUs[0]],
default_allocation,
default_frame,
};
// Add information to structure // Add information to structure
IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg; IBluetoothAudioProvider::LeAudioSubgroupBisConfiguration sub_bis_cfg;
sub_bis_cfg.numBis = 1; sub_bis_cfg.numBis = 2;
sub_bis_cfg.bisConfiguration = bis_cfg; sub_bis_cfg.bisConfiguration = bis_cfg;
IBluetoothAudioProvider::LeAudioBroadcastSubgroupConfiguration sub_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}; setting.subgroupsConfigurations = {sub_cfg};
broadcast_settings.push_back(setting); broadcast_settings.push_back(setting);
@ -721,6 +853,36 @@ LeAudioOffloadAudioProvider::
return filtered_setting; 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 ndk::ScopedAStatus
LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration( LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
const std::optional<std::vector< const std::optional<std::vector<
@ -729,23 +891,28 @@ LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement& const IBluetoothAudioProvider::LeAudioBroadcastConfigurationRequirement&
in_requirement, in_requirement,
LeAudioBroadcastConfigurationSetting* _aidl_return) { LeAudioBroadcastConfigurationSetting* _aidl_return) {
getBroadcastSettings(); if (in_requirement.subgroupConfigurationRequirements.empty()) {
_aidl_return = nullptr; 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; std::vector<LeAudioBroadcastConfigurationSetting> filtered_settings;
if (!in_remoteSinkAudioCapabilities.has_value()) { if (!in_remoteSinkAudioCapabilities.has_value()) {
LOG(WARNING) << __func__ << ": Empty capability"; LOG(INFO) << __func__ << ": Empty capability, get all broadcast settings";
return ndk::ScopedAStatus::ok(); filtered_settings = broadcast_settings;
} } else {
for (auto& setting : broadcast_settings) { for (auto& setting : broadcast_settings) {
for (auto& capability : in_remoteSinkAudioCapabilities.value()) { for (auto& capability : in_remoteSinkAudioCapabilities.value()) {
if (!capability.has_value()) continue; if (!capability.has_value()) continue;
auto filtered_setting = auto filtered_setting =
getCapabilitiesMatchedBroadcastConfigurationSettings( getCapabilitiesMatchedBroadcastConfigurationSettings(
setting, capability.value()); setting, capability.value());
if (filtered_setting.has_value()) if (filtered_setting.has_value())
filtered_settings.push_back(filtered_setting.value()); filtered_settings.push_back(filtered_setting.value());
}
} }
} }
@ -754,36 +921,52 @@ LeAudioOffloadAudioProvider::getLeAudioBroadcastConfiguration(
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
// Match and return the first matched requirement
if (in_requirement.subgroupConfigurationRequirements.empty()) { if (in_requirement.subgroupConfigurationRequirements.empty()) {
LOG(INFO) << __func__ << ": Empty requirement"; LOG(INFO) << __func__ << ": Empty requirement";
*_aidl_return = filtered_settings[0]; *_aidl_return = filtered_settings[0];
return ndk::ScopedAStatus::ok(); 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) { for (auto& setting : filtered_settings) {
// Further filter out bis configuration LeAudioBroadcastConfigurationSetting matched_setting(setting);
LeAudioBroadcastConfigurationSetting filtered_setting(setting); matched_setting.subgroupsConfigurations.clear();
filtered_setting.subgroupsConfigurations.clear(); auto total_num_bis = 0;
for (auto& sub_cfg : setting.subgroupsConfigurations) {
bool isMatched = false; bool matched_all_requirement = true;
for (auto& sub_req : in_requirement.subgroupConfigurationRequirements) {
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 // Matching number of BIS
if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size()) if (sub_req.bisNumPerSubgroup != sub_cfg.bisConfigurations.size())
continue; continue;
// Currently will ignore quality and context hint. // Currently will ignore quality matching.
isMatched = true; matched_setting.subgroupsConfigurations.push_back(sub_cfg);
total_num_bis += sub_cfg.bisConfigurations.size();
is_matched = true;
break;
}
// There is an unmatched requirement, this setting cannot be used
if (!is_matched) {
matched_all_requirement = false;
break; break;
} }
if (isMatched)
filtered_setting.subgroupsConfigurations.push_back(sub_cfg);
}
// 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"; LOG(WARNING) << __func__ << ": Cannot match any requirement";

View file

@ -96,8 +96,8 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
// Private matching function definitions // Private matching function definitions
bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec); bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec);
bool isCapabilitiesMatchedContext( bool filterCapabilitiesMatchedContext(
AudioContext setting_context, AudioContext& setting_context,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities); const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
bool isMatchedSamplingFreq( bool isMatchedSamplingFreq(
CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq, CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq,
@ -134,11 +134,10 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
std::vector<std::optional<AseDirectionConfiguration>>& std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations); valid_direction_configurations);
void filterRequirementAseDirectionConfiguration( void filterRequirementAseDirectionConfiguration(
std::vector<std::optional<AseDirectionConfiguration>>& std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
direction_configurations, direction_configurations,
const std::optional<std::vector<std::optional<AseDirectionRequirement>>>& const std::vector<std::optional<AseDirectionRequirement>>& requirements,
requirements, std::optional<std::vector<std::optional<AseDirectionConfiguration>>>&
std::vector<std::optional<AseDirectionConfiguration>>&
valid_direction_configurations); valid_direction_configurations);
std::optional<LeAudioAseConfigurationSetting> std::optional<LeAudioAseConfigurationSetting>
getCapabilitiesMatchedAseConfigurationSettings( getCapabilitiesMatchedAseConfigurationSettings(
@ -157,6 +156,14 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
LeAudioBroadcastConfigurationSetting& setting, LeAudioBroadcastConfigurationSetting& setting,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities); const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
void getBroadcastSettings(); 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 { class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {