/* * 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. */ #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() { if (!leAudioCodecCapabilities.empty() || isInvalidFileContent) { return std::nullopt; } 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::vector BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities( const std::optional& le_audio_offload_setting) { if (!leAudioCodecCapabilities.empty()) { return leAudioCodecCapabilities; } if (!le_audio_offload_setting.has_value()) { LOG(ERROR) << __func__ << ": input le_audio_offload_setting content need to be non empty"; return {}; } ClearLeAudioCodecCapabilities(); isInvalidFileContent = true; std::vector 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 {}; } 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(); } 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)); } } } } } 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()); // encode and decode cannot be unknown at the same time if (unicast_encode_capability.codecType == CodecType::UNKNOWN && unicast_decode_capability.codecType == CodecType::UNKNOWN) { continue; } BroadcastCapability broadcast_capability = {.codecType = CodecType::UNKNOWN}; 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)); } return {.codecType = CodecType::UNKNOWN}; } 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()}}; } 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; 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.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