Additional context matching logic for LEA multi-codec
When we match with the remote capabilities, we don't filter by
preferred audio context, since they are suggestion.
Instead, we populate settings into 2 separated vector:
- Matched settings with matched preferred context,
- Matched settings,
We then filter by the first vector, then by the second vector.
Bug: 306225778
Test: atest VtsHalBluetoothAudioTargetTest
Change-Id: I03f322e8de509127d218a9de6b41dd39b2ebdcba
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
index 5002a1a..1f54e6c 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp
@@ -175,12 +175,13 @@
if (!metadata.has_value()) continue;
if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) {
// Check all pref audio context to see if anything matched
- auto& context = metadata.value()
- .get<MetadataLtv::Tag::preferredAudioContexts>()
- .values;
- if (setting_context.bitmask & context.bitmask) {
+ 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 &= context.bitmask;
+ setting_context.bitmask &= prefer_context.bitmask;
return true;
}
}
@@ -219,8 +220,9 @@
/*cfg_channel*/,
CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts&
/*capability_channel*/) {
+ // Simply ignore.
+ // Later can use additional capabilities to match requirement.
bool isMatched = true;
- // TODO: how to match?
return isMatched;
}
@@ -317,22 +319,34 @@
return true;
}
-bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration(
- LeAudioAseConfiguration setting_cfg,
- LeAudioAseConfiguration requirement_cfg) {
+bool isMonoConfig(
+ CodecSpecificConfigurationLtv::AudioChannelAllocation allocation) {
+ auto channel_count = std::bitset<32>(allocation.bitmask);
+ return (channel_count.count() <= 1);
+}
+
+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())) {
+ LOG(WARNING) << __func__ << ": Doesn't match valid codec, cfg = "
+ << setting_cfg.codecId.value().toString()
+ << ", req = " << requirement_cfg.codecId.value().toString();
return false;
}
}
- if (requirement_cfg.targetLatency ==
- LeAudioAseConfiguration::TargetLatency::UNDEFINED ||
+ if (requirement_cfg.targetLatency !=
+ LeAudioAseConfiguration::TargetLatency::UNDEFINED &&
setting_cfg.targetLatency != requirement_cfg.targetLatency) {
+ LOG(WARNING) << __func__ << ": Doesn't match target latency, cfg = "
+ << int(setting_cfg.targetLatency)
+ << ", req = " << int(requirement_cfg.targetLatency);
return false;
}
// Ignore PHY requirement
@@ -346,11 +360,24 @@
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()) {
+ LOG(WARNING) << __func__ << ": Config not found for the requirement "
+ << requirement_cfg.toString();
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) {
+ LOG(WARNING) << __func__
+ << ": Config doesn't match the requirement, cfg = "
+ << cfg->second.toString()
+ << ", req = " << requirement_cfg.toString();
return false;
}
}
@@ -395,35 +422,132 @@
}
}
+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;
+}
+
+int getCountFromBitmask(int bitmask) {
+ return std::bitset<32>(bitmask).count();
+}
+
+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) {
+ LOG(INFO) << __func__ << ": Found a mono config for the mono requirement";
+ // 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) {
+ // Prefer the same allocation_bitmask
+ int channel_count = getCountFromBitmask(req_allocation_bitmask);
+ for (auto& cfg : valid_direction_configurations) {
+ int cfg_bitmask =
+ getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration);
+ if (cfg_bitmask == req_allocation_bitmask) {
+ LOG(INFO) << __func__
+ << ": Found an exact match for the requirement allocation of "
+ << cfg_bitmask;
+ return {cfg};
+ }
+ }
+ // No exact match found
+ 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 {};
+}
+
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) {
+ // For every requirement, find the matched ase configuration
+ if (!direction_configurations.has_value()) return;
+
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;
+ LOG(INFO) << __func__ << ": Testing for requirement = "
+ << requirement.value().toString();
+ auto req_allocation_bitmask = getLeAudioAseConfigurationAllocationBitmask(
+ requirement.value().aseConfiguration);
+ auto req_channel_count = getCountFromBitmask(req_allocation_bitmask);
+
+ auto temp = std::vector<AseDirectionConfiguration>();
+
for (auto direction_configuration : direction_configurations.value()) {
if (!direction_configuration.has_value()) continue;
- if (!isMatchedAseConfiguration(
+ if (!filterMatchedAseConfiguration(
direction_configuration.value().aseConfiguration,
requirement.value().aseConfiguration))
continue;
// Valid if match any requirement.
- valid_direction_configurations.value().push_back(direction_configuration);
- break;
+ temp.push_back(direction_configuration.value());
}
- }
- // 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 the best matching config based on channel allocation
+ auto total_cfg_channel_count = 0;
+ auto req_valid_configs =
+ getValidConfigurationsFromAllocation(req_allocation_bitmask, temp);
+ // Count and check required channel counts
+ for (auto& cfg : req_valid_configs) {
+ total_cfg_channel_count += getCountFromBitmask(
+ getLeAudioAseConfigurationAllocationBitmask(cfg.aseConfiguration));
+ valid_direction_configurations.value().push_back(cfg);
+ }
+ if (total_cfg_channel_count != req_channel_count) {
+ LOG(WARNING) << __func__
+ << ": Wrong channel count, req = " << req_channel_count
+ << ", total = " << total_cfg_channel_count;
+ valid_direction_configurations = std::nullopt;
+ return;
+ }
}
}
@@ -444,10 +568,6 @@
.flags = setting.flags,
.packing = setting.packing,
};
- // Try to match context in metadata.
- if (!filterCapabilitiesMatchedContext(filtered_setting.audioContext,
- capabilities))
- return std::nullopt;
// Get a list of all matched AseDirectionConfiguration
// for the input direction
@@ -495,15 +615,11 @@
requirement.audioContext.bitmask)
return std::nullopt;
+ LOG(INFO) << __func__
+ << ": Checking if the setting match: " << setting.toString();
// 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;
-
// Create a new LeAudioAseConfigurationSetting to return
LeAudioAseConfigurationSetting filtered_setting{
.audioContext = setting.audioContext,
@@ -515,7 +631,10 @@
filterRequirementAseDirectionConfiguration(
setting.sinkAseConfiguration, requirement.sinkAseRequirement.value(),
filtered_setting.sinkAseConfiguration);
- if (!filtered_setting.sinkAseConfiguration.has_value()) return std::nullopt;
+ if (!filtered_setting.sinkAseConfiguration.has_value()) {
+ LOG(WARNING) << __func__ << "Setting's sink doesn't match!";
+ return std::nullopt;
+ }
}
if (requirement.sourceAseRequirement.has_value()) {
@@ -523,82 +642,21 @@
setting.sourceAseConfiguration,
requirement.sourceAseRequirement.value(),
filtered_setting.sourceAseConfiguration);
- if (!filtered_setting.sourceAseConfiguration.has_value())
+ if (!filtered_setting.sourceAseConfiguration.has_value()) {
+ LOG(WARNING) << __func__ << "Setting's source doesn't match!";
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>>>&
- in_remoteSinkAudioCapabilities,
- const std::optional<std::vector<
- std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&
- in_remoteSourceAudioCapabilities,
+std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
+LeAudioOffloadAudioProvider::matchWithRequirement(
+ std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
+ matched_ase_configuration_settings,
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);
- }
-
- // Each setting consist of source and sink AseDirectionConfiguration vector
- // Filter every sink capability
- std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
- 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());
- }
- }
- }
- }
-
- // 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();
- }
+ in_requirements) {
// Each requirement will match with a valid setting
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result;
for (auto& requirement : in_requirements) {
@@ -626,7 +684,103 @@
break;
}
}
+ return result;
+}
+// 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>>>&
+ 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);
+ }
+
+ // Split out preferred and non-preferred settings based on context
+ // An example: preferred = MEDIA, available: MEDIA | CONVERSATION
+ // -> preferred list will have settings with MEDIA context
+ // -> non-preferred list will have settings with any context
+ // We want to match requirement with preferred context settings first
+ std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
+ matched_ase_configuration_settings;
+ // Matched ASE configuration with non-preferred audio context
+ std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
+ non_prefer_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()) {
+ // Push to non-prefer first for the broadest matching possible
+ non_prefer_matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ // Try to filter out prefer context to another vector.
+ if (filterCapabilitiesMatchedContext(
+ filtered_ase_configuration_setting.value().audioContext,
+ capability.value())) {
+ matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ }
+ }
+ }
+
+ // 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
+ // Push to non-prefer first for the broadest matching possible
+ non_prefer_matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ // Try to filter out prefer context to another vector.
+ if (filterCapabilitiesMatchedContext(
+ filtered_ase_configuration_setting.value().audioContext,
+ capability.value())) {
+ matched_ase_configuration_settings.push_back(
+ filtered_ase_configuration_setting.value());
+ }
+ }
+ }
+
+ auto result =
+ matchWithRequirement(matched_ase_configuration_settings, in_requirements);
+ if (result.empty()) {
+ LOG(WARNING) << __func__
+ << ": Cannot match with preferred context settings";
+ result = matchWithRequirement(non_prefer_matched_ase_configuration_settings,
+ in_requirements);
+ if (result.empty())
+ LOG(WARNING) << __func__
+ << ": Cannot match with non preferred context settings";
+ }
*_aidl_return = result;
return ndk::ScopedAStatus::ok();
};
@@ -642,7 +796,6 @@
requirement_qos.maxTransportLatencyMs) {
return false;
}
- // Ignore other parameters, as they are not populated in the setting_qos
return true;
}
@@ -680,17 +833,26 @@
// Get a list of all matched AseDirectionConfiguration
// for the input direction
- std::vector<std::optional<AseDirectionConfiguration>>*
- direction_configuration = nullptr;
+ std::optional<std::vector<std::optional<AseDirectionConfiguration>>>
+ direction_configuration = std::nullopt;
if (direction == kLeAudioDirectionSink) {
if (!setting.sinkAseConfiguration.has_value()) continue;
- direction_configuration = &setting.sinkAseConfiguration.value();
+ direction_configuration.emplace(setting.sinkAseConfiguration.value());
} else {
if (!setting.sourceAseConfiguration.has_value()) continue;
- direction_configuration = &setting.sourceAseConfiguration.value();
+ direction_configuration.emplace(setting.sourceAseConfiguration.value());
}
- for (auto cfg : *direction_configuration) {
+ 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()) {
@@ -701,14 +863,30 @@
// Try to match the ASE configuration
// and QoS with requirement
if (!cfg.value().qosConfiguration.has_value()) continue;
- if (isMatchedAseConfiguration(
+ if (filterMatchedAseConfiguration(
cfg.value().aseConfiguration,
direction_qos_requirement.value().aseConfiguration) &&
isMatchedQosRequirement(cfg.value().qosConfiguration.value(),
direction_qos_requirement.value())) {
- return cfg.value().qosConfiguration;
+ 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);
+ 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;
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
index 06cd405..afa2efd 100644
--- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
+++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h
@@ -122,8 +122,9 @@
bool isCapabilitiesMatchedCodecConfiguration(
std::vector<CodecSpecificConfigurationLtv>& codec_cfg,
std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities);
- bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg,
- LeAudioAseConfiguration requirement_cfg);
+ bool filterMatchedAseConfiguration(
+ LeAudioAseConfiguration& setting_cfg,
+ const LeAudioAseConfiguration& requirement_cfg);
bool isMatchedBISConfiguration(
LeAudioBisConfiguration bis_cfg,
const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities);
@@ -164,6 +165,13 @@
bool isSubgroupConfigurationMatchedContext(
AudioContext requirement_context,
LeAudioBroadcastSubgroupConfiguration configuration);
+ std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
+ matchWithRequirement(
+ std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>&
+ matched_ase_configuration_settings,
+ const std::vector<
+ IBluetoothAudioProvider::LeAudioConfigurationRequirement>&
+ in_requirements);
};
class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index ec63831..66cca0b 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -4177,6 +4177,7 @@
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies();
sampling_rate.bitmask =
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000 |
+ CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000 |
CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000;
auto frame_duration =
CodecSpecificCapabilitiesLtv::SupportedFrameDurations();
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
index 5429a8f..780154a 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.cpp
@@ -267,7 +267,7 @@
ase_configuration_settings_.clear();
configurations_.clear();
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
- CodecLocation::HOST);
+ CodecLocation::ADSP);
if (!loaded)
LOG(ERROR) << ": Unable to load le audio set configuration files.";
} else
@@ -371,7 +371,6 @@
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
ase.codecConfiguration.push_back(frame_sdu_structure);
- // TODO: Channel count
}
void AudioSetConfigurationProviderJson::populateAseConfiguration(
@@ -415,8 +414,53 @@
}
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
- LeAudioAseQosConfiguration& qos,
- const le_audio::QosConfiguration* qos_cfg) {
+ LeAudioAseQosConfiguration& qos, const le_audio::QosConfiguration* qos_cfg,
+ LeAudioAseConfiguration& ase) {
+ std::optional<CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU>
+ frameBlock = std::nullopt;
+ std::optional<CodecSpecificConfigurationLtv::FrameDuration> frameDuration =
+ std::nullopt;
+ std::optional<CodecSpecificConfigurationLtv::AudioChannelAllocation>
+ allocation = std::nullopt;
+ std::optional<CodecSpecificConfigurationLtv::OctetsPerCodecFrame> octet =
+ std::nullopt;
+
+ for (auto& cfg_ltv : ase.codecConfiguration) {
+ auto tag = cfg_ltv.getTag();
+ if (tag == CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU) {
+ frameBlock =
+ cfg_ltv.get<CodecSpecificConfigurationLtv::codecFrameBlocksPerSDU>();
+ } else if (tag == CodecSpecificConfigurationLtv::frameDuration) {
+ frameDuration =
+ cfg_ltv.get<CodecSpecificConfigurationLtv::frameDuration>();
+ } else if (tag == CodecSpecificConfigurationLtv::audioChannelAllocation) {
+ allocation =
+ cfg_ltv.get<CodecSpecificConfigurationLtv::audioChannelAllocation>();
+ } else if (tag == CodecSpecificConfigurationLtv::octetsPerCodecFrame) {
+ octet = cfg_ltv.get<CodecSpecificConfigurationLtv::octetsPerCodecFrame>();
+ }
+ }
+
+ int frameBlockValue = 1;
+ if (frameBlock.has_value()) frameBlockValue = frameBlock.value().value;
+
+ // Populate maxSdu
+ if (allocation.has_value() && octet.has_value()) {
+ auto channel_count = std::bitset<32>(allocation.value().bitmask).count();
+ qos.maxSdu = channel_count * octet.value().value * frameBlockValue;
+ }
+ // Populate sduIntervalUs
+ if (frameDuration.has_value()) {
+ switch (frameDuration.value()) {
+ case CodecSpecificConfigurationLtv::FrameDuration::US7500:
+ qos.sduIntervalUs = 7500;
+ break;
+ case CodecSpecificConfigurationLtv::FrameDuration::US10000:
+ qos.sduIntervalUs = 10000;
+ break;
+ }
+ qos.sduIntervalUs *= frameBlockValue;
+ }
qos.maxTransportLatencyMs = qos_cfg->max_transport_latency();
qos.retransmissionNum = qos_cfg->retransmission_number();
}
@@ -436,7 +480,7 @@
populateAseConfiguration(ase, flat_subconfig, qos_cfg);
// Translate into LeAudioAseQosConfiguration
- populateAseQosConfiguration(qos, qos_cfg);
+ populateAseQosConfiguration(qos, qos_cfg, ase);
// Translate location to data path id
switch (location) {
@@ -453,6 +497,8 @@
path.dataPathId = kIsoDataPathPlatformDefault;
break;
}
+ // Move codecId to iso data path
+ path.isoDataPathConfiguration.codecId = ase.codecId.value();
direction_conf.aseConfiguration = ase;
direction_conf.qosConfiguration = qos;
@@ -678,7 +724,8 @@
media_context.bitmask =
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
- AudioContext::UNSPECIFIED | AudioContext::MEDIA);
+ AudioContext::UNSPECIFIED | AudioContext::MEDIA |
+ AudioContext::SOUND_EFFECTS);
AudioContext conversational_context = AudioContext();
conversational_context.bitmask =
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h
index ce91fca..6639009 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothLeAudioAseConfigurationSettingProvider.h
@@ -79,7 +79,7 @@
static void populateAseQosConfiguration(
LeAudioAseQosConfiguration& qos,
- const le_audio::QosConfiguration* qos_cfg);
+ const le_audio::QosConfiguration* qos_cfg, LeAudioAseConfiguration& ase);
static AseDirectionConfiguration SetConfigurationFromFlatSubconfig(
const le_audio::AudioSetSubConfiguration* flat_subconfig,