blob: 473777c945e9644c3d4e1d8334c973285fcf981f [file] [log] [blame]
/*
* 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 <set>
#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<AudioLocation>(
static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN;
static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
static bool isInvalidFileContent = false;
std::optional<setting::LeAudioOffloadSetting>
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<SessionType, std::vector<CodecInfo>>
BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
const std::optional<setting::LeAudioOffloadSetting>&
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<std::string, CodecInfo> 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<CodecInfo::Transport::Tag::leAudio>();
// Mapping codec configuration information
auto& transport =
codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
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<std::string> 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<SessionType, std::vector<CodecInfo>>();
session_codecs_map_[encoding_path] = std::vector<CodecInfo>();
session_codecs_map_[decoding_path] = std::vector<CodecInfo>();
session_codecs_map_[broadcast_path] = std::vector<CodecInfo>();
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<LeAudioCodecCapabilitiesSetting>
BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
const std::optional<setting::LeAudioOffloadSetting>&
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<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
const std::optional<setting::LeAudioOffloadSetting>&
le_audio_offload_setting) {
std::vector<setting::Scenario> 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<setting::LeAudioOffloadSetting>&
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<setting::LeAudioOffloadSetting>&
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<setting::LeAudioOffloadSetting>&
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<setting::LeAudioOffloadSetting>&
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<LeAudioCodecCapabilitiesSetting>
BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities(
const std::vector<setting::Scenario>& supported_scenarios) {
std::vector<LeAudioCodecCapabilitiesSetting> 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<std::optional<Lc3Capabilities>> 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 <class T>
BroadcastCapability BluetoothLeAudioCodecsProvider::ComposeBroadcastCapability(
const CodecType& codec_type, const AudioLocation& audio_location,
const uint8_t& channel_count, const std::vector<T>& capability) {
return {.codecType = codec_type,
.supportedChannel = audio_location,
.channelCountPerStream = channel_count,
.leAudioCodecCapabilities = std::optional(capability)};
}
template <class T>
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