Loading bluetooth/audio/utils/Android.bp +15 −1 Original line number Original line Diff line number Diff line Loading @@ -40,9 +40,13 @@ cc_library_shared { "aidl_session/BluetoothAudioCodecs.cpp", "aidl_session/BluetoothAudioCodecs.cpp", "aidl_session/BluetoothAudioSession.cpp", "aidl_session/BluetoothAudioSession.cpp", "aidl_session/HidlToAidlMiddleware.cpp", "aidl_session/HidlToAidlMiddleware.cpp", "aidl_session/BluetoothLeAudioCodecsProvider.cpp", ], ], export_include_dirs: ["aidl_session/"], export_include_dirs: ["aidl_session/"], header_libs: ["libhardware_headers"], header_libs: [ "libhardware_headers", "libxsdc-utils", ], shared_libs: [ shared_libs: [ "android.hardware.bluetooth.audio@2.0", "android.hardware.bluetooth.audio@2.0", "android.hardware.bluetooth.audio@2.1", "android.hardware.bluetooth.audio@2.1", Loading @@ -53,5 +57,15 @@ cc_library_shared { "liblog", "liblog", "android.hardware.bluetooth.audio-V2-ndk", "android.hardware.bluetooth.audio-V2-ndk", "libhidlbase", "libhidlbase", "libxml2", ], ], generated_sources: ["le_audio_codec_capabilities"], generated_headers: ["le_audio_codec_capabilities"], } xsd_config { name: "le_audio_codec_capabilities", srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"], package_name: "aidl.android.hardware.bluetooth.audio.setting", api_dir: "le_audio_codec_capabilities/schema", } } bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp +4 −109 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ #include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h> #include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h> #include <android-base/logging.h> #include <android-base/logging.h> #include "BluetoothLeAudioCodecsProvider.h" namespace aidl { namespace aidl { namespace android { namespace android { namespace hardware { namespace hardware { Loading Loading @@ -96,67 +98,6 @@ const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = { std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities; std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities; static const UnicastCapability kInvalidUnicastCapability = { .codecType = CodecType::UNKNOWN}; static const BroadcastCapability kInvalidBroadcastCapability = { .codecType = CodecType::UNKNOWN}; // Default Supported Codecs // LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30 static const Lc3Capabilities kLc3Capability_16_1 = { .samplingFrequencyHz = {16000}, .frameDurationUs = {7500}, .octetsPerFrame = {30}}; // Default Supported Codecs // LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40 static const Lc3Capabilities kLc3Capability_16_2 = { .samplingFrequencyHz = {16000}, .frameDurationUs = {10000}, .octetsPerFrame = {40}}; // Default Supported Codecs // LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60 static const Lc3Capabilities kLc3Capability_24_2 = { .samplingFrequencyHz = {24000}, .frameDurationUs = {10000}, .octetsPerFrame = {60}}; // Default Supported Codecs // LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80 static const Lc3Capabilities kLc3Capability_32_2 = { .samplingFrequencyHz = {32000}, .frameDurationUs = {10000}, .octetsPerFrame = {80}}; // Default Supported Codecs // LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120 static const Lc3Capabilities kLc3Capability_48_4 = { .samplingFrequencyHz = {48000}, .frameDurationUs = {10000}, .octetsPerFrame = {120}}; static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = { kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2, kLc3Capability_16_2, kLc3Capability_16_1}; static AudioLocation stereoAudio = static_cast<AudioLocation>( static_cast<uint8_t>(AudioLocation::FRONT_LEFT) | static_cast<uint8_t>(AudioLocation::FRONT_RIGHT)); static AudioLocation monoAudio = AudioLocation::UNKNOWN; // Stores the supported setting of audio location, connected device, and the // channel count for each device std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>> supportedDeviceSetting = { // Stereo, two connected device, one for L one for R std::make_tuple(stereoAudio, 2, 1), // Stereo, one connected device for both L and R std::make_tuple(stereoAudio, 1, 2), // Mono std::make_tuple(monoAudio, 1, 1)}; template <class T> template <class T> bool BluetoothAudioCodecs::ContainedInVector( bool BluetoothAudioCodecs::ContainedInVector( const std::vector<T>& vector, const typename identity<T>::type& target) { const std::vector<T>& vector, const typename identity<T>::type& target) { Loading Loading @@ -444,19 +385,6 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( return false; return false; } } UnicastCapability composeUnicastLc3Capability( AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount, const Lc3Capabilities& capability) { return { .codecType = CodecType::LC3, .supportedChannel = audioLocation, .deviceCount = deviceCnt, .channelCountPerDevice = channelCount, .leAudioCodecCapabilities = UnicastCapability::LeAudioCodecCapabilities(capability), }; } std::vector<LeAudioCodecCapabilitiesSetting> std::vector<LeAudioCodecCapabilitiesSetting> BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( const SessionType& session_type) { const SessionType& session_type) { Loading @@ -470,41 +398,8 @@ BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( } } if (kDefaultOffloadLeAudioCapabilities.empty()) { if (kDefaultOffloadLeAudioCapabilities.empty()) { for (auto [audioLocation, deviceCnt, channelCount] : kDefaultOffloadLeAudioCapabilities = supportedDeviceSetting) { BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(); for (auto capability : supportedLc3CapabilityList) { UnicastCapability lc3Capability = composeUnicastLc3Capability( audioLocation, deviceCnt, channelCount, capability); UnicastCapability lc3MonoDecodeCapability = composeUnicastLc3Capability(monoAudio, 1, 1, capability); // Adds the capability for encode only kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = kInvalidUnicastCapability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for decode only kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = kInvalidUnicastCapability, .unicastDecodeCapability = lc3Capability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for the case that encode and decode exist at the // same time(force one active device for decode) kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = lc3MonoDecodeCapability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for the case that encode and decode exist at the // same time kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = lc3Capability, .broadcastCapability = kInvalidBroadcastCapability}); } } } } return kDefaultOffloadLeAudioCapabilities; return kDefaultOffloadLeAudioCapabilities; Loading bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp 0 → 100644 +312 −0 Original line number Original line Diff line number Diff line /* * 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<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; std::vector<LeAudioCodecCapabilitiesSetting> BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities() { if (!leAudioCodecCapabilities.empty()) { return leAudioCodecCapabilities; } const auto le_audio_offload_setting = setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile); if (!le_audio_offload_setting.has_value()) { LOG(ERROR) << __func__ << ": Failed to read " << kLeAudioCodecCapabilitiesFile; return {}; } std::vector<setting::Scenario> 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); return leAudioCodecCapabilities; } 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)); } } } } } 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()); // 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 <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()}}; } 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 bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h 0 → 100644 +87 −0 Original line number Original line Diff line number Diff line /* * 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. */ #pragma once #include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h> #include <android-base/logging.h> #include <unordered_map> #include "aidl_android_hardware_bluetooth_audio_setting.h" namespace aidl { namespace android { namespace hardware { namespace bluetooth { namespace audio { class BluetoothLeAudioCodecsProvider { public: static std::vector<LeAudioCodecCapabilitiesSetting> GetLeAudioCodecCapabilities(); private: static inline std::unordered_map<std::string, setting::Configuration> configuration_map_; static inline std::unordered_map<std::string, setting::CodecConfiguration> codec_configuration_map_; static inline std::unordered_map<std::string, setting::StrategyConfiguration> strategy_configuration_map_; static std::vector<setting::Scenario> GetScenarios( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateCodecConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateStrategyConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static std::vector<LeAudioCodecCapabilitiesSetting> ComposeLeAudioCodecCapabilities( const std::vector<setting::Scenario>& supported_scenarios); static UnicastCapability GetUnicastCapability( const std::string& coding_direction); template <class T> static inline UnicastCapability ComposeUnicastCapability( const CodecType& codec_type, const AudioLocation& audio_location, const uint8_t& device_cnt, const uint8_t& channel_count, const T& capability); static inline Lc3Capabilities ComposeLc3Capability( const setting::CodecConfiguration& codec_configuration); static inline AudioLocation GetAudioLocation( const setting::AudioLocation& audio_location); static inline CodecType GetCodecType(const setting::CodecType& codec_type); static inline bool IsValidCodecConfiguration( const setting::CodecConfiguration& codec_configuration); static inline bool IsValidStrategyConfiguration( const setting::StrategyConfiguration& strategy_configuration); }; } // namespace audio } // namespace bluetooth } // namespace hardware } // namespace android } // namespace aidl bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml 0 → 100644 +61 −0 Original line number Original line Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <!--- This is an example to configure LE Audio hardware offload supported capability settings In the follow list, there would be only one list in this file. Add element into each list as needed. codecConfigurationList: Supported codec capability along with its parameter setting strategyConfigurationList: ASE Configuration strategies configurationList: For each configuration , there are two attributes - codecConfiguration - strategyConfiguration scenarioList: There would be only one `scenarios` group For each scenario, the are two attributes - encode - decode If a scenario is unidirectional, mark another direction as `invalid` The configuration should be chosen from `configurationList` --> <leAudioOffloadSetting> <scenarioList> <!-- encode only --> <scenario encode="OneChanMono_16_1" decode="invalid"/> <scenario encode="TwoChanStereo_16_1" decode="invalid"/> <scenario encode="OneChanStereo_16_1" decode="invalid"/> <scenario encode="OneChanMono_16_2" decode="invalid"/> <scenario encode="TwoChanStereo_16_2" decode="invalid"/> <scenario encode="OneChanStereo_16_2" decode="invalid"/> <!-- encode and decode --> <scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/> <scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/> <scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/> <scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/> <scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/> <scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/> <scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/> <scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/> </scenarioList> <configurationList> <configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/> <configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/> <configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/> <configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/> <configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/> <configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/> </configurationList> <codecConfigurationList> <codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/> <codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/> </codecConfigurationList> <strategyConfigurationList> <strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/> <strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/> <strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/> </strategyConfigurationList> </leAudioOffloadSetting> Loading
bluetooth/audio/utils/Android.bp +15 −1 Original line number Original line Diff line number Diff line Loading @@ -40,9 +40,13 @@ cc_library_shared { "aidl_session/BluetoothAudioCodecs.cpp", "aidl_session/BluetoothAudioCodecs.cpp", "aidl_session/BluetoothAudioSession.cpp", "aidl_session/BluetoothAudioSession.cpp", "aidl_session/HidlToAidlMiddleware.cpp", "aidl_session/HidlToAidlMiddleware.cpp", "aidl_session/BluetoothLeAudioCodecsProvider.cpp", ], ], export_include_dirs: ["aidl_session/"], export_include_dirs: ["aidl_session/"], header_libs: ["libhardware_headers"], header_libs: [ "libhardware_headers", "libxsdc-utils", ], shared_libs: [ shared_libs: [ "android.hardware.bluetooth.audio@2.0", "android.hardware.bluetooth.audio@2.0", "android.hardware.bluetooth.audio@2.1", "android.hardware.bluetooth.audio@2.1", Loading @@ -53,5 +57,15 @@ cc_library_shared { "liblog", "liblog", "android.hardware.bluetooth.audio-V2-ndk", "android.hardware.bluetooth.audio-V2-ndk", "libhidlbase", "libhidlbase", "libxml2", ], ], generated_sources: ["le_audio_codec_capabilities"], generated_headers: ["le_audio_codec_capabilities"], } xsd_config { name: "le_audio_codec_capabilities", srcs: ["le_audio_codec_capabilities/le_audio_codec_capabilities.xsd"], package_name: "aidl.android.hardware.bluetooth.audio.setting", api_dir: "le_audio_codec_capabilities/schema", } }
bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp +4 −109 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,8 @@ #include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h> #include <aidl/android/hardware/bluetooth/audio/SbcChannelMode.h> #include <android-base/logging.h> #include <android-base/logging.h> #include "BluetoothLeAudioCodecsProvider.h" namespace aidl { namespace aidl { namespace android { namespace android { namespace hardware { namespace hardware { Loading Loading @@ -96,67 +98,6 @@ const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = { std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities; std::vector<LeAudioCodecCapabilitiesSetting> kDefaultOffloadLeAudioCapabilities; static const UnicastCapability kInvalidUnicastCapability = { .codecType = CodecType::UNKNOWN}; static const BroadcastCapability kInvalidBroadcastCapability = { .codecType = CodecType::UNKNOWN}; // Default Supported Codecs // LC3 16_1: sample rate: 16 kHz, frame duration: 7.5 ms, octets per frame: 30 static const Lc3Capabilities kLc3Capability_16_1 = { .samplingFrequencyHz = {16000}, .frameDurationUs = {7500}, .octetsPerFrame = {30}}; // Default Supported Codecs // LC3 16_2: sample rate: 16 kHz, frame duration: 10 ms, octets per frame: 40 static const Lc3Capabilities kLc3Capability_16_2 = { .samplingFrequencyHz = {16000}, .frameDurationUs = {10000}, .octetsPerFrame = {40}}; // Default Supported Codecs // LC3 24_2: sample rate: 24 kHz, frame duration: 10 ms, octets per frame: 60 static const Lc3Capabilities kLc3Capability_24_2 = { .samplingFrequencyHz = {24000}, .frameDurationUs = {10000}, .octetsPerFrame = {60}}; // Default Supported Codecs // LC3 32_2: sample rate: 32 kHz, frame duration: 10 ms, octets per frame: 80 static const Lc3Capabilities kLc3Capability_32_2 = { .samplingFrequencyHz = {32000}, .frameDurationUs = {10000}, .octetsPerFrame = {80}}; // Default Supported Codecs // LC3 48_4: sample rate: 48 kHz, frame duration: 10 ms, octets per frame: 120 static const Lc3Capabilities kLc3Capability_48_4 = { .samplingFrequencyHz = {48000}, .frameDurationUs = {10000}, .octetsPerFrame = {120}}; static const std::vector<Lc3Capabilities> supportedLc3CapabilityList = { kLc3Capability_48_4, kLc3Capability_32_2, kLc3Capability_24_2, kLc3Capability_16_2, kLc3Capability_16_1}; static AudioLocation stereoAudio = static_cast<AudioLocation>( static_cast<uint8_t>(AudioLocation::FRONT_LEFT) | static_cast<uint8_t>(AudioLocation::FRONT_RIGHT)); static AudioLocation monoAudio = AudioLocation::UNKNOWN; // Stores the supported setting of audio location, connected device, and the // channel count for each device std::vector<std::tuple<AudioLocation, uint8_t, uint8_t>> supportedDeviceSetting = { // Stereo, two connected device, one for L one for R std::make_tuple(stereoAudio, 2, 1), // Stereo, one connected device for both L and R std::make_tuple(stereoAudio, 1, 2), // Mono std::make_tuple(monoAudio, 1, 1)}; template <class T> template <class T> bool BluetoothAudioCodecs::ContainedInVector( bool BluetoothAudioCodecs::ContainedInVector( const std::vector<T>& vector, const typename identity<T>::type& target) { const std::vector<T>& vector, const typename identity<T>::type& target) { Loading Loading @@ -444,19 +385,6 @@ bool BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( return false; return false; } } UnicastCapability composeUnicastLc3Capability( AudioLocation audioLocation, uint8_t deviceCnt, uint8_t channelCount, const Lc3Capabilities& capability) { return { .codecType = CodecType::LC3, .supportedChannel = audioLocation, .deviceCount = deviceCnt, .channelCountPerDevice = channelCount, .leAudioCodecCapabilities = UnicastCapability::LeAudioCodecCapabilities(capability), }; } std::vector<LeAudioCodecCapabilitiesSetting> std::vector<LeAudioCodecCapabilitiesSetting> BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( const SessionType& session_type) { const SessionType& session_type) { Loading @@ -470,41 +398,8 @@ BluetoothAudioCodecs::GetLeAudioOffloadCodecCapabilities( } } if (kDefaultOffloadLeAudioCapabilities.empty()) { if (kDefaultOffloadLeAudioCapabilities.empty()) { for (auto [audioLocation, deviceCnt, channelCount] : kDefaultOffloadLeAudioCapabilities = supportedDeviceSetting) { BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(); for (auto capability : supportedLc3CapabilityList) { UnicastCapability lc3Capability = composeUnicastLc3Capability( audioLocation, deviceCnt, channelCount, capability); UnicastCapability lc3MonoDecodeCapability = composeUnicastLc3Capability(monoAudio, 1, 1, capability); // Adds the capability for encode only kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = kInvalidUnicastCapability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for decode only kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = kInvalidUnicastCapability, .unicastDecodeCapability = lc3Capability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for the case that encode and decode exist at the // same time(force one active device for decode) kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = lc3MonoDecodeCapability, .broadcastCapability = kInvalidBroadcastCapability}); // Adds the capability for the case that encode and decode exist at the // same time kDefaultOffloadLeAudioCapabilities.push_back( {.unicastEncodeCapability = lc3Capability, .unicastDecodeCapability = lc3Capability, .broadcastCapability = kInvalidBroadcastCapability}); } } } } return kDefaultOffloadLeAudioCapabilities; return kDefaultOffloadLeAudioCapabilities; Loading
bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.cpp 0 → 100644 +312 −0 Original line number Original line Diff line number Diff line /* * 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<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; std::vector<LeAudioCodecCapabilitiesSetting> BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities() { if (!leAudioCodecCapabilities.empty()) { return leAudioCodecCapabilities; } const auto le_audio_offload_setting = setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile); if (!le_audio_offload_setting.has_value()) { LOG(ERROR) << __func__ << ": Failed to read " << kLeAudioCodecCapabilitiesFile; return {}; } std::vector<setting::Scenario> 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); return leAudioCodecCapabilities; } 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)); } } } } } 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()); // 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 <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()}}; } 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
bluetooth/audio/utils/aidl_session/BluetoothLeAudioCodecsProvider.h 0 → 100644 +87 −0 Original line number Original line Diff line number Diff line /* * 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. */ #pragma once #include <aidl/android/hardware/bluetooth/audio/LeAudioCodecCapabilitiesSetting.h> #include <android-base/logging.h> #include <unordered_map> #include "aidl_android_hardware_bluetooth_audio_setting.h" namespace aidl { namespace android { namespace hardware { namespace bluetooth { namespace audio { class BluetoothLeAudioCodecsProvider { public: static std::vector<LeAudioCodecCapabilitiesSetting> GetLeAudioCodecCapabilities(); private: static inline std::unordered_map<std::string, setting::Configuration> configuration_map_; static inline std::unordered_map<std::string, setting::CodecConfiguration> codec_configuration_map_; static inline std::unordered_map<std::string, setting::StrategyConfiguration> strategy_configuration_map_; static std::vector<setting::Scenario> GetScenarios( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateCodecConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static void UpdateStrategyConfigurationsToMap( const std::optional<setting::LeAudioOffloadSetting>& le_audio_offload_setting); static std::vector<LeAudioCodecCapabilitiesSetting> ComposeLeAudioCodecCapabilities( const std::vector<setting::Scenario>& supported_scenarios); static UnicastCapability GetUnicastCapability( const std::string& coding_direction); template <class T> static inline UnicastCapability ComposeUnicastCapability( const CodecType& codec_type, const AudioLocation& audio_location, const uint8_t& device_cnt, const uint8_t& channel_count, const T& capability); static inline Lc3Capabilities ComposeLc3Capability( const setting::CodecConfiguration& codec_configuration); static inline AudioLocation GetAudioLocation( const setting::AudioLocation& audio_location); static inline CodecType GetCodecType(const setting::CodecType& codec_type); static inline bool IsValidCodecConfiguration( const setting::CodecConfiguration& codec_configuration); static inline bool IsValidStrategyConfiguration( const setting::StrategyConfiguration& strategy_configuration); }; } // namespace audio } // namespace bluetooth } // namespace hardware } // namespace android } // namespace aidl
bluetooth/audio/utils/le_audio_codec_capabilities/le_audio_codec_capabilities.xml 0 → 100644 +61 −0 Original line number Original line Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <!--- This is an example to configure LE Audio hardware offload supported capability settings In the follow list, there would be only one list in this file. Add element into each list as needed. codecConfigurationList: Supported codec capability along with its parameter setting strategyConfigurationList: ASE Configuration strategies configurationList: For each configuration , there are two attributes - codecConfiguration - strategyConfiguration scenarioList: There would be only one `scenarios` group For each scenario, the are two attributes - encode - decode If a scenario is unidirectional, mark another direction as `invalid` The configuration should be chosen from `configurationList` --> <leAudioOffloadSetting> <scenarioList> <!-- encode only --> <scenario encode="OneChanMono_16_1" decode="invalid"/> <scenario encode="TwoChanStereo_16_1" decode="invalid"/> <scenario encode="OneChanStereo_16_1" decode="invalid"/> <scenario encode="OneChanMono_16_2" decode="invalid"/> <scenario encode="TwoChanStereo_16_2" decode="invalid"/> <scenario encode="OneChanStereo_16_2" decode="invalid"/> <!-- encode and decode --> <scenario encode="OneChanStereo_16_1" decode="OneChanStereo_16_1"/> <scenario encode="OneChanStereo_16_1" decode="OneChanMono_16_1"/> <scenario encode="TwoChanStereo_16_1" decode="OneChanMono_16_1"/> <scenario encode="OneChanMono_16_1" decode="OneChanMono_16_1"/> <scenario encode="OneChanStereo_16_2" decode="OneChanStereo_16_2"/> <scenario encode="OneChanStereo_16_2" decode="OneChanMono_16_2"/> <scenario encode="TwoChanStereo_16_2" decode="OneChanMono_16_2"/> <scenario encode="OneChanMono_16_2" decode="OneChanMono_16_2"/> </scenarioList> <configurationList> <configuration name="OneChanMono_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/> <configuration name="TwoChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/> <configuration name="OneChanStereo_16_1" codecConfiguration="LC3_16k_1" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/> <configuration name="OneChanMono_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="MONO_ONE_CIS_PER_DEVICE"/> <configuration name="TwoChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_TWO_CISES_PER_DEVICE"/> <configuration name="OneChanStereo_16_2" codecConfiguration="LC3_16k_2" strategyConfiguration="STEREO_ONE_CIS_PER_DEVICE"/> </configurationList> <codecConfigurationList> <codecConfiguration name="LC3_16k_1" codec="LC3" samplingFrequency="16000" frameDurationUs="7500" octetsPerCodecFrame="30"/> <codecConfiguration name="LC3_16k_2" codec="LC3" samplingFrequency="16000" frameDurationUs="10000" octetsPerCodecFrame="40"/> </codecConfigurationList> <strategyConfigurationList> <strategyConfiguration name="STEREO_ONE_CIS_PER_DEVICE" audioLocation="STEREO" connectedDevice="2" channelCount="1"/> <strategyConfiguration name="STEREO_TWO_CISES_PER_DEVICE" audioLocation="STEREO" connectedDevice="1" channelCount="2"/> <strategyConfiguration name="MONO_ONE_CIS_PER_DEVICE" audioLocation="MONO" connectedDevice="1" channelCount="1"/> </strategyConfigurationList> </leAudioOffloadSetting>