diff options
author | Bao Do <quocbaodo@google.com> | 2023-12-14 03:01:09 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-12-14 03:01:09 +0000 |
commit | e44972d2f256dc812adb7e2a30d51ebfafe0ef35 (patch) | |
tree | b762596de2058b6127f88177f9849850c8fa062d | |
parent | 55718123bab331ac7da3a79572c7bf3f80c616b8 (diff) | |
parent | b448b9c84ced01b96f4a6d443d9b990509ae3a27 (diff) | |
download | interfaces-e44972d2f256dc812adb7e2a30d51ebfafe0ef35.tar.gz |
Default implementation of getLeAudioAseConfiguration am: 867af60a77 am: bf3e0f9b53 am: b448b9c84c
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2836201
Change-Id: Ia744b52dda52d7b64336ed42a9dfa381e4bafb25
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp | 413 | ||||
-rw-r--r-- | bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h | 7 |
2 files changed, 410 insertions, 10 deletions
diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp index 91ede92667..406856896c 100644 --- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.cpp @@ -28,6 +28,60 @@ namespace hardware { namespace bluetooth { namespace audio { +constexpr uint8_t kLeAudioDirectionSink = 0x01; +constexpr uint8_t kLeAudioDirectionSource = 0x02; + +const std::map<CodecSpecificConfigurationLtv::SamplingFrequency, uint32_t> + freq_to_support_bitmask_map = { + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ8000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ11025, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ11025}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ16000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ22050, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ22050}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ24000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ32000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ48000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ88200, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ88200}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ96000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ96000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ176400, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ176400}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ192000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ192000}, + {CodecSpecificConfigurationLtv::SamplingFrequency::HZ384000, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies::HZ384000}, +}; + +// Helper map from capability's tag to configuration's tag +std::map<CodecSpecificCapabilitiesLtv::Tag, CodecSpecificConfigurationLtv::Tag> + cap_to_cfg_tag_map = { + {CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies, + CodecSpecificConfigurationLtv::Tag::samplingFrequency}, + {CodecSpecificCapabilitiesLtv::Tag::supportedMaxCodecFramesPerSDU, + CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU}, + {CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations, + CodecSpecificConfigurationLtv::Tag::frameDuration}, + {CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts, + CodecSpecificConfigurationLtv::Tag::audioChannelAllocation}, + {CodecSpecificCapabilitiesLtv::Tag::supportedOctetsPerCodecFrame, + CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame}, +}; + +const std::map<CodecSpecificConfigurationLtv::FrameDuration, uint32_t> + fduration_to_support_fduration_map = { + {CodecSpecificConfigurationLtv::FrameDuration::US7500, + CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US7500}, + {CodecSpecificConfigurationLtv::FrameDuration::US10000, + CodecSpecificCapabilitiesLtv::SupportedFrameDurations::US10000}, +}; + LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider() : LeAudioOffloadAudioProvider() { session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH; @@ -87,6 +141,310 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::setCodecPriority( return ndk::ScopedAStatus::ok(); }; +bool LeAudioOffloadAudioProvider::isMatchedValidCodec(CodecId cfg_codec, + CodecId req_codec) { + auto priority = codec_priority_map_.find(cfg_codec); + if (priority != codec_priority_map_.end() && priority->second == -1) + return false; + return cfg_codec == req_codec; +} + +bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedContext( + AudioContext setting_context, + const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities) { + // If has no metadata, assume match + if (!capabilities.metadata.has_value()) return true; + + for (auto metadata : capabilities.metadata.value()) { + if (!metadata.has_value()) continue; + if (metadata.value().getTag() == MetadataLtv::Tag::preferredAudioContexts) { + // Check all pref audio context to see if anything matched + auto& context = metadata.value() + .get<MetadataLtv::Tag::preferredAudioContexts>() + .values; + if (setting_context.bitmask & context.bitmask) return true; + } + } + + return false; +} + +bool LeAudioOffloadAudioProvider::isMatchedSamplingFreq( + CodecSpecificConfigurationLtv::SamplingFrequency& cfg_freq, + CodecSpecificCapabilitiesLtv::SupportedSamplingFrequencies& + capability_freq) { + for (auto [freq, bitmask] : freq_to_support_bitmask_map) + if (cfg_freq == freq) return (capability_freq.bitmask & bitmask); + return false; +} + +bool LeAudioOffloadAudioProvider::isMatchedFrameDuration( + CodecSpecificConfigurationLtv::FrameDuration& cfg_fduration, + CodecSpecificCapabilitiesLtv::SupportedFrameDurations& + capability_fduration) { + for (auto [fduration, bitmask] : fduration_to_support_fduration_map) + if (cfg_fduration == fduration) + return (capability_fduration.bitmask & bitmask); + return false; +} + +bool LeAudioOffloadAudioProvider::isMatchedAudioChannel( + CodecSpecificConfigurationLtv::AudioChannelAllocation& + /*cfg_channel*/, + CodecSpecificCapabilitiesLtv::SupportedAudioChannelCounts& + /*capability_channel*/) { + bool isMatched = true; + // TODO: how to match? + return isMatched; +} + +bool LeAudioOffloadAudioProvider::isMatchedCodecFramesPerSDU( + CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU& cfg_frame_sdu, + CodecSpecificCapabilitiesLtv::SupportedMaxCodecFramesPerSDU& + capability_frame_sdu) { + return cfg_frame_sdu.value <= capability_frame_sdu.value; +} + +bool LeAudioOffloadAudioProvider::isMatchedOctetsPerCodecFrame( + CodecSpecificConfigurationLtv::OctetsPerCodecFrame& cfg_octets, + CodecSpecificCapabilitiesLtv::SupportedOctetsPerCodecFrame& + capability_octets) { + return cfg_octets.value >= capability_octets.minimum && + cfg_octets.value <= capability_octets.maximum; +} + +bool LeAudioOffloadAudioProvider::isCapabilitiesMatchedCodecConfiguration( + std::vector<CodecSpecificConfigurationLtv>& codec_cfg, + std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities) { + // Convert all codec_cfg into a map of tags -> correct data + std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv> + cfg_tag_map; + for (auto codec_cfg_data : codec_cfg) + cfg_tag_map[codec_cfg_data.getTag()] = codec_cfg_data; + + for (auto& codec_capability : codec_capabilities) { + auto cfg = cfg_tag_map.find(cap_to_cfg_tag_map[codec_capability.getTag()]); + // Cannot find tag for the capability: + if (cfg == cfg_tag_map.end()) return false; + + // Matching logic for sampling frequency + if (codec_capability.getTag() == + CodecSpecificCapabilitiesLtv::Tag::supportedSamplingFrequencies) { + if (!isMatchedSamplingFreq( + cfg->second + .get<CodecSpecificConfigurationLtv::Tag::samplingFrequency>(), + codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: + supportedSamplingFrequencies>())) + return false; + } else if (codec_capability.getTag() == + CodecSpecificCapabilitiesLtv::Tag::supportedFrameDurations) { + if (!isMatchedFrameDuration( + cfg->second + .get<CodecSpecificConfigurationLtv::Tag::frameDuration>(), + codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: + supportedFrameDurations>())) + return false; + } else if (codec_capability.getTag() == + CodecSpecificCapabilitiesLtv::Tag::supportedAudioChannelCounts) { + if (!isMatchedAudioChannel( + cfg->second.get< + CodecSpecificConfigurationLtv::Tag::audioChannelAllocation>(), + codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: + supportedAudioChannelCounts>())) + return false; + } else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag:: + supportedMaxCodecFramesPerSDU) { + if (!isMatchedCodecFramesPerSDU( + cfg->second.get< + CodecSpecificConfigurationLtv::Tag::codecFrameBlocksPerSDU>(), + codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: + supportedMaxCodecFramesPerSDU>())) + return false; + } else if (codec_capability.getTag() == CodecSpecificCapabilitiesLtv::Tag:: + supportedOctetsPerCodecFrame) { + if (!isMatchedOctetsPerCodecFrame( + cfg->second.get< + CodecSpecificConfigurationLtv::Tag::octetsPerCodecFrame>(), + codec_capability.get<CodecSpecificCapabilitiesLtv::Tag:: + supportedOctetsPerCodecFrame>())) + return false; + } + } + + return true; +} + +bool LeAudioOffloadAudioProvider::isMatchedAseConfiguration( + LeAudioAseConfiguration setting_cfg, + LeAudioAseConfiguration requirement_cfg) { + // Check matching for codec configuration <=> requirement ASE codec + // Also match if no CodecId requirement + if (requirement_cfg.codecId.has_value()) { + if (!setting_cfg.codecId.has_value()) return false; + if (!isMatchedValidCodec(setting_cfg.codecId.value(), + requirement_cfg.codecId.value())) + return false; + } + + if (setting_cfg.targetLatency != requirement_cfg.targetLatency) return false; + // Ignore PHY requirement + + // Check all codec configuration + std::map<CodecSpecificConfigurationLtv::Tag, CodecSpecificConfigurationLtv> + cfg_tag_map; + for (auto cfg : setting_cfg.codecConfiguration) + cfg_tag_map[cfg.getTag()] = cfg; + + for (auto requirement_cfg : requirement_cfg.codecConfiguration) { + // Directly compare CodecSpecificConfigurationLtv + auto cfg = cfg_tag_map.find(requirement_cfg.getTag()); + if (cfg == cfg_tag_map.end()) return false; + + if (cfg->second != requirement_cfg) return false; + } + // Ignore vendor configuration and metadata requirement + + return true; +} + +void LeAudioOffloadAudioProvider::filterCapabilitiesAseDirectionConfiguration( + std::vector<std::optional<AseDirectionConfiguration>>& + direction_configurations, + const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities, + std::vector<std::optional<AseDirectionConfiguration>>& + valid_direction_configurations) { + for (auto direction_configuration : direction_configurations) { + if (!direction_configuration.has_value()) continue; + if (!direction_configuration.value().aseConfiguration.codecId.has_value()) + continue; + if (!isMatchedValidCodec( + direction_configuration.value().aseConfiguration.codecId.value(), + capabilities.codecId)) + continue; + // Check matching for codec configuration <=> codec capabilities + if (!isCapabilitiesMatchedCodecConfiguration( + direction_configuration.value().aseConfiguration.codecConfiguration, + capabilities.codecSpecificCapabilities)) + continue; + valid_direction_configurations.push_back(direction_configuration); + } +} + +void LeAudioOffloadAudioProvider::filterRequirementAseDirectionConfiguration( + std::vector<std::optional<AseDirectionConfiguration>>& + direction_configurations, + const std::optional<std::vector<std::optional<AseDirectionRequirement>>>& + requirements, + std::vector<std::optional<AseDirectionConfiguration>>& + valid_direction_configurations) { + for (auto direction_configuration : direction_configurations) { + if (!requirements.has_value()) { + // If there's no requirement, all are valid + valid_direction_configurations.push_back(direction_configuration); + continue; + } + if (!direction_configuration.has_value()) continue; + + for (auto& requirement : requirements.value()) { + if (!requirement.has_value()) continue; + if (!isMatchedAseConfiguration( + direction_configuration.value().aseConfiguration, + requirement.value().aseConfiguration)) + continue; + // Valid if match any requirement. + valid_direction_configurations.push_back(direction_configuration); + break; + } + } +} + +/* Get a new LeAudioAseConfigurationSetting by matching a setting with a + * capabilities. The new setting will have a filtered list of + * AseDirectionConfiguration that matched the capabilities */ +std::optional<LeAudioAseConfigurationSetting> +LeAudioOffloadAudioProvider::getCapabilitiesMatchedAseConfigurationSettings( + IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting, + const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities, + uint8_t direction) { + // Try to match context in metadata. + if (!isCapabilitiesMatchedContext(setting.audioContext, capabilities)) + return std::nullopt; + + // Get a list of all matched AseDirectionConfiguration + // for the input direction + std::vector<std::optional<AseDirectionConfiguration>>* + direction_configuration = nullptr; + if (direction == kLeAudioDirectionSink) { + if (!setting.sinkAseConfiguration.has_value()) return std::nullopt; + direction_configuration = &setting.sinkAseConfiguration.value(); + } else { + if (!setting.sourceAseConfiguration.has_value()) return std::nullopt; + direction_configuration = &setting.sourceAseConfiguration.value(); + } + std::vector<std::optional<AseDirectionConfiguration>> + valid_direction_configuration; + filterCapabilitiesAseDirectionConfiguration( + *direction_configuration, capabilities, valid_direction_configuration); + if (valid_direction_configuration.empty()) return std::nullopt; + + // Create a new LeAudioAseConfigurationSetting and return + LeAudioAseConfigurationSetting filtered_setting; + filtered_setting.audioContext = setting.audioContext; + filtered_setting.packing = setting.packing; + if (direction == kLeAudioDirectionSink) { + filtered_setting.sinkAseConfiguration = valid_direction_configuration; + } else { + filtered_setting.sourceAseConfiguration = valid_direction_configuration; + } + filtered_setting.flags = setting.flags; + + return filtered_setting; +} + +/* Get a new LeAudioAseConfigurationSetting by matching a setting with a + * requirement. The new setting will have a filtered list of + * AseDirectionConfiguration that matched the requirement */ +std::optional<LeAudioAseConfigurationSetting> +LeAudioOffloadAudioProvider::getRequirementMatchedAseConfigurationSettings( + IBluetoothAudioProvider::LeAudioAseConfigurationSetting& setting, + const IBluetoothAudioProvider::LeAudioConfigurationRequirement& + requirement) { + // Try to match context in metadata. + if (setting.audioContext != requirement.audioContext) return std::nullopt; + + // Check requirement for the correct direction + const std::optional<std::vector<std::optional<AseDirectionRequirement>>>* + direction_requirement; + std::vector<std::optional<AseDirectionConfiguration>>* + direction_configuration; + if (setting.sinkAseConfiguration.has_value()) { + direction_configuration = &setting.sinkAseConfiguration.value(); + direction_requirement = &requirement.sinkAseRequirement; + } else { + direction_configuration = &setting.sourceAseConfiguration.value(); + direction_requirement = &requirement.sourceAseRequirement; + } + + std::vector<std::optional<AseDirectionConfiguration>> + valid_direction_configuration; + filterRequirementAseDirectionConfiguration(*direction_configuration, + *direction_requirement, + valid_direction_configuration); + if (valid_direction_configuration.empty()) return std::nullopt; + + // Create a new LeAudioAseConfigurationSetting and return + LeAudioAseConfigurationSetting filtered_setting; + filtered_setting.audioContext = setting.audioContext; + filtered_setting.packing = setting.packing; + if (setting.sinkAseConfiguration.has_value()) + filtered_setting.sinkAseConfiguration = valid_direction_configuration; + else + filtered_setting.sourceAseConfiguration = valid_direction_configuration; + filtered_setting.flags = setting.flags; + + return filtered_setting; +} + ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration( const std::optional<std::vector< std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>& @@ -98,12 +456,55 @@ ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseConfiguration( in_requirements, std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>* _aidl_return) { - /* TODO: Implement */ - (void)in_remoteSinkAudioCapabilities; - (void)in_remoteSourceAudioCapabilities; - (void)in_requirements; - (void)_aidl_return; - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + // Get all configuration settings + std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> + ase_configuration_settings = + BluetoothAudioCodecs::GetLeAudioAseConfigurationSettings(); + + // Currently won't handle case where both sink and source capabilities + // are passed in. Only handle one of them. + const std::optional<std::vector< + std::optional<IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>* + in_remoteAudioCapabilities; + uint8_t direction = 0; + if (in_remoteSinkAudioCapabilities.has_value()) { + direction = kLeAudioDirectionSink; + in_remoteAudioCapabilities = &in_remoteSinkAudioCapabilities; + } else { + direction = kLeAudioDirectionSource; + in_remoteAudioCapabilities = &in_remoteSourceAudioCapabilities; + } + + std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> + capability_matched_ase_configuration_settings; + // Matching with remote capabilities + for (auto& setting : ase_configuration_settings) { + for (auto& capability : in_remoteAudioCapabilities->value()) { + if (!capability.has_value()) continue; + auto filtered_ase_configuration_setting = + getCapabilitiesMatchedAseConfigurationSettings( + setting, capability.value(), direction); + if (filtered_ase_configuration_setting.has_value()) { + capability_matched_ase_configuration_settings.push_back( + filtered_ase_configuration_setting.value()); + } + } + } + + // Matching with requirements + std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting> result; + for (auto& setting : capability_matched_ase_configuration_settings) { + for (auto& requirement : in_requirements) { + auto filtered_ase_configuration_setting = + getRequirementMatchedAseConfigurationSettings(setting, requirement); + if (filtered_ase_configuration_setting.has_value()) { + result.push_back(filtered_ase_configuration_setting.value()); + } + } + } + + *_aidl_return = result; + return ndk::ScopedAStatus::ok(); }; ndk::ScopedAStatus LeAudioOffloadAudioProvider::getLeAudioAseQosConfiguration( diff --git a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h index bfca061ddd..2488f9b78b 100644 --- a/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h +++ b/bluetooth/audio/aidl/default/LeAudioOffloadAudioProvider.h @@ -87,7 +87,7 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider { // Private matching function definitions bool isMatchedValidCodec(CodecId cfg_codec, CodecId req_codec); - bool isMatchedContext( + bool isCapabilitiesMatchedContext( AudioContext setting_context, const IBluetoothAudioProvider::LeAudioDeviceCapabilities& capabilities); bool isMatchedSamplingFreq( @@ -113,9 +113,8 @@ class LeAudioOffloadAudioProvider : public BluetoothAudioProvider { bool isCapabilitiesMatchedCodecConfiguration( std::vector<CodecSpecificConfigurationLtv>& codec_cfg, std::vector<CodecSpecificCapabilitiesLtv> codec_capabilities); - bool isRequirementAseConfigurationMatched( - LeAudioAseConfiguration setting_cfg, - LeAudioAseConfiguration requirement_cfg); + bool isMatchedAseConfiguration(LeAudioAseConfiguration setting_cfg, + LeAudioAseConfiguration requirement_cfg); void filterCapabilitiesAseDirectionConfiguration( std::vector<std::optional<AseDirectionConfiguration>>& direction_configurations, |