/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "aidl/android/hardware/bluetooth/audio/ChannelMode.h" #include "aidl/android/hardware/bluetooth/audio/CodecId.h" #include "aidl_android_hardware_bluetooth_audio_setting_enums.h" #define LOG_TAG "BTAudioCodecsProviderAidl" #include "BluetoothLeAudioCodecsProvider.h" namespace aidl { namespace android { namespace hardware { namespace bluetooth { namespace audio { static const char* kLeAudioCodecCapabilitiesFile = "/vendor/etc/le_audio_codec_capabilities.xml"; static const AudioLocation kStereoAudio = static_cast( static_cast(AudioLocation::FRONT_LEFT) | static_cast(AudioLocation::FRONT_RIGHT)); static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN; static std::vector leAudioCodecCapabilities; static bool isInvalidFileContent = false; std::optional BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() { auto le_audio_offload_setting = setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile); if (!le_audio_offload_setting.has_value()) { LOG(ERROR) << __func__ << ": Failed to read " << kLeAudioCodecCapabilitiesFile; } return le_audio_offload_setting; } std::unordered_map> BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo( const std::optional& le_audio_offload_setting) { // Load from previous storage if present if (!session_codecs_map_.empty()) return session_codecs_map_; isInvalidFileContent = true; if (!le_audio_offload_setting.has_value()) return {}; // Load scenario, configuration, codec configuration and strategy LoadConfigurationToMap(le_audio_offload_setting); if (supported_scenarios_.empty() || configuration_map_.empty() || codec_configuration_map_.empty() || strategy_configuration_map_.empty()) return {}; // Map each configuration into a CodecInfo std::unordered_map config_codec_info_map_; for (auto& p : configuration_map_) { // Initialize new CodecInfo for the config auto config_name = p.first; // Getting informations from codecConfig and strategyConfig const auto codec_config_name = p.second.getCodecConfiguration(); const auto strategy_config_name = p.second.getStrategyConfiguration(); const auto codec_configuration_map_iter = codec_configuration_map_.find(codec_config_name); if (codec_configuration_map_iter == codec_configuration_map_.end()) continue; const auto strategy_configuration_map_iter = strategy_configuration_map_.find(strategy_config_name); if (strategy_configuration_map_iter == strategy_configuration_map_.end()) continue; if (config_codec_info_map_.count(config_name) == 0) config_codec_info_map_[config_name] = CodecInfo(); const auto& codec_config = codec_configuration_map_iter->second; const auto codec = codec_config.getCodec(); const auto& strategy_config = strategy_configuration_map_iter->second; const auto strategy_config_channel_count = strategy_config.getChannelCount(); // Initiate information auto& codec_info = config_codec_info_map_[config_name]; switch (codec) { case setting::CodecType::LC3: codec_info.name = "LC3"; codec_info.id = CodecId::Core::LC3; break; default: codec_info.name = "UNDEFINE"; codec_info.id = CodecId::Vendor(); break; } codec_info.transport = CodecInfo::Transport::make(); // Mapping codec configuration information auto& transport = codec_info.transport.get(); transport.samplingFrequencyHz.push_back( codec_config.getSamplingFrequency()); // Mapping octetsPerCodecFrame to bitdepth for easier comparison. transport.bitdepth.push_back(codec_config.getOctetsPerCodecFrame()); transport.frameDurationUs.push_back(codec_config.getFrameDurationUs()); switch (strategy_config.getAudioLocation()) { case setting::AudioLocation::MONO: if (strategy_config_channel_count == 1) transport.channelMode.push_back(ChannelMode::MONO); else transport.channelMode.push_back(ChannelMode::DUALMONO); break; case setting::AudioLocation::STEREO: transport.channelMode.push_back(ChannelMode::STEREO); break; default: transport.channelMode.push_back(ChannelMode::UNKNOWN); break; } } // Goes through every scenario, deduplicate configuration, skip the invalid // config references (e.g. the "invalid" entries in the xml file). std::set encoding_config, decoding_config, broadcast_config; for (auto& s : supported_scenarios_) { if (s.hasEncode() && config_codec_info_map_.count(s.getEncode())) { encoding_config.insert(s.getEncode()); } if (s.hasDecode() && config_codec_info_map_.count(s.getDecode())) { decoding_config.insert(s.getDecode()); } if (s.hasBroadcast() && config_codec_info_map_.count(s.getBroadcast())) { broadcast_config.insert(s.getBroadcast()); } } // Split by session types and add results const auto encoding_path = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH; const auto decoding_path = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH; const auto broadcast_path = SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH; session_codecs_map_ = std::unordered_map>(); session_codecs_map_[encoding_path] = std::vector(); session_codecs_map_[decoding_path] = std::vector(); session_codecs_map_[broadcast_path] = std::vector(); session_codecs_map_[encoding_path].reserve(encoding_config.size()); session_codecs_map_[decoding_path].reserve(decoding_config.size()); session_codecs_map_[broadcast_path].reserve(broadcast_config.size()); for (auto& c : encoding_config) session_codecs_map_[encoding_path].push_back(config_codec_info_map_[c]); for (auto& c : decoding_config) session_codecs_map_[decoding_path].push_back(config_codec_info_map_[c]); for (auto& c : broadcast_config) session_codecs_map_[broadcast_path].push_back(config_codec_info_map_[c]); isInvalidFileContent = session_codecs_map_.empty(); return session_codecs_map_; } std::vector BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities( const std::optional& le_audio_offload_setting) { if (!leAudioCodecCapabilities.empty()) { return leAudioCodecCapabilities; } isInvalidFileContent = true; if (!le_audio_offload_setting.has_value()) { LOG(ERROR) << __func__ << ": input le_audio_offload_setting content need to be non empty"; return {}; } LoadConfigurationToMap(le_audio_offload_setting); if (supported_scenarios_.empty() || configuration_map_.empty() || codec_configuration_map_.empty() || strategy_configuration_map_.empty()) return {}; leAudioCodecCapabilities = ComposeLeAudioCodecCapabilities(supported_scenarios_); isInvalidFileContent = leAudioCodecCapabilities.empty(); return leAudioCodecCapabilities; } void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() { leAudioCodecCapabilities.clear(); configuration_map_.clear(); codec_configuration_map_.clear(); strategy_configuration_map_.clear(); session_codecs_map_.clear(); supported_scenarios_.clear(); } std::vector BluetoothLeAudioCodecsProvider::GetScenarios( const std::optional& le_audio_offload_setting) { std::vector supported_scenarios; if (le_audio_offload_setting->hasScenarioList()) { for (const auto& scenario_list : le_audio_offload_setting->getScenarioList()) { if (!scenario_list.hasScenario()) { continue; } for (const auto& scenario : scenario_list.getScenario()) { if (scenario.hasEncode() && scenario.hasDecode()) { supported_scenarios.push_back(scenario); } } } } return supported_scenarios; } void BluetoothLeAudioCodecsProvider::UpdateConfigurationsToMap( const std::optional& le_audio_offload_setting) { if (le_audio_offload_setting->hasConfigurationList()) { for (const auto& configuration_list : le_audio_offload_setting->getConfigurationList()) { if (!configuration_list.hasConfiguration()) { continue; } for (const auto& configuration : configuration_list.getConfiguration()) { if (configuration.hasName() && configuration.hasCodecConfiguration() && configuration.hasStrategyConfiguration()) { configuration_map_.insert( make_pair(configuration.getName(), configuration)); } } } } } void BluetoothLeAudioCodecsProvider::UpdateCodecConfigurationsToMap( const std::optional& le_audio_offload_setting) { if (le_audio_offload_setting->hasCodecConfigurationList()) { for (const auto& codec_configuration_list : le_audio_offload_setting->getCodecConfigurationList()) { if (!codec_configuration_list.hasCodecConfiguration()) { continue; } for (const auto& codec_configuration : codec_configuration_list.getCodecConfiguration()) { if (IsValidCodecConfiguration(codec_configuration)) { codec_configuration_map_.insert( make_pair(codec_configuration.getName(), codec_configuration)); } } } } } void BluetoothLeAudioCodecsProvider::UpdateStrategyConfigurationsToMap( const std::optional& le_audio_offload_setting) { if (le_audio_offload_setting->hasStrategyConfigurationList()) { for (const auto& strategy_configuration_list : le_audio_offload_setting->getStrategyConfigurationList()) { if (!strategy_configuration_list.hasStrategyConfiguration()) { continue; } for (const auto& strategy_configuration : strategy_configuration_list.getStrategyConfiguration()) { if (IsValidStrategyConfiguration(strategy_configuration)) { strategy_configuration_map_.insert(make_pair( strategy_configuration.getName(), strategy_configuration)); } } } } } void BluetoothLeAudioCodecsProvider::LoadConfigurationToMap( const std::optional& le_audio_offload_setting) { ClearLeAudioCodecCapabilities(); supported_scenarios_ = GetScenarios(le_audio_offload_setting); if (supported_scenarios_.empty()) { LOG(ERROR) << __func__ << ": No scenarios in " << kLeAudioCodecCapabilitiesFile; return; } UpdateConfigurationsToMap(le_audio_offload_setting); if (configuration_map_.empty()) { LOG(ERROR) << __func__ << ": No configurations in " << kLeAudioCodecCapabilitiesFile; return; } UpdateCodecConfigurationsToMap(le_audio_offload_setting); if (codec_configuration_map_.empty()) { LOG(ERROR) << __func__ << ": No codec configurations in " << kLeAudioCodecCapabilitiesFile; return; } UpdateStrategyConfigurationsToMap(le_audio_offload_setting); if (strategy_configuration_map_.empty()) { LOG(ERROR) << __func__ << ": No strategy configurations in " << kLeAudioCodecCapabilitiesFile; return; } } std::vector BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities( const std::vector& supported_scenarios) { std::vector le_audio_codec_capabilities; for (const auto& scenario : supported_scenarios) { UnicastCapability unicast_encode_capability = GetUnicastCapability(scenario.getEncode()); UnicastCapability unicast_decode_capability = GetUnicastCapability(scenario.getDecode()); BroadcastCapability broadcast_capability = {.codecType = CodecType::UNKNOWN}; if (scenario.hasBroadcast()) { broadcast_capability = GetBroadcastCapability(scenario.getBroadcast()); } // At least one capability should be valid if (unicast_encode_capability.codecType == CodecType::UNKNOWN && unicast_decode_capability.codecType == CodecType::UNKNOWN && broadcast_capability.codecType == CodecType::UNKNOWN) { LOG(ERROR) << __func__ << ": None of the capability is valid."; continue; } le_audio_codec_capabilities.push_back( {.unicastEncodeCapability = unicast_encode_capability, .unicastDecodeCapability = unicast_decode_capability, .broadcastCapability = broadcast_capability}); } return le_audio_codec_capabilities; } UnicastCapability BluetoothLeAudioCodecsProvider::GetUnicastCapability( const std::string& coding_direction) { if (coding_direction == "invalid") { return {.codecType = CodecType::UNKNOWN}; } auto configuration_iter = configuration_map_.find(coding_direction); if (configuration_iter == configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } auto codec_configuration_iter = codec_configuration_map_.find( configuration_iter->second.getCodecConfiguration()); if (codec_configuration_iter == codec_configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } auto strategy_configuration_iter = strategy_configuration_map_.find( configuration_iter->second.getStrategyConfiguration()); if (strategy_configuration_iter == strategy_configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } CodecType codec_type = GetCodecType(codec_configuration_iter->second.getCodec()); if (codec_type == CodecType::LC3) { return ComposeUnicastCapability( codec_type, GetAudioLocation( strategy_configuration_iter->second.getAudioLocation()), strategy_configuration_iter->second.getConnectedDevice(), strategy_configuration_iter->second.getChannelCount(), ComposeLc3Capability(codec_configuration_iter->second)); } else if (codec_type == CodecType::APTX_ADAPTIVE_LE || codec_type == CodecType::APTX_ADAPTIVE_LEX) { return ComposeUnicastCapability( codec_type, GetAudioLocation( strategy_configuration_iter->second.getAudioLocation()), strategy_configuration_iter->second.getConnectedDevice(), strategy_configuration_iter->second.getChannelCount(), ComposeAptxAdaptiveLeCapability(codec_configuration_iter->second)); } return {.codecType = CodecType::UNKNOWN}; } BroadcastCapability BluetoothLeAudioCodecsProvider::GetBroadcastCapability( const std::string& coding_direction) { if (coding_direction == "invalid") { return {.codecType = CodecType::UNKNOWN}; } auto configuration_iter = configuration_map_.find(coding_direction); if (configuration_iter == configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } auto codec_configuration_iter = codec_configuration_map_.find( configuration_iter->second.getCodecConfiguration()); if (codec_configuration_iter == codec_configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } auto strategy_configuration_iter = strategy_configuration_map_.find( configuration_iter->second.getStrategyConfiguration()); if (strategy_configuration_iter == strategy_configuration_map_.end()) { return {.codecType = CodecType::UNKNOWN}; } CodecType codec_type = GetCodecType(codec_configuration_iter->second.getCodec()); std::vector> bcastLc3Cap( 1, std::optional(ComposeLc3Capability(codec_configuration_iter->second))); if (codec_type == CodecType::LC3) { return ComposeBroadcastCapability( codec_type, GetAudioLocation( strategy_configuration_iter->second.getAudioLocation()), strategy_configuration_iter->second.getChannelCount(), bcastLc3Cap); } return {.codecType = CodecType::UNKNOWN}; } template BroadcastCapability BluetoothLeAudioCodecsProvider::ComposeBroadcastCapability( const CodecType& codec_type, const AudioLocation& audio_location, const uint8_t& channel_count, const std::vector& capability) { return {.codecType = codec_type, .supportedChannel = audio_location, .channelCountPerStream = channel_count, .leAudioCodecCapabilities = std::optional(capability)}; } template UnicastCapability BluetoothLeAudioCodecsProvider::ComposeUnicastCapability( const CodecType& codec_type, const AudioLocation& audio_location, const uint8_t& device_cnt, const uint8_t& channel_count, const T& capability) { return { .codecType = codec_type, .supportedChannel = audio_location, .deviceCount = device_cnt, .channelCountPerDevice = channel_count, .leAudioCodecCapabilities = UnicastCapability::LeAudioCodecCapabilities(capability), }; } Lc3Capabilities BluetoothLeAudioCodecsProvider::ComposeLc3Capability( const setting::CodecConfiguration& codec_configuration) { return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()}, .frameDurationUs = {codec_configuration.getFrameDurationUs()}, .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}}; } AptxAdaptiveLeCapabilities BluetoothLeAudioCodecsProvider::ComposeAptxAdaptiveLeCapability( const setting::CodecConfiguration& codec_configuration) { return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()}, .frameDurationUs = {codec_configuration.getFrameDurationUs()}, .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}}; } AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation( const setting::AudioLocation& audio_location) { switch (audio_location) { case setting::AudioLocation::MONO: return kMonoAudio; case setting::AudioLocation::STEREO: return kStereoAudio; default: return AudioLocation::UNKNOWN; } } CodecType BluetoothLeAudioCodecsProvider::GetCodecType( const setting::CodecType& codec_type) { switch (codec_type) { case setting::CodecType::LC3: return CodecType::LC3; case setting::CodecType::APTX_ADAPTIVE_LE: return CodecType::APTX_ADAPTIVE_LE; case setting::CodecType::APTX_ADAPTIVE_LEX: return CodecType::APTX_ADAPTIVE_LEX; default: return CodecType::UNKNOWN; } } bool BluetoothLeAudioCodecsProvider::IsValidCodecConfiguration( const setting::CodecConfiguration& codec_configuration) { return codec_configuration.hasName() && codec_configuration.hasCodec() && codec_configuration.hasSamplingFrequency() && codec_configuration.hasFrameDurationUs() && codec_configuration.hasOctetsPerCodecFrame(); } bool BluetoothLeAudioCodecsProvider::IsValidStrategyConfiguration( const setting::StrategyConfiguration& strategy_configuration) { if (!strategy_configuration.hasName() || !strategy_configuration.hasAudioLocation() || !strategy_configuration.hasConnectedDevice() || !strategy_configuration.hasChannelCount()) { return false; } if (strategy_configuration.getAudioLocation() == setting::AudioLocation::STEREO) { if ((strategy_configuration.getConnectedDevice() == 2 && strategy_configuration.getChannelCount() == 1) || (strategy_configuration.getConnectedDevice() == 1 && strategy_configuration.getChannelCount() == 2)) { // Stereo // 1. two connected device, one for L one for R // 2. one connected device for both L and R return true; } else if (strategy_configuration.getConnectedDevice() == 0 && strategy_configuration.getChannelCount() == 2) { // Broadcast return true; } } else if (strategy_configuration.getAudioLocation() == setting::AudioLocation::MONO) { if (strategy_configuration.getConnectedDevice() == 1 && strategy_configuration.getChannelCount() == 1) { // Mono return true; } } return false; } } // namespace audio } // namespace bluetooth } // namespace hardware } // namespace android } // namespace aidl