mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
New format eliminates the need for allocation and deduplicates many configurations. We change the file name of the default set configuration for intergration with vendor's configuration. We also support storing the configuration name in the vendor's metadata for easier debugging. Bug: 331490291 Test: atest VtsHalBluetoothAudioTargetTest Change-Id: I0f6cfbdad1da05c35d7c758c69d7030527e1f46a
852 lines
34 KiB
C++
852 lines
34 KiB
C++
/*
|
|
* Copyright (C) 2023 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 STREAM_TO_UINT8(u8, p) \
|
|
{ \
|
|
(u8) = (uint8_t)(*(p)); \
|
|
(p) += 1; \
|
|
}
|
|
#define STREAM_TO_UINT16(u16, p) \
|
|
{ \
|
|
(u16) = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \
|
|
(p) += 2; \
|
|
}
|
|
#define STREAM_TO_UINT32(u32, p) \
|
|
{ \
|
|
(u32) = (((uint32_t)(*(p))) + ((((uint32_t)(*((p) + 1)))) << 8) + \
|
|
((((uint32_t)(*((p) + 2)))) << 16) + \
|
|
((((uint32_t)(*((p) + 3)))) << 24)); \
|
|
(p) += 4; \
|
|
}
|
|
|
|
#define LOG_TAG "BTAudioAseConfigAidl"
|
|
|
|
#include "BluetoothLeAudioAseConfigurationSettingProvider.h"
|
|
|
|
#include <aidl/android/hardware/bluetooth/audio/AudioConfiguration.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/AudioContext.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/CodecId.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificCapabilitiesLtv.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/CodecSpecificConfigurationLtv.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/LeAudioAseConfiguration.h>
|
|
#include <aidl/android/hardware/bluetooth/audio/Phy.h>
|
|
#include <android-base/logging.h>
|
|
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/util.h"
|
|
|
|
namespace aidl {
|
|
namespace android {
|
|
namespace hardware {
|
|
namespace bluetooth {
|
|
namespace audio {
|
|
|
|
/* Internal structure definition */
|
|
std::map<std::string,
|
|
std::tuple<std::vector<std::optional<AseDirectionConfiguration>>,
|
|
std::vector<std::optional<AseDirectionConfiguration>>,
|
|
ConfigurationFlags>>
|
|
configurations_;
|
|
|
|
std::vector<LeAudioAseConfigurationSetting> ase_configuration_settings_;
|
|
|
|
constexpr uint8_t kIsoDataPathHci = 0x00;
|
|
constexpr uint8_t kIsoDataPathPlatformDefault = 0x01;
|
|
constexpr uint8_t kIsoDataPathDisabled = 0xFF;
|
|
|
|
constexpr uint8_t kLeAudioDirectionSink = 0x01;
|
|
constexpr uint8_t kLeAudioDirectionSource = 0x02;
|
|
constexpr uint8_t kLeAudioDirectionBoth =
|
|
kLeAudioDirectionSink | kLeAudioDirectionSource;
|
|
|
|
/* Sampling Frequencies */
|
|
constexpr uint8_t kLeAudioSamplingFreq8000Hz = 0x01;
|
|
constexpr uint8_t kLeAudioSamplingFreq11025Hz = 0x02;
|
|
constexpr uint8_t kLeAudioSamplingFreq16000Hz = 0x03;
|
|
constexpr uint8_t kLeAudioSamplingFreq22050Hz = 0x04;
|
|
constexpr uint8_t kLeAudioSamplingFreq24000Hz = 0x05;
|
|
constexpr uint8_t kLeAudioSamplingFreq32000Hz = 0x06;
|
|
constexpr uint8_t kLeAudioSamplingFreq44100Hz = 0x07;
|
|
constexpr uint8_t kLeAudioSamplingFreq48000Hz = 0x08;
|
|
constexpr uint8_t kLeAudioSamplingFreq88200Hz = 0x09;
|
|
constexpr uint8_t kLeAudioSamplingFreq96000Hz = 0x0A;
|
|
constexpr uint8_t kLeAudioSamplingFreq176400Hz = 0x0B;
|
|
constexpr uint8_t kLeAudioSamplingFreq192000Hz = 0x0C;
|
|
constexpr uint8_t kLeAudioSamplingFreq384000Hz = 0x0D;
|
|
|
|
/* Frame Durations */
|
|
constexpr uint8_t kLeAudioCodecFrameDur7500us = 0x00;
|
|
constexpr uint8_t kLeAudioCodecFrameDur10000us = 0x01;
|
|
|
|
/* Audio Allocations */
|
|
constexpr uint32_t kLeAudioLocationNotAllowed = 0x00000000;
|
|
constexpr uint32_t kLeAudioLocationFrontLeft = 0x00000001;
|
|
constexpr uint32_t kLeAudioLocationFrontRight = 0x00000002;
|
|
constexpr uint32_t kLeAudioLocationFrontCenter = 0x00000004;
|
|
constexpr uint32_t kLeAudioLocationLowFreqEffects1 = 0x00000008;
|
|
constexpr uint32_t kLeAudioLocationBackLeft = 0x00000010;
|
|
constexpr uint32_t kLeAudioLocationBackRight = 0x00000020;
|
|
constexpr uint32_t kLeAudioLocationFrontLeftOfCenter = 0x00000040;
|
|
constexpr uint32_t kLeAudioLocationFrontRightOfCenter = 0x00000080;
|
|
constexpr uint32_t kLeAudioLocationBackCenter = 0x00000100;
|
|
constexpr uint32_t kLeAudioLocationLowFreqEffects2 = 0x00000200;
|
|
constexpr uint32_t kLeAudioLocationSideLeft = 0x00000400;
|
|
constexpr uint32_t kLeAudioLocationSideRight = 0x00000800;
|
|
constexpr uint32_t kLeAudioLocationTopFrontLeft = 0x00001000;
|
|
constexpr uint32_t kLeAudioLocationTopFrontRight = 0x00002000;
|
|
constexpr uint32_t kLeAudioLocationTopFrontCenter = 0x00004000;
|
|
constexpr uint32_t kLeAudioLocationTopCenter = 0x00008000;
|
|
constexpr uint32_t kLeAudioLocationTopBackLeft = 0x00010000;
|
|
constexpr uint32_t kLeAudioLocationTopBackRight = 0x00020000;
|
|
constexpr uint32_t kLeAudioLocationTopSideLeft = 0x00040000;
|
|
constexpr uint32_t kLeAudioLocationTopSideRight = 0x00080000;
|
|
constexpr uint32_t kLeAudioLocationTopBackCenter = 0x00100000;
|
|
constexpr uint32_t kLeAudioLocationBottomFrontCenter = 0x00200000;
|
|
constexpr uint32_t kLeAudioLocationBottomFrontLeft = 0x00400000;
|
|
constexpr uint32_t kLeAudioLocationBottomFrontRight = 0x00800000;
|
|
constexpr uint32_t kLeAudioLocationFrontLeftWide = 0x01000000;
|
|
constexpr uint32_t kLeAudioLocationFrontRightWide = 0x02000000;
|
|
constexpr uint32_t kLeAudioLocationLeftSurround = 0x04000000;
|
|
constexpr uint32_t kLeAudioLocationRightSurround = 0x08000000;
|
|
|
|
constexpr uint32_t kLeAudioLocationAnyLeft =
|
|
kLeAudioLocationFrontLeft | kLeAudioLocationBackLeft |
|
|
kLeAudioLocationFrontLeftOfCenter | kLeAudioLocationSideLeft |
|
|
kLeAudioLocationTopFrontLeft | kLeAudioLocationTopBackLeft |
|
|
kLeAudioLocationTopSideLeft | kLeAudioLocationBottomFrontLeft |
|
|
kLeAudioLocationFrontLeftWide | kLeAudioLocationLeftSurround;
|
|
|
|
constexpr uint32_t kLeAudioLocationAnyRight =
|
|
kLeAudioLocationFrontRight | kLeAudioLocationBackRight |
|
|
kLeAudioLocationFrontRightOfCenter | kLeAudioLocationSideRight |
|
|
kLeAudioLocationTopFrontRight | kLeAudioLocationTopBackRight |
|
|
kLeAudioLocationTopSideRight | kLeAudioLocationBottomFrontRight |
|
|
kLeAudioLocationFrontRightWide | kLeAudioLocationRightSurround;
|
|
|
|
constexpr uint32_t kLeAudioLocationStereo =
|
|
kLeAudioLocationFrontLeft | kLeAudioLocationFrontRight;
|
|
|
|
/* Octets Per Frame */
|
|
constexpr uint16_t kLeAudioCodecFrameLen30 = 30;
|
|
constexpr uint16_t kLeAudioCodecFrameLen40 = 40;
|
|
constexpr uint16_t kLeAudioCodecFrameLen60 = 60;
|
|
constexpr uint16_t kLeAudioCodecFrameLen80 = 80;
|
|
constexpr uint16_t kLeAudioCodecFrameLen100 = 100;
|
|
constexpr uint16_t kLeAudioCodecFrameLen120 = 120;
|
|
|
|
/* Helper map for matching various sampling frequency notations */
|
|
const std::map<uint8_t, CodecSpecificConfigurationLtv::SamplingFrequency>
|
|
sampling_freq_map = {
|
|
{kLeAudioSamplingFreq8000Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ8000},
|
|
{kLeAudioSamplingFreq16000Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ16000},
|
|
{kLeAudioSamplingFreq24000Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ24000},
|
|
{kLeAudioSamplingFreq32000Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ32000},
|
|
{kLeAudioSamplingFreq44100Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ44100},
|
|
{kLeAudioSamplingFreq48000Hz,
|
|
CodecSpecificConfigurationLtv::SamplingFrequency::HZ48000}};
|
|
|
|
/* Helper map for matching various frame durations notations */
|
|
const std::map<uint8_t, CodecSpecificConfigurationLtv::FrameDuration>
|
|
frame_duration_map = {
|
|
{kLeAudioCodecFrameDur7500us,
|
|
CodecSpecificConfigurationLtv::FrameDuration::US7500},
|
|
{kLeAudioCodecFrameDur10000us,
|
|
CodecSpecificConfigurationLtv::FrameDuration::US10000}};
|
|
|
|
/* Helper map for matching various audio channel allocation notations */
|
|
std::map<uint32_t, uint32_t> audio_channel_allocation_map = {
|
|
{kLeAudioLocationNotAllowed,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::NOT_ALLOWED},
|
|
{kLeAudioLocationFrontLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT},
|
|
{kLeAudioLocationFrontRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT},
|
|
{kLeAudioLocationFrontCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER},
|
|
{kLeAudioLocationLowFreqEffects1,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
|
LOW_FREQUENCY_EFFECTS_1},
|
|
{kLeAudioLocationBackLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_LEFT},
|
|
{kLeAudioLocationBackRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_RIGHT},
|
|
{kLeAudioLocationFrontLeftOfCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
|
FRONT_LEFT_OF_CENTER},
|
|
{kLeAudioLocationFrontRightOfCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
|
FRONT_RIGHT_OF_CENTER},
|
|
{kLeAudioLocationBackCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::BACK_CENTER},
|
|
{kLeAudioLocationLowFreqEffects2,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
|
LOW_FREQUENCY_EFFECTS_2},
|
|
{kLeAudioLocationSideLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_LEFT},
|
|
{kLeAudioLocationSideRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::SIDE_RIGHT},
|
|
{kLeAudioLocationTopFrontLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_LEFT},
|
|
{kLeAudioLocationTopFrontRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_RIGHT},
|
|
{kLeAudioLocationTopFrontCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_FRONT_CENTER},
|
|
{kLeAudioLocationTopCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_CENTER},
|
|
{kLeAudioLocationTopBackLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_LEFT},
|
|
{kLeAudioLocationTopBackRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_RIGHT},
|
|
{kLeAudioLocationTopSideLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_LEFT},
|
|
{kLeAudioLocationTopSideRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_SIDE_RIGHT},
|
|
{kLeAudioLocationTopBackCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::TOP_BACK_CENTER},
|
|
{kLeAudioLocationBottomFrontCenter,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::
|
|
BOTTOM_FRONT_CENTER},
|
|
{kLeAudioLocationBottomFrontLeft,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_LEFT},
|
|
{kLeAudioLocationBottomFrontRight,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::BOTTOM_FRONT_RIGHT},
|
|
{kLeAudioLocationFrontLeftWide,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT_WIDE},
|
|
{kLeAudioLocationFrontRightWide,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT_WIDE},
|
|
{kLeAudioLocationLeftSurround,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::LEFT_SURROUND},
|
|
{kLeAudioLocationRightSurround,
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::RIGHT_SURROUND},
|
|
};
|
|
|
|
// Set configuration and scenario files with fallback default
|
|
static const std::vector<
|
|
std::pair<const char* /*schema*/, const char* /*content*/>>
|
|
kLeAudioSetConfigs = {
|
|
{"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_configurations.bfbs",
|
|
"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_configurations.json"},
|
|
|
|
{"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_configurations.bfbs",
|
|
"/vendor/etc/aidl/le_audio/"
|
|
"aidl_default_audio_set_configurations.json"},
|
|
};
|
|
static const std::vector<
|
|
std::pair<const char* /*schema*/, const char* /*content*/>>
|
|
kLeAudioSetScenarios = {{"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_scenarios.bfbs",
|
|
"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_scenarios.json"},
|
|
|
|
{"/vendor/etc/aidl/le_audio/"
|
|
"aidl_audio_set_scenarios.bfbs",
|
|
"/vendor/etc/aidl/le_audio/"
|
|
"aidl_default_audio_set_scenarios.json"}};
|
|
|
|
/* Implementation */
|
|
|
|
std::vector<LeAudioAseConfigurationSetting>
|
|
AudioSetConfigurationProviderJson::GetLeAudioAseConfigurationSettings() {
|
|
AudioSetConfigurationProviderJson::LoadAudioSetConfigurationProviderJson();
|
|
return ase_configuration_settings_;
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::
|
|
LoadAudioSetConfigurationProviderJson() {
|
|
if (configurations_.empty() || ase_configuration_settings_.empty()) {
|
|
ase_configuration_settings_.clear();
|
|
configurations_.clear();
|
|
auto loaded = LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios,
|
|
CodecLocation::ADSP);
|
|
if (!loaded)
|
|
LOG(ERROR) << ": Unable to load le audio set configuration files.";
|
|
} else
|
|
LOG(INFO) << ": Reusing loaded le audio set configuration";
|
|
}
|
|
|
|
const le_audio::CodecSpecificConfiguration*
|
|
AudioSetConfigurationProviderJson::LookupCodecSpecificParam(
|
|
const flatbuffers::Vector<flatbuffers::Offset<
|
|
le_audio::CodecSpecificConfiguration>>* flat_codec_specific_params,
|
|
le_audio::CodecSpecificLtvGenericTypes type) {
|
|
auto it = std::find_if(
|
|
flat_codec_specific_params->cbegin(), flat_codec_specific_params->cend(),
|
|
[&type](const auto& csc) { return (csc->type() == type); });
|
|
return (it != flat_codec_specific_params->cend()) ? *it : nullptr;
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::populateAudioChannelAllocation(
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation&
|
|
audio_channel_allocation,
|
|
uint32_t audio_location) {
|
|
audio_channel_allocation.bitmask = 0;
|
|
for (auto [allocation, bitmask] : audio_channel_allocation_map) {
|
|
if (audio_location & allocation)
|
|
audio_channel_allocation.bitmask |= bitmask;
|
|
}
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::populateConfigurationData(
|
|
LeAudioAseConfiguration& ase,
|
|
const flatbuffers::Vector<
|
|
flatbuffers::Offset<le_audio::CodecSpecificConfiguration>>*
|
|
flat_codec_specific_params) {
|
|
uint8_t sampling_frequency = 0;
|
|
uint8_t frame_duration = 0;
|
|
uint32_t audio_channel_allocation = 0;
|
|
uint16_t octets_per_codec_frame = 0;
|
|
uint8_t codec_frames_blocks_per_sdu = 0;
|
|
|
|
auto param = LookupCodecSpecificParam(
|
|
flat_codec_specific_params,
|
|
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_SAMPLING_FREQUENCY);
|
|
if (param) {
|
|
auto ptr = param->compound_value()->value()->data();
|
|
STREAM_TO_UINT8(sampling_frequency, ptr);
|
|
}
|
|
|
|
param = LookupCodecSpecificParam(
|
|
flat_codec_specific_params,
|
|
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_FRAME_DURATION);
|
|
if (param) {
|
|
auto ptr = param->compound_value()->value()->data();
|
|
STREAM_TO_UINT8(frame_duration, ptr);
|
|
}
|
|
|
|
param = LookupCodecSpecificParam(
|
|
flat_codec_specific_params,
|
|
le_audio::
|
|
CodecSpecificLtvGenericTypes_SUPPORTED_AUDIO_CHANNEL_ALLOCATION);
|
|
if (param) {
|
|
auto ptr = param->compound_value()->value()->data();
|
|
STREAM_TO_UINT32(audio_channel_allocation, ptr);
|
|
}
|
|
|
|
param = LookupCodecSpecificParam(
|
|
flat_codec_specific_params,
|
|
le_audio::CodecSpecificLtvGenericTypes_SUPPORTED_OCTETS_PER_CODEC_FRAME);
|
|
if (param) {
|
|
auto ptr = param->compound_value()->value()->data();
|
|
STREAM_TO_UINT16(octets_per_codec_frame, ptr);
|
|
}
|
|
|
|
param = LookupCodecSpecificParam(
|
|
flat_codec_specific_params,
|
|
le_audio::
|
|
CodecSpecificLtvGenericTypes_SUPPORTED_CODEC_FRAME_BLOCKS_PER_SDU);
|
|
if (param) {
|
|
auto ptr = param->compound_value()->value()->data();
|
|
STREAM_TO_UINT8(codec_frames_blocks_per_sdu, ptr);
|
|
}
|
|
|
|
// Make the correct value
|
|
ase.codecConfiguration = std::vector<CodecSpecificConfigurationLtv>();
|
|
|
|
auto sampling_freq_it = sampling_freq_map.find(sampling_frequency);
|
|
if (sampling_freq_it != sampling_freq_map.end())
|
|
ase.codecConfiguration.push_back(sampling_freq_it->second);
|
|
auto frame_duration_it = frame_duration_map.find(frame_duration);
|
|
if (frame_duration_it != frame_duration_map.end())
|
|
ase.codecConfiguration.push_back(frame_duration_it->second);
|
|
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation channel_allocation;
|
|
populateAudioChannelAllocation(channel_allocation, audio_channel_allocation);
|
|
ase.codecConfiguration.push_back(channel_allocation);
|
|
|
|
auto octet_structure = CodecSpecificConfigurationLtv::OctetsPerCodecFrame();
|
|
octet_structure.value = octets_per_codec_frame;
|
|
ase.codecConfiguration.push_back(octet_structure);
|
|
|
|
auto frame_sdu_structure =
|
|
CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU();
|
|
frame_sdu_structure.value = codec_frames_blocks_per_sdu;
|
|
ase.codecConfiguration.push_back(frame_sdu_structure);
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::populateAseConfiguration(
|
|
const std::string& name, LeAudioAseConfiguration& ase,
|
|
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
|
const le_audio::QosConfiguration* qos_cfg) {
|
|
// Target latency
|
|
switch (qos_cfg->target_latency()) {
|
|
case le_audio::AudioSetConfigurationTargetLatency::
|
|
AudioSetConfigurationTargetLatency_BALANCED_RELIABILITY:
|
|
ase.targetLatency =
|
|
LeAudioAseConfiguration::TargetLatency::BALANCED_LATENCY_RELIABILITY;
|
|
break;
|
|
case le_audio::AudioSetConfigurationTargetLatency::
|
|
AudioSetConfigurationTargetLatency_HIGH_RELIABILITY:
|
|
ase.targetLatency =
|
|
LeAudioAseConfiguration::TargetLatency::HIGHER_RELIABILITY;
|
|
break;
|
|
case le_audio::AudioSetConfigurationTargetLatency::
|
|
AudioSetConfigurationTargetLatency_LOW:
|
|
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::LOWER;
|
|
break;
|
|
default:
|
|
ase.targetLatency = LeAudioAseConfiguration::TargetLatency::UNDEFINED;
|
|
break;
|
|
};
|
|
|
|
ase.targetPhy = Phy::TWO_M;
|
|
// Making CodecId
|
|
if (flat_subconfig->codec_id()->coding_format() ==
|
|
(uint8_t)CodecId::Core::LC3) {
|
|
ase.codecId = CodecId::Core::LC3;
|
|
} else {
|
|
auto vendorC = CodecId::Vendor();
|
|
vendorC.codecId = flat_subconfig->codec_id()->vendor_codec_id();
|
|
vendorC.id = flat_subconfig->codec_id()->vendor_company_id();
|
|
ase.codecId = vendorC;
|
|
}
|
|
// Codec configuration data
|
|
populateConfigurationData(ase, flat_subconfig->codec_configuration());
|
|
// Populate the config name for easier debug
|
|
auto meta = std::vector<std::optional<MetadataLtv>>();
|
|
MetadataLtv::VendorSpecific cfg_name;
|
|
cfg_name.opaqueValue = std::vector<uint8_t>(name.begin(), name.end());
|
|
meta.push_back(cfg_name);
|
|
ase.metadata = meta;
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::populateAseQosConfiguration(
|
|
LeAudioAseQosConfiguration& qos, const le_audio::QosConfiguration* qos_cfg,
|
|
LeAudioAseConfiguration& ase, uint8_t ase_channel_cnt) {
|
|
std::optional<CodecSpecificConfigurationLtv::CodecFrameBlocksPerSDU>
|
|
frameBlock = std::nullopt;
|
|
std::optional<CodecSpecificConfigurationLtv::FrameDuration> frameDuration =
|
|
std::nullopt;
|
|
std::optional<CodecSpecificConfigurationLtv::OctetsPerCodecFrame> octet =
|
|
std::nullopt;
|
|
|
|
// Hack to put back allocation
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation allocation =
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation();
|
|
if (ase_channel_cnt == 1) {
|
|
allocation.bitmask |=
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_CENTER;
|
|
|
|
} else {
|
|
allocation.bitmask |=
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_LEFT |
|
|
CodecSpecificConfigurationLtv::AudioChannelAllocation::FRONT_RIGHT;
|
|
}
|
|
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::octetsPerCodecFrame) {
|
|
octet = cfg_ltv.get<CodecSpecificConfigurationLtv::octetsPerCodecFrame>();
|
|
} else if (tag == CodecSpecificConfigurationLtv::audioChannelAllocation) {
|
|
// Change to the old hack allocation
|
|
cfg_ltv.set<CodecSpecificConfigurationLtv::audioChannelAllocation>(
|
|
allocation);
|
|
}
|
|
}
|
|
|
|
int frameBlockValue = 1;
|
|
if (frameBlock.has_value()) frameBlockValue = frameBlock.value().value;
|
|
|
|
// Populate maxSdu
|
|
if (octet.has_value()) {
|
|
qos.maxSdu = ase_channel_cnt * 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();
|
|
}
|
|
|
|
// Parse into AseDirectionConfiguration
|
|
AseDirectionConfiguration
|
|
AudioSetConfigurationProviderJson::SetConfigurationFromFlatSubconfig(
|
|
const std::string& name,
|
|
const le_audio::AudioSetSubConfiguration* flat_subconfig,
|
|
const le_audio::QosConfiguration* qos_cfg, CodecLocation location) {
|
|
AseDirectionConfiguration direction_conf;
|
|
|
|
LeAudioAseConfiguration ase;
|
|
LeAudioAseQosConfiguration qos;
|
|
LeAudioDataPathConfiguration path;
|
|
|
|
// Translate into LeAudioAseConfiguration
|
|
populateAseConfiguration(name, ase, flat_subconfig, qos_cfg);
|
|
|
|
// Translate into LeAudioAseQosConfiguration
|
|
populateAseQosConfiguration(qos, qos_cfg, ase,
|
|
flat_subconfig->ase_channel_cnt());
|
|
|
|
// Translate location to data path id
|
|
switch (location) {
|
|
case CodecLocation::ADSP:
|
|
path.isoDataPathConfiguration.isTransparent = true;
|
|
path.dataPathId = kIsoDataPathPlatformDefault;
|
|
break;
|
|
case CodecLocation::HOST:
|
|
path.isoDataPathConfiguration.isTransparent = true;
|
|
path.dataPathId = kIsoDataPathHci;
|
|
break;
|
|
case CodecLocation::CONTROLLER:
|
|
path.isoDataPathConfiguration.isTransparent = false;
|
|
path.dataPathId = kIsoDataPathPlatformDefault;
|
|
break;
|
|
}
|
|
// Move codecId to iso data path
|
|
path.isoDataPathConfiguration.codecId = ase.codecId.value();
|
|
|
|
direction_conf.aseConfiguration = ase;
|
|
direction_conf.qosConfiguration = qos;
|
|
direction_conf.dataPathConfiguration = path;
|
|
|
|
return direction_conf;
|
|
}
|
|
|
|
// Parse into AseDirectionConfiguration and the ConfigurationFlags
|
|
// and put them in the given list.
|
|
void AudioSetConfigurationProviderJson::processSubconfig(
|
|
const std::string& name,
|
|
const le_audio::AudioSetSubConfiguration* subconfig,
|
|
const le_audio::QosConfiguration* qos_cfg,
|
|
std::vector<std::optional<AseDirectionConfiguration>>&
|
|
directionAseConfiguration,
|
|
CodecLocation location) {
|
|
auto ase_cnt = subconfig->ase_cnt();
|
|
auto config =
|
|
SetConfigurationFromFlatSubconfig(name, subconfig, qos_cfg, location);
|
|
directionAseConfiguration.push_back(config);
|
|
// Put the same setting again.
|
|
if (ase_cnt == 2) directionAseConfiguration.push_back(config);
|
|
}
|
|
|
|
void AudioSetConfigurationProviderJson::PopulateAseConfigurationFromFlat(
|
|
const le_audio::AudioSetConfiguration* flat_cfg,
|
|
std::vector<const le_audio::CodecConfiguration*>* codec_cfgs,
|
|
std::vector<const le_audio::QosConfiguration*>* qos_cfgs,
|
|
CodecLocation location,
|
|
std::vector<std::optional<AseDirectionConfiguration>>&
|
|
sourceAseConfiguration,
|
|
std::vector<std::optional<AseDirectionConfiguration>>& sinkAseConfiguration,
|
|
ConfigurationFlags& /*configurationFlags*/) {
|
|
if (flat_cfg == nullptr) {
|
|
LOG(ERROR) << "flat_cfg cannot be null";
|
|
return;
|
|
}
|
|
std::string codec_config_key = flat_cfg->codec_config_name()->str();
|
|
auto* qos_config_key_array = flat_cfg->qos_config_name();
|
|
|
|
constexpr std::string_view default_qos = "QoS_Config_Balanced_Reliability";
|
|
|
|
std::string qos_sink_key(default_qos);
|
|
std::string qos_source_key(default_qos);
|
|
|
|
/* We expect maximum two QoS settings. First for Sink and second for Source
|
|
*/
|
|
if (qos_config_key_array->size() > 0) {
|
|
qos_sink_key = qos_config_key_array->Get(0)->str();
|
|
if (qos_config_key_array->size() > 1) {
|
|
qos_source_key = qos_config_key_array->Get(1)->str();
|
|
} else {
|
|
qos_source_key = qos_sink_key;
|
|
}
|
|
}
|
|
|
|
LOG(INFO) << "Audio set config " << flat_cfg->name()->c_str()
|
|
<< ": codec config " << codec_config_key.c_str() << ", qos_sink "
|
|
<< qos_sink_key.c_str() << ", qos_source "
|
|
<< qos_source_key.c_str();
|
|
|
|
// Find the first qos config that match the name
|
|
const le_audio::QosConfiguration* qos_sink_cfg = nullptr;
|
|
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
|
|
if ((*i)->name()->str() == qos_sink_key) {
|
|
qos_sink_cfg = *i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const le_audio::QosConfiguration* qos_source_cfg = nullptr;
|
|
for (auto i = qos_cfgs->begin(); i != qos_cfgs->end(); ++i) {
|
|
if ((*i)->name()->str() == qos_source_key) {
|
|
qos_source_cfg = *i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// First codec_cfg with the same name
|
|
const le_audio::CodecConfiguration* codec_cfg = nullptr;
|
|
for (auto i = codec_cfgs->begin(); i != codec_cfgs->end(); ++i) {
|
|
if ((*i)->name()->str() == codec_config_key) {
|
|
codec_cfg = *i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process each subconfig and put it into the correct list
|
|
if (codec_cfg != nullptr && codec_cfg->subconfigurations()) {
|
|
/* Load subconfigurations */
|
|
for (auto subconfig : *codec_cfg->subconfigurations()) {
|
|
if (subconfig->direction() == kLeAudioDirectionSink) {
|
|
processSubconfig(flat_cfg->name()->str(), subconfig, qos_sink_cfg,
|
|
sinkAseConfiguration, location);
|
|
} else {
|
|
processSubconfig(flat_cfg->name()->str(), subconfig, qos_source_cfg,
|
|
sourceAseConfiguration, location);
|
|
}
|
|
}
|
|
} else {
|
|
if (codec_cfg == nullptr) {
|
|
LOG(ERROR) << "No codec config matching key " << codec_config_key.c_str()
|
|
<< " found";
|
|
} else {
|
|
LOG(ERROR) << "Configuration '" << flat_cfg->name()->c_str()
|
|
<< "' has no valid subconfigurations.";
|
|
}
|
|
}
|
|
|
|
// TODO: Populate information for ConfigurationFlags
|
|
}
|
|
|
|
bool AudioSetConfigurationProviderJson::LoadConfigurationsFromFiles(
|
|
const char* schema_file, const char* content_file, CodecLocation location) {
|
|
flatbuffers::Parser configurations_parser_;
|
|
std::string configurations_schema_binary_content;
|
|
bool ok = flatbuffers::LoadFile(schema_file, true,
|
|
&configurations_schema_binary_content);
|
|
LOG(INFO) << __func__ << ": Loading file " << schema_file;
|
|
if (!ok) return ok;
|
|
|
|
/* Load the binary schema */
|
|
ok = configurations_parser_.Deserialize(
|
|
(uint8_t*)configurations_schema_binary_content.c_str(),
|
|
configurations_schema_binary_content.length());
|
|
if (!ok) return ok;
|
|
|
|
/* Load the content from JSON */
|
|
std::string configurations_json_content;
|
|
LOG(INFO) << __func__ << ": Loading file " << content_file;
|
|
ok = flatbuffers::LoadFile(content_file, false, &configurations_json_content);
|
|
if (!ok) return ok;
|
|
|
|
/* Parse */
|
|
LOG(INFO) << __func__ << ": Parse JSON content";
|
|
ok = configurations_parser_.Parse(configurations_json_content.c_str());
|
|
if (!ok) return ok;
|
|
|
|
/* Import from flatbuffers */
|
|
LOG(INFO) << __func__ << ": Build flat buffer structure";
|
|
auto configurations_root = le_audio::GetAudioSetConfigurations(
|
|
configurations_parser_.builder_.GetBufferPointer());
|
|
if (!configurations_root) return false;
|
|
|
|
auto flat_qos_configs = configurations_root->qos_configurations();
|
|
if ((flat_qos_configs == nullptr) || (flat_qos_configs->size() == 0))
|
|
return false;
|
|
|
|
LOG(DEBUG) << ": Updating " << flat_qos_configs->size()
|
|
<< " qos config entries.";
|
|
std::vector<const le_audio::QosConfiguration*> qos_cfgs;
|
|
for (auto const& flat_qos_cfg : *flat_qos_configs) {
|
|
qos_cfgs.push_back(flat_qos_cfg);
|
|
}
|
|
|
|
auto flat_codec_configs = configurations_root->codec_configurations();
|
|
if ((flat_codec_configs == nullptr) || (flat_codec_configs->size() == 0))
|
|
return false;
|
|
|
|
LOG(DEBUG) << ": Updating " << flat_codec_configs->size()
|
|
<< " codec config entries.";
|
|
std::vector<const le_audio::CodecConfiguration*> codec_cfgs;
|
|
for (auto const& flat_codec_cfg : *flat_codec_configs) {
|
|
codec_cfgs.push_back(flat_codec_cfg);
|
|
}
|
|
|
|
auto flat_configs = configurations_root->configurations();
|
|
if ((flat_configs == nullptr) || (flat_configs->size() == 0)) return false;
|
|
|
|
LOG(DEBUG) << ": Updating " << flat_configs->size() << " config entries.";
|
|
for (auto const& flat_cfg : *flat_configs) {
|
|
// Create 3 vector to use
|
|
std::vector<std::optional<AseDirectionConfiguration>>
|
|
sourceAseConfiguration;
|
|
std::vector<std::optional<AseDirectionConfiguration>> sinkAseConfiguration;
|
|
ConfigurationFlags configurationFlags;
|
|
PopulateAseConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs, location,
|
|
sourceAseConfiguration,
|
|
sinkAseConfiguration, configurationFlags);
|
|
if (sourceAseConfiguration.empty() && sinkAseConfiguration.empty())
|
|
continue;
|
|
configurations_[flat_cfg->name()->str()] = std::make_tuple(
|
|
sourceAseConfiguration, sinkAseConfiguration, configurationFlags);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AudioSetConfigurationProviderJson::LoadScenariosFromFiles(
|
|
const char* schema_file, const char* content_file) {
|
|
flatbuffers::Parser scenarios_parser_;
|
|
std::string scenarios_schema_binary_content;
|
|
bool ok = flatbuffers::LoadFile(schema_file, true,
|
|
&scenarios_schema_binary_content);
|
|
LOG(INFO) << __func__ << ": Loading file " << schema_file;
|
|
if (!ok) return ok;
|
|
|
|
/* Load the binary schema */
|
|
ok = scenarios_parser_.Deserialize(
|
|
(uint8_t*)scenarios_schema_binary_content.c_str(),
|
|
scenarios_schema_binary_content.length());
|
|
if (!ok) return ok;
|
|
|
|
/* Load the content from JSON */
|
|
LOG(INFO) << __func__ << ": Loading file " << content_file;
|
|
std::string scenarios_json_content;
|
|
ok = flatbuffers::LoadFile(content_file, false, &scenarios_json_content);
|
|
if (!ok) return ok;
|
|
|
|
/* Parse */
|
|
LOG(INFO) << __func__ << ": Parse json content";
|
|
ok = scenarios_parser_.Parse(scenarios_json_content.c_str());
|
|
if (!ok) return ok;
|
|
|
|
/* Import from flatbuffers */
|
|
LOG(INFO) << __func__ << ": Build flat buffer structure";
|
|
auto scenarios_root = le_audio::GetAudioSetScenarios(
|
|
scenarios_parser_.builder_.GetBufferPointer());
|
|
if (!scenarios_root) return false;
|
|
|
|
auto flat_scenarios = scenarios_root->scenarios();
|
|
if ((flat_scenarios == nullptr) || (flat_scenarios->size() == 0))
|
|
return false;
|
|
|
|
LOG(INFO) << __func__ << ": Turn flat buffer into structure";
|
|
AudioContext media_context = AudioContext();
|
|
media_context.bitmask =
|
|
(AudioContext::ALERTS | AudioContext::INSTRUCTIONAL |
|
|
AudioContext::NOTIFICATIONS | AudioContext::EMERGENCY_ALARM |
|
|
AudioContext::UNSPECIFIED | AudioContext::MEDIA |
|
|
AudioContext::SOUND_EFFECTS);
|
|
|
|
AudioContext conversational_context = AudioContext();
|
|
conversational_context.bitmask =
|
|
(AudioContext::RINGTONE_ALERTS | AudioContext::CONVERSATIONAL);
|
|
|
|
AudioContext live_context = AudioContext();
|
|
live_context.bitmask = AudioContext::LIVE_AUDIO;
|
|
|
|
AudioContext game_context = AudioContext();
|
|
game_context.bitmask = AudioContext::GAME;
|
|
|
|
AudioContext voice_assistants_context = AudioContext();
|
|
voice_assistants_context.bitmask = AudioContext::VOICE_ASSISTANTS;
|
|
|
|
LOG(DEBUG) << "Updating " << flat_scenarios->size() << " scenarios.";
|
|
for (auto const& scenario : *flat_scenarios) {
|
|
if (!scenario->configurations()) continue;
|
|
std::string scenario_name = scenario->name()->c_str();
|
|
AudioContext context;
|
|
if (scenario_name == "Media")
|
|
context = AudioContext(media_context);
|
|
else if (scenario_name == "Conversational")
|
|
context = AudioContext(conversational_context);
|
|
else if (scenario_name == "Live")
|
|
context = AudioContext(live_context);
|
|
else if (scenario_name == "Game")
|
|
context = AudioContext(game_context);
|
|
else if (scenario_name == "VoiceAssistants")
|
|
context = AudioContext(voice_assistants_context);
|
|
LOG(DEBUG) << "Scenario " << scenario->name()->c_str()
|
|
<< " configs: " << scenario->configurations()->size()
|
|
<< " context: " << context.toString();
|
|
|
|
for (auto it = scenario->configurations()->begin();
|
|
it != scenario->configurations()->end(); ++it) {
|
|
auto config_name = it->str();
|
|
auto configuration = configurations_.find(config_name);
|
|
if (configuration == configurations_.end()) continue;
|
|
LOG(DEBUG) << "Getting configuration with name: " << config_name;
|
|
auto [source, sink, flags] = configuration->second;
|
|
// Each configuration will create a LeAudioAseConfigurationSetting
|
|
// with the same {context, packing}
|
|
// and different data
|
|
LeAudioAseConfigurationSetting setting;
|
|
setting.audioContext = context;
|
|
// TODO: Packing
|
|
setting.sourceAseConfiguration = source;
|
|
setting.sinkAseConfiguration = sink;
|
|
setting.flags = flags;
|
|
// Add to list of setting
|
|
LOG(DEBUG) << "Pushing configuration to list: " << config_name;
|
|
ase_configuration_settings_.push_back(setting);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AudioSetConfigurationProviderJson::LoadContent(
|
|
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
|
config_files,
|
|
std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
|
|
scenario_files,
|
|
CodecLocation location) {
|
|
bool is_loaded_config = false;
|
|
for (auto [schema, content] : config_files) {
|
|
if (LoadConfigurationsFromFiles(schema, content, location)) {
|
|
is_loaded_config = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool is_loaded_scenario = false;
|
|
for (auto [schema, content] : scenario_files) {
|
|
if (LoadScenariosFromFiles(schema, content)) {
|
|
is_loaded_scenario = true;
|
|
break;
|
|
}
|
|
}
|
|
return is_loaded_config && is_loaded_scenario;
|
|
}
|
|
|
|
} // namespace audio
|
|
} // namespace bluetooth
|
|
} // namespace hardware
|
|
} // namespace android
|
|
} // namespace aidl
|