diff --git a/bluetooth/audio/aidl/default/A2dpBits.h b/bluetooth/audio/aidl/default/A2dpBits.h new file mode 100644 index 0000000000..f467c9588f --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpBits.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#pragma once + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpBits { + const uint8_t* cdata_; + uint8_t* data_; + + public: + A2dpBits(const std::vector& vector) : cdata_(vector.data()) {} + + A2dpBits(std::vector& vector) + : cdata_(vector.data()), data_(vector.data()) {} + + struct Range { + const int first, len; + constexpr Range(int first, int last) + : first(first), len(last - first + 1) {} + constexpr Range(int index) : first(index), len(1) {} + }; + + constexpr bool get(int bit) const { + return (cdata_[bit >> 3] >> (7 - (bit & 7))) & 1; + } + + constexpr unsigned get(const Range& range) const { + unsigned v(0); + for (int i = 0; i < range.len; i++) + v |= get(range.first + i) << ((range.len - 1) - i); + return v; + } + + constexpr void set(int bit, int value = 1) { + uint8_t m = 1 << (7 - (bit & 7)); + if (value) + data_[bit >> 3] |= m; + else + data_[bit >> 3] &= ~m; + } + + constexpr void set(const Range& range, int value) { + for (int i = 0; i < range.len; i++) + set(range.first + i, (value >> ((range.len - 1) - i)) & 1); + } + + constexpr int find_active_bit(const Range& range) const { + unsigned v = get(range); + int i = 0; + for (; i < range.len && ((v >> i) & 1) == 0; i++) + ; + return i < range.len && (v ^ (1 << i)) == 0 + ? range.first + (range.len - 1) - i + : -1; + } +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp index 2d0d8c9e96..ba7a89d8be 100644 --- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp @@ -22,6 +22,10 @@ #include #include +#include "A2dpOffloadCodecAac.h" +#include "A2dpOffloadCodecFactory.h" +#include "A2dpOffloadCodecSbc.h" + namespace aidl { namespace android { namespace hardware { @@ -48,19 +52,44 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::startSession( const std::shared_ptr& host_if, const AudioConfiguration& audio_config, const std::vector& latency_modes, DataMQDesc* _aidl_return) { - if (audio_config.getTag() != AudioConfiguration::a2dpConfig) { - LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" - << audio_config.toString(); - *_aidl_return = DataMQDesc(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( - session_type_, audio_config.get())) { + if (audio_config.getTag() == AudioConfiguration::Tag::a2dp) { + auto a2dp_config = audio_config.get(); + A2dpStatus a2dp_status = A2dpStatus::NOT_SUPPORTED_CODEC_TYPE; + + if (a2dp_config.codecId == + A2dpOffloadCodecSbc::GetInstance()->GetCodecId()) { + SbcParameters sbc_parameters; + a2dp_status = A2dpOffloadCodecSbc::GetInstance()->ParseConfiguration( + a2dp_config.configuration, &sbc_parameters); + + } else if (a2dp_config.codecId == + A2dpOffloadCodecAac::GetInstance()->GetCodecId()) { + AacParameters aac_parameters; + a2dp_status = A2dpOffloadCodecAac::GetInstance()->ParseConfiguration( + a2dp_config.configuration, &aac_parameters); + } + if (a2dp_status != A2dpStatus::OK) { + LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" + << audio_config.toString(); + *_aidl_return = DataMQDesc(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } else if (audio_config.getTag() == AudioConfiguration::Tag::a2dpConfig) { + if (!BluetoothAudioCodecs::IsOffloadCodecConfigurationValid( + session_type_, + audio_config.get())) { + LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" + << audio_config.toString(); + *_aidl_return = DataMQDesc(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + } else { LOG(WARNING) << __func__ << " - Invalid Audio Configuration=" << audio_config.toString(); *_aidl_return = DataMQDesc(); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + return BluetoothAudioProvider::startSession( host_if, audio_config, latency_modes, _aidl_return); } @@ -73,6 +102,36 @@ ndk::ScopedAStatus A2dpOffloadAudioProvider::onSessionReady( return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration( + const CodecId& codec_id, const std::vector& configuration, + CodecParameters* codec_parameters, A2dpStatus* _aidl_return) { + auto codec = A2dpOffloadCodecFactory::GetInstance()->GetCodec(codec_id); + if (!codec) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " - CodecId=" << codec_id.toString() << " is not found"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + *_aidl_return = codec->ParseConfiguration(configuration, codec_parameters); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus A2dpOffloadAudioProvider::getA2dpConfiguration( + const std::vector& remote_a2dp_capabilities, + const A2dpConfigurationHint& hint, + std::optional* _aidl_return) { + *_aidl_return = std::nullopt; + A2dpConfiguration avdtp_configuration; + + if (A2dpOffloadCodecFactory::GetInstance()->GetConfiguration( + remote_a2dp_capabilities, hint, &avdtp_configuration)) + *_aidl_return = + std::make_optional(std::move(avdtp_configuration)); + + return ndk::ScopedAStatus::ok(); +} + } // namespace audio } // namespace bluetooth } // namespace hardware diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h index e6f188bda6..7cc6deeada 100644 --- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h +++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.h @@ -34,7 +34,16 @@ class A2dpOffloadAudioProvider : public BluetoothAudioProvider { const std::shared_ptr& host_if, const AudioConfiguration& audio_config, const std::vector& latency_modes, - DataMQDesc* _aidl_return); + DataMQDesc* _aidl_return) override; + + ndk::ScopedAStatus parseA2dpConfiguration( + const CodecId& codec_id, const std::vector& configuration, + CodecParameters* codec_parameters, A2dpStatus* _aidl_return) override; + + ndk::ScopedAStatus getA2dpConfiguration( + const std::vector& remote_a2dp_capabilities, + const A2dpConfigurationHint& hint, + std::optional* _aidl_return) override; private: ndk::ScopedAStatus onSessionReady(DataMQDesc* _aidl_return) override; diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodec.h b/bluetooth/audio/aidl/default/A2dpOffloadCodec.h new file mode 100644 index 0000000000..7ed5872346 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodec.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +#include "BluetoothAudioProviderFactory.h" + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpOffloadCodec { + protected: + A2dpOffloadCodec(const CodecInfo& info) : info(info) {} + virtual ~A2dpOffloadCodec() {} + + public: + const CodecInfo& info; + + const CodecId& GetCodecId() const { return info.id; } + + virtual A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const = 0; + + virtual bool BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const = 0; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp new file mode 100644 index 0000000000..0f5533a4d9 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.cpp @@ -0,0 +1,378 @@ +/* + * 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. + */ + +#include "A2dpOffloadCodecAac.h" + +#include "A2dpBits.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * AAC Local Capabilities + */ + +enum : bool { + kEnableObjectTypeMpeg2AacLc = true, + kEnableObjectTypeMpeg4AacLc = true, +}; + +enum : bool { + kEnableSamplingFrequency44100 = true, + kEnableSamplingFrequency48000 = true, + kEnableSamplingFrequency88200 = false, + kEnableSamplingFrequency96000 = false, +}; + +enum : bool { + kEnableChannels1 = true, + kEnableChannels2 = true, +}; + +enum : bool { + kEnableVbrSupported = true, +}; + +enum : int { + kBitdepth = 24, +}; + +/** + * AAC Signaling format [A2DP - 4.5] + */ + +// clang-format off + +constexpr A2dpBits::Range kObjectType ( 0, 6 ); +constexpr A2dpBits::Range kDrcEnable ( 7 ); +constexpr A2dpBits::Range kSamplingFrequency ( 8, 19 ); +constexpr A2dpBits::Range kChannels ( 20, 23 ); +constexpr A2dpBits::Range kVbrSupported ( 24 ); +constexpr A2dpBits::Range kBitrate ( 25, 47 ); +constexpr size_t kCapabilitiesSize = 48/8; + +// clang-format on + +enum { + kObjectTypeMpeg2AacLc = kObjectType.first, + kObjectTypeMpeg4AacLc, + kObjectTypeMpeg4AacLtp, + kObjectTypeMpeg4AacScalable, + kObjectTypeMpeg4AacHeV1, + kObjectTypeMpeg4AacHeV2, + kObjectTypeMpeg4AacEldV2 +}; + +enum { + kSamplingFrequency8000 = kSamplingFrequency.first, + kSamplingFrequency11025, + kSamplingFrequency12000, + kSamplingFrequency16000, + kSamplingFrequency22050, + kSamplingFrequency24000, + kSamplingFrequency32000, + kSamplingFrequency44100, + kSamplingFrequency48000, + kSamplingFrequency64000, + kSamplingFrequency88200, + kSamplingFrequency96000 +}; + +enum { kChannels1 = kChannels.first, kChannels2, kChannels51, kChannels71 }; + +/** + * AAC Conversion functions + */ + +static AacParameters::ObjectType GetObjectTypeEnum(int object_type) { + switch (object_type) { + case kObjectTypeMpeg2AacLc: + return AacParameters::ObjectType::MPEG2_AAC_LC; + case kObjectTypeMpeg4AacLc: + default: + return AacParameters::ObjectType::MPEG4_AAC_LC; + } +} + +static int GetSamplingFrequencyBit(int32_t sampling_frequency) { + switch (sampling_frequency) { + case 8000: + return kSamplingFrequency8000; + case 11025: + return kSamplingFrequency11025; + case 12000: + return kSamplingFrequency12000; + case 16000: + return kSamplingFrequency16000; + case 22050: + return kSamplingFrequency22050; + case 24000: + return kSamplingFrequency24000; + case 32000: + return kSamplingFrequency32000; + case 44100: + return kSamplingFrequency44100; + case 48000: + return kSamplingFrequency48000; + case 64000: + return kSamplingFrequency64000; + case 88200: + return kSamplingFrequency88200; + case 96000: + return kSamplingFrequency96000; + default: + return -1; + } +} + +static int32_t GetSamplingFrequencyValue(int sampling_frequency) { + switch (sampling_frequency) { + case kSamplingFrequency8000: + return 8000; + case kSamplingFrequency11025: + return 11025; + case kSamplingFrequency12000: + return 12000; + case kSamplingFrequency16000: + return 16000; + case kSamplingFrequency22050: + return 22050; + case kSamplingFrequency24000: + return 24000; + case kSamplingFrequency32000: + return 32000; + case kSamplingFrequency44100: + return 44100; + case kSamplingFrequency48000: + return 48000; + case kSamplingFrequency64000: + return 64000; + case kSamplingFrequency88200: + return 88200; + case kSamplingFrequency96000: + return 96000; + default: + return 0; + } +} + +static int GetChannelsBit(ChannelMode channel_mode) { + switch (channel_mode) { + case ChannelMode::MONO: + return kChannels1; + case ChannelMode::STEREO: + return kChannels2; + default: + return -1; + } +} + +static ChannelMode GetChannelModeEnum(int channel_mode) { + switch (channel_mode) { + case kChannels1: + return ChannelMode::MONO; + case kChannels2: + return ChannelMode::STEREO; + default: + return ChannelMode::UNKNOWN; + } +} + +/** + * AAC Class implementation + */ + +const A2dpOffloadCodecAac* A2dpOffloadCodecAac::GetInstance() { + static A2dpOffloadCodecAac instance; + return &instance; +} + +A2dpOffloadCodecAac::A2dpOffloadCodecAac() + : A2dpOffloadCodec(info_), + info_({.id = CodecId(CodecId::A2dp::AAC), .name = "AAC"}) { + info_.transport.set(); + auto& a2dp_info = info_.transport.get(); + + /* --- Setup Capabilities --- */ + + a2dp_info.capabilities.resize(kCapabilitiesSize); + std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0); + + auto capabilities = A2dpBits(a2dp_info.capabilities); + + capabilities.set(kObjectTypeMpeg2AacLc, kEnableObjectTypeMpeg2AacLc); + capabilities.set(kObjectTypeMpeg4AacLc, kEnableObjectTypeMpeg4AacLc); + + capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100); + capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000); + capabilities.set(kSamplingFrequency88200, kEnableSamplingFrequency88200); + capabilities.set(kSamplingFrequency96000, kEnableSamplingFrequency96000); + + capabilities.set(kChannels1, kEnableChannels1); + capabilities.set(kChannels2, kEnableChannels2); + + capabilities.set(kVbrSupported, kEnableVbrSupported); + + /* --- Setup Sampling Frequencies --- */ + + auto& sampling_frequency = a2dp_info.samplingFrequencyHz; + + for (auto v : {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 64000, 88200, 96000}) + if (capabilities.get(GetSamplingFrequencyBit(int32_t(v)))) + sampling_frequency.push_back(v); + + /* --- Setup Channel Modes --- */ + + auto& channel_modes = a2dp_info.channelMode; + + for (auto v : {ChannelMode::MONO, ChannelMode::STEREO}) + if (capabilities.get(GetChannelsBit(v))) channel_modes.push_back(v); + + /* --- Setup Bitdepth --- */ + + a2dp_info.bitdepth.push_back(kBitdepth); +} + +A2dpStatus A2dpOffloadCodecAac::ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters, AacParameters* aac_parameters) const { + auto& a2dp_info = info.transport.get(); + + if (configuration.size() != a2dp_info.capabilities.size()) + return A2dpStatus::BAD_LENGTH; + + auto config = A2dpBits(configuration); + auto lcaps = A2dpBits(a2dp_info.capabilities); + + /* --- Check Object Type --- */ + + int object_type = config.find_active_bit(kObjectType); + if (object_type < 0) return A2dpStatus::INVALID_OBJECT_TYPE; + if (!lcaps.get(object_type)) return A2dpStatus::NOT_SUPPORTED_OBJECT_TYPE; + + /* --- Check Sampling Frequency --- */ + + int sampling_frequency = config.find_active_bit(kSamplingFrequency); + if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY; + if (!lcaps.get(sampling_frequency)) + return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY; + + /* --- Check Channels --- */ + + int channels = config.find_active_bit(kChannels); + if (channels < 0) return A2dpStatus::INVALID_CHANNELS; + if (!lcaps.get(channels)) return A2dpStatus::NOT_SUPPORTED_CHANNELS; + + /* --- Check Bitrate --- */ + + bool vbr = config.get(kVbrSupported); + if (vbr && !lcaps.get(kVbrSupported)) return A2dpStatus::NOT_SUPPORTED_VBR; + + int bitrate = config.get(kBitrate); + if (vbr && lcaps.get(kBitrate) && bitrate > lcaps.get(kBitrate)) + return A2dpStatus::NOT_SUPPORTED_BIT_RATE; + + /* --- Return --- */ + + codec_parameters->channelMode = GetChannelModeEnum(channels); + codec_parameters->samplingFrequencyHz = + GetSamplingFrequencyValue(sampling_frequency); + codec_parameters->bitdepth = kBitdepth; + + codec_parameters->minBitrate = vbr ? 0 : bitrate; + codec_parameters->maxBitrate = bitrate; + + if (aac_parameters) + aac_parameters->object_type = GetObjectTypeEnum(object_type); + + return A2dpStatus::OK; +} + +bool A2dpOffloadCodecAac::BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const { + auto& a2dp_info = info_.transport.get(); + + if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false; + + auto lcaps = A2dpBits(a2dp_info.capabilities); + auto rcaps = A2dpBits(remote_capabilities); + + configuration->resize(a2dp_info.capabilities.size()); + std::fill(begin(*configuration), end(*configuration), 0); + auto config = A2dpBits(*configuration); + + /* --- Select Object Type --- */ + + if (lcaps.get(kObjectTypeMpeg2AacLc) && rcaps.get(kObjectTypeMpeg2AacLc)) + config.set(kObjectTypeMpeg2AacLc); + else if (lcaps.get(kObjectTypeMpeg4AacLc) && rcaps.get(kObjectTypeMpeg4AacLc)) + config.set(kObjectTypeMpeg4AacLc); + else + return false; + + /* --- Select Sampling Frequency --- */ + + auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1; + + if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint)) + config.set(sf_hint); + else if (lcaps.get(kSamplingFrequency96000) && + rcaps.get(kSamplingFrequency96000)) + config.set(kSamplingFrequency96000); + else if (lcaps.get(kSamplingFrequency88200) && + rcaps.get(kSamplingFrequency88200)) + config.set(kSamplingFrequency88200); + else if (lcaps.get(kSamplingFrequency48000) && + rcaps.get(kSamplingFrequency48000)) + config.set(kSamplingFrequency48000); + else if (lcaps.get(kSamplingFrequency44100) && + rcaps.get(kSamplingFrequency44100)) + config.set(kSamplingFrequency44100); + else + return false; + + /* --- Select Channels --- */ + + auto ch_hint = hint ? GetChannelsBit(hint->channelMode) : -1; + + if (ch_hint >= 0 && lcaps.get(ch_hint) && rcaps.get(ch_hint)) + config.set(ch_hint); + else if (lcaps.get(kChannels2) && rcaps.get(kChannels2)) + config.set(kChannels2); + else if (lcaps.get(kChannels1) && rcaps.get(kChannels1)) + config.set(kChannels1); + else + return false; + + /* --- Select Bitrate --- */ + + if (!hint || hint->minBitrate == 0) + config.set(kVbrSupported, + lcaps.get(kVbrSupported) && rcaps.get(kVbrSupported)); + + int32_t bitrate = lcaps.get(kBitrate); + if (hint && hint->maxBitrate > 0 && bitrate) + bitrate = std::min(hint->maxBitrate, bitrate); + else if (hint && hint->maxBitrate > 0) + bitrate = hint->maxBitrate; + config.set(kBitrate, bitrate); + + return true; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h new file mode 100644 index 0000000000..eefa89b521 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecAac.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +struct AacParameters : public CodecParameters { + enum class ObjectType { MPEG2_AAC_LC, MPEG4_AAC_LC }; + + ObjectType object_type; +}; + +class A2dpOffloadCodecAac : public A2dpOffloadCodec { + CodecInfo info_; + + A2dpOffloadCodecAac(); + + A2dpStatus ParseConfiguration(const std::vector& configuration, + CodecParameters* codec_parameters, + AacParameters* aac_parameters) const; + + public: + static const A2dpOffloadCodecAac* GetInstance(); + + A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const override { + return ParseConfiguration(configuration, codec_parameters, nullptr); + } + + A2dpStatus ParseConfiguration(const std::vector& configuration, + AacParameters* aac_parameters) const { + return ParseConfiguration(configuration, aac_parameters, aac_parameters); + } + + bool BuildConfiguration(const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const override; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp new file mode 100644 index 0000000000..73d8fb178f --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.cpp @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#include "A2dpOffloadCodecFactory.h" + +#include +#include + +#include "A2dpOffloadCodecAac.h" +#include "A2dpOffloadCodecSbc.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * Local Capabilities Configuration + */ + +enum : bool { + kEnableAac = true, + kEnableSbc = true, +}; + +/** + * Class implementation + */ + +const A2dpOffloadCodecFactory* A2dpOffloadCodecFactory::GetInstance() { + static A2dpOffloadCodecFactory instance; + return &instance; +} + +A2dpOffloadCodecFactory::A2dpOffloadCodecFactory() + : name("Offload"), codecs(ranked_codecs_) { + ranked_codecs_.reserve(kEnableAac + kEnableSbc); + + if (kEnableAac) ranked_codecs_.push_back(A2dpOffloadCodecAac::GetInstance()); + if (kEnableSbc) ranked_codecs_.push_back(A2dpOffloadCodecSbc::GetInstance()); +} + +const A2dpOffloadCodec* A2dpOffloadCodecFactory::GetCodec(CodecId id) const { + auto codec = std::find_if(begin(ranked_codecs_), end(ranked_codecs_), + [&](auto c) { return id == c->info.id; }); + + return codec != end(ranked_codecs_) ? *codec : nullptr; +} + +bool A2dpOffloadCodecFactory::GetConfiguration( + const std::vector& remote_capabilities, + const A2dpConfigurationHint& hint, A2dpConfiguration* configuration) const { + decltype(ranked_codecs_) codecs; + + codecs.reserve(ranked_codecs_.size()); + + auto hinted_codec = + std::find_if(begin(ranked_codecs_), end(ranked_codecs_), + [&](auto c) { return hint.codecId == c->info.id; }); + + if (hinted_codec != end(ranked_codecs_)) codecs.push_back(*hinted_codec); + + std::copy_if(begin(ranked_codecs_), end(ranked_codecs_), + std::back_inserter(codecs), + [&](auto c) { return c != *hinted_codec; }); + + for (auto codec : codecs) { + auto rc = + std::find_if(begin(remote_capabilities), end(remote_capabilities), + [&](auto& rc__) { return codec->info.id == rc__.id; }); + + if ((rc == end(remote_capabilities)) || + !codec->BuildConfiguration(rc->capabilities, hint.codecParameters, + &configuration->configuration)) + continue; + + configuration->id = codec->info.id; + A2dpStatus status = codec->ParseConfiguration(configuration->configuration, + &configuration->parameters); + assert(status == A2dpStatus::OK); + + configuration->remoteSeid = rc->seid; + + return true; + } + + return false; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h new file mode 100644 index 0000000000..3fb5b1dc64 --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecFactory.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +class A2dpOffloadCodecFactory { + std::vector ranked_codecs_; + + A2dpOffloadCodecFactory(); + + public: + const std::string name; + const std::vector& codecs; + + static const A2dpOffloadCodecFactory* GetInstance(); + + const A2dpOffloadCodec* GetCodec(CodecId id) const; + + bool GetConfiguration(const std::vector&, + const A2dpConfigurationHint& hint, + A2dpConfiguration* configuration) const; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp new file mode 100644 index 0000000000..36d8f729eb --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.cpp @@ -0,0 +1,510 @@ +/* + * 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. + */ + +#include "A2dpOffloadCodecSbc.h" + +#include + +#include "A2dpBits.h" + +namespace aidl::android::hardware::bluetooth::audio { + +/** + * SBC Local Capabilities + */ + +enum : bool { + kEnableSamplingFrequency44100 = true, + kEnableSamplingFrequency48000 = true, +}; + +enum : bool { + kEnableChannelModeMono = true, + kEnableChannelModeDualChannel = true, + kEnableChannelModeStereo = true, + kEnableChannelModeJointStereo = true, +}; + +enum : bool { + kEnableBlockLength4 = true, + kEnableBlockLength8 = true, + kEnableBlockLength12 = true, + kEnableBlockLength16 = true, +}; + +enum : bool { + kEnableSubbands4 = true, + kEnableSubbands8 = true, +}; + +enum : bool { + kEnableAllocationMethodSnr = true, + kEnableAllocationMethodLoudness = true, +}; + +enum : uint8_t { + kDefaultMinimumBitpool = 2, + kDefaultMaximumBitpool = 250, +}; + +enum : int { + kBitdepth = 16, +}; + +/** + * SBC Signaling format [A2DP - 4.3] + */ + +// clang-format off + +constexpr A2dpBits::Range kSamplingFrequency ( 0, 3 ); +constexpr A2dpBits::Range kChannelMode ( 4, 7 ); +constexpr A2dpBits::Range kBlockLength ( 8, 11 ); +constexpr A2dpBits::Range kSubbands ( 12, 13 ); +constexpr A2dpBits::Range kAllocationMethod ( 14, 15 ); +constexpr A2dpBits::Range kMinimumBitpool ( 16, 23 ); +constexpr A2dpBits::Range kMaximumBitpool ( 24, 31 ); +constexpr size_t kCapabilitiesSize = 32/8; + +// clang-format on + +enum { + kSamplingFrequency16000 = kSamplingFrequency.first, + kSamplingFrequency32000, + kSamplingFrequency44100, + kSamplingFrequency48000 +}; + +enum { + kChannelModeMono = kChannelMode.first, + kChannelModeDualChannel, + kChannelModeStereo, + kChannelModeJointStereo +}; + +enum { + kBlockLength4 = kBlockLength.first, + kBlockLength8, + kBlockLength12, + kBlockLength16 +}; + +enum { kSubbands8 = kSubbands.first, kSubbands4 }; + +enum { + kAllocationMethodSnr = kAllocationMethod.first, + kAllocationMethodLoudness +}; + +/** + * SBC Conversion functions + */ + +static int GetSamplingFrequencyBit(int32_t sampling_frequency) { + switch (sampling_frequency) { + case 16000: + return kSamplingFrequency16000; + case 32000: + return kSamplingFrequency32000; + case 44100: + return kSamplingFrequency44100; + case 48000: + return kSamplingFrequency48000; + default: + return -1; + } +} + +static int32_t GetSamplingFrequencyValue(int sampling_frequency) { + switch (sampling_frequency) { + case kSamplingFrequency16000: + return 16000; + case kSamplingFrequency32000: + return 32000; + case kSamplingFrequency44100: + return 44100; + case kSamplingFrequency48000: + return 48000; + default: + return 0; + } +} + +static int GetChannelModeBit(ChannelMode channel_mode) { + switch (channel_mode) { + case ChannelMode::STEREO: + return kChannelModeJointStereo | kChannelModeStereo; + case ChannelMode::DUALMONO: + return kChannelModeDualChannel; + case ChannelMode::MONO: + return kChannelModeMono; + default: + return -1; + } +} + +static ChannelMode GetChannelModeEnum(int channel_mode) { + switch (channel_mode) { + case kChannelModeMono: + return ChannelMode::MONO; + case kChannelModeDualChannel: + return ChannelMode::DUALMONO; + case kChannelModeStereo: + case kChannelModeJointStereo: + return ChannelMode::STEREO; + default: + return ChannelMode::UNKNOWN; + } +} + +static int32_t GetBlockLengthValue(int block_length) { + switch (block_length) { + case kBlockLength4: + return 4; + case kBlockLength8: + return 8; + case kBlockLength12: + return 12; + case kBlockLength16: + return 16; + default: + return 0; + } +} + +static int32_t GetSubbandsValue(int subbands) { + switch (subbands) { + case kSubbands4: + return 4; + case kSubbands8: + return 8; + default: + return 0; + } +} + +static SbcParameters::AllocationMethod GetAllocationMethodEnum( + int allocation_method) { + switch (allocation_method) { + case kAllocationMethodSnr: + return SbcParameters::AllocationMethod::SNR; + case kAllocationMethodLoudness: + default: + return SbcParameters::AllocationMethod::LOUDNESS; + } +} + +static int32_t GetSamplingFrequencyValue(const A2dpBits& configuration) { + return GetSamplingFrequencyValue( + configuration.find_active_bit(kSamplingFrequency)); +} + +static int32_t GetBlockLengthValue(const A2dpBits& configuration) { + return GetBlockLengthValue(configuration.find_active_bit(kBlockLength)); +} + +static int32_t GetSubbandsValue(const A2dpBits& configuration) { + return GetSubbandsValue(configuration.find_active_bit(kSubbands)); +} + +static int GetFrameSize(const A2dpBits& configuration, int bitpool) { + const int kSbcHeaderSize = 4; + int subbands = GetSubbandsValue(configuration); + int blocks = GetBlockLengthValue(configuration); + + unsigned bits = + ((4 * subbands) << !configuration.get(kChannelModeMono)) + + ((blocks * bitpool) << configuration.get(kChannelModeDualChannel)) + + ((configuration.get(kChannelModeJointStereo) ? subbands : 0)); + + return kSbcHeaderSize + ((bits + 7) >> 3); +} + +static int GetBitrate(const A2dpBits& configuration, int bitpool) { + int sampling_frequency = GetSamplingFrequencyValue(configuration); + int subbands = GetSubbandsValue(configuration); + int blocks = GetBlockLengthValue(configuration); + int bits = 8 * GetFrameSize(configuration, bitpool); + + return (bits * sampling_frequency) / (blocks * subbands); +} + +static uint8_t GetBitpool(const A2dpBits& configuration, int bitrate) { + int bitpool = 0; + + for (int i = 128; i; i >>= 1) + if (bitrate > GetBitrate(configuration, bitpool + i)) { + bitpool += i; + } + + return std::clamp(bitpool, 2, 250); +} + +/** + * SBC Class implementation + */ + +const A2dpOffloadCodecSbc* A2dpOffloadCodecSbc::GetInstance() { + static A2dpOffloadCodecSbc instance; + return &instance; +} + +A2dpOffloadCodecSbc::A2dpOffloadCodecSbc() + : A2dpOffloadCodec(info_), + info_({.id = CodecId(CodecId::A2dp::SBC), .name = "SBC"}) { + info_.transport.set(); + auto& a2dp_info = info_.transport.get(); + + /* --- Setup Capabilities --- */ + + a2dp_info.capabilities.resize(kCapabilitiesSize); + std::fill(begin(a2dp_info.capabilities), end(a2dp_info.capabilities), 0); + + auto capabilities = A2dpBits(a2dp_info.capabilities); + + capabilities.set(kSamplingFrequency44100, kEnableSamplingFrequency44100); + capabilities.set(kSamplingFrequency48000, kEnableSamplingFrequency48000); + + capabilities.set(kChannelModeMono, kEnableChannelModeMono); + capabilities.set(kChannelModeDualChannel, kEnableChannelModeDualChannel); + capabilities.set(kChannelModeStereo, kEnableChannelModeStereo); + capabilities.set(kChannelModeJointStereo, kEnableChannelModeJointStereo); + + capabilities.set(kBlockLength4, kEnableBlockLength4); + capabilities.set(kBlockLength8, kEnableBlockLength8); + capabilities.set(kBlockLength12, kEnableBlockLength12); + capabilities.set(kBlockLength16, kEnableBlockLength16); + + capabilities.set(kSubbands4, kEnableSubbands4); + capabilities.set(kSubbands8, kEnableSubbands8); + + capabilities.set(kSubbands4, kEnableSubbands4); + capabilities.set(kSubbands8, kEnableSubbands8); + + capabilities.set(kAllocationMethodSnr, kEnableAllocationMethodSnr); + capabilities.set(kAllocationMethodLoudness, kEnableAllocationMethodLoudness); + + capabilities.set(kMinimumBitpool, kDefaultMinimumBitpool); + capabilities.set(kMaximumBitpool, kDefaultMaximumBitpool); + + /* --- Setup Sampling Frequencies --- */ + + auto& sampling_frequency = a2dp_info.samplingFrequencyHz; + + for (auto v : {16000, 32000, 44100, 48000}) + if (capabilities.get(GetSamplingFrequencyBit(int32_t(v)))) + sampling_frequency.push_back(v); + + /* --- Setup Channel Modes --- */ + + auto& channel_modes = a2dp_info.channelMode; + + for (auto v : {ChannelMode::MONO, ChannelMode::DUALMONO, ChannelMode::STEREO}) + if (capabilities.get(GetChannelModeBit(v))) channel_modes.push_back(v); + + /* --- Setup Bitdepth --- */ + + a2dp_info.bitdepth.push_back(kBitdepth); +} + +A2dpStatus A2dpOffloadCodecSbc::ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters, SbcParameters* sbc_parameters) const { + auto& a2dp_info = info.transport.get(); + + if (configuration.size() != a2dp_info.capabilities.size()) + return A2dpStatus::BAD_LENGTH; + + auto config = A2dpBits(configuration); + auto lcaps = A2dpBits(a2dp_info.capabilities); + + /* --- Check Sampling Frequency --- */ + + int sampling_frequency = config.find_active_bit(kSamplingFrequency); + if (sampling_frequency < 0) return A2dpStatus::INVALID_SAMPLING_FREQUENCY; + if (!lcaps.get(sampling_frequency)) + return A2dpStatus::NOT_SUPPORTED_SAMPLING_FREQUENCY; + + /* --- Check Channel Mode --- */ + + int channel_mode = config.find_active_bit(kChannelMode); + if (channel_mode < 0) return A2dpStatus::INVALID_CHANNEL_MODE; + if (!lcaps.get(channel_mode)) return A2dpStatus::NOT_SUPPORTED_CHANNEL_MODE; + + /* --- Check Block Length --- */ + + int block_length = config.find_active_bit(kBlockLength); + if (block_length < 0) return A2dpStatus::INVALID_BLOCK_LENGTH; + + /* --- Check Subbands --- */ + + int subbands = config.find_active_bit(kSubbands); + if (subbands < 0) return A2dpStatus::INVALID_SUBBANDS; + if (!lcaps.get(subbands)) return A2dpStatus::NOT_SUPPORTED_SUBBANDS; + + /* --- Check Allocation Method --- */ + + int allocation_method = config.find_active_bit(kAllocationMethod); + if (allocation_method < 0) return A2dpStatus::INVALID_ALLOCATION_METHOD; + if (!lcaps.get(allocation_method)) + return A2dpStatus::NOT_SUPPORTED_ALLOCATION_METHOD; + + /* --- Check Bitpool --- */ + + uint8_t min_bitpool = config.get(kMinimumBitpool); + if (min_bitpool < 2 || min_bitpool > 250) + return A2dpStatus::INVALID_MINIMUM_BITPOOL_VALUE; + if (min_bitpool < lcaps.get(kMinimumBitpool)) + return A2dpStatus::NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE; + + uint8_t max_bitpool = config.get(kMaximumBitpool); + if (max_bitpool < 2 || max_bitpool > 250) + return A2dpStatus::INVALID_MAXIMUM_BITPOOL_VALUE; + if (max_bitpool > lcaps.get(kMaximumBitpool)) + return A2dpStatus::NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE; + + /* --- Return --- */ + + codec_parameters->channelMode = GetChannelModeEnum(channel_mode); + codec_parameters->samplingFrequencyHz = + GetSamplingFrequencyValue(sampling_frequency); + codec_parameters->bitdepth = kBitdepth; + + codec_parameters->minBitrate = GetBitrate(config, min_bitpool); + codec_parameters->maxBitrate = GetBitrate(config, max_bitpool); + + if (sbc_parameters) { + sbc_parameters->block_length = GetBlockLengthValue(block_length); + sbc_parameters->subbands = GetSubbandsValue(subbands); + sbc_parameters->allocation_method = + GetAllocationMethodEnum(allocation_method); + sbc_parameters->min_bitpool = min_bitpool; + sbc_parameters->max_bitpool = max_bitpool; + } + + return A2dpStatus::OK; +} + +bool A2dpOffloadCodecSbc::BuildConfiguration( + const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const { + auto& a2dp_info = info.transport.get(); + + if (remote_capabilities.size() != a2dp_info.capabilities.size()) return false; + + auto lcaps = A2dpBits(a2dp_info.capabilities); + auto rcaps = A2dpBits(remote_capabilities); + + configuration->resize(a2dp_info.capabilities.size()); + std::fill(begin(*configuration), end(*configuration), 0); + auto config = A2dpBits(*configuration); + + /* --- Select Sampling Frequency --- */ + + auto sf_hint = hint ? GetSamplingFrequencyBit(hint->samplingFrequencyHz) : -1; + + if (sf_hint >= 0 && lcaps.get(sf_hint) && rcaps.get(sf_hint)) + config.set(sf_hint); + else if (lcaps.get(kSamplingFrequency44100) && + rcaps.get(kSamplingFrequency44100)) + config.set(kSamplingFrequency44100); + else if (lcaps.get(kSamplingFrequency48000) && + rcaps.get(kSamplingFrequency48000)) + config.set(kSamplingFrequency48000); + else + return false; + + /* --- Select Channel Mode --- */ + + auto cm_hint = hint ? GetChannelModeBit(hint->channelMode) : -1; + + if (cm_hint >= 0 && lcaps.get(cm_hint) && rcaps.get(cm_hint)) + config.set(cm_hint); + else if (lcaps.get(kChannelModeJointStereo) && + rcaps.get(kChannelModeJointStereo)) + config.set(kChannelModeJointStereo); + else if (lcaps.get(kChannelModeStereo) && rcaps.get(kChannelModeStereo)) + config.set(kChannelModeStereo); + else if (lcaps.get(kChannelModeDualChannel) && + rcaps.get(kChannelModeDualChannel)) + config.set(kChannelModeDualChannel); + else if (lcaps.get(kChannelModeMono) && rcaps.get(kChannelModeMono)) + config.set(kChannelModeMono); + else + return false; + + /* --- Select Block Length --- */ + + if (lcaps.get(kBlockLength16) && rcaps.get(kBlockLength16)) + config.set(kBlockLength16); + else if (lcaps.get(kBlockLength12) && rcaps.get(kBlockLength12)) + config.set(kBlockLength12); + else if (lcaps.get(kBlockLength8) && rcaps.get(kBlockLength8)) + config.set(kBlockLength8); + else if (lcaps.get(kBlockLength4) && rcaps.get(kBlockLength4)) + config.set(kBlockLength4); + else + return false; + + /* --- Select Subbands --- */ + + if (lcaps.get(kSubbands8) && rcaps.get(kSubbands8)) + config.set(kSubbands8); + else if (lcaps.get(kSubbands4) && rcaps.get(kSubbands4)) + config.set(kSubbands4); + else + return false; + + /* --- Select Allocation method --- */ + + if (lcaps.get(kAllocationMethodLoudness) && + rcaps.get(kAllocationMethodLoudness)) + config.set(kAllocationMethodLoudness); + else if (lcaps.get(kAllocationMethodSnr) && rcaps.get(kAllocationMethodSnr)) + config.set(kAllocationMethodSnr); + else + return false; + + /* --- Select Bitpool --- */ + + uint8_t min_bitpool = rcaps.get(kMinimumBitpool); + uint8_t max_bitpool = rcaps.get(kMaximumBitpool); + + if (min_bitpool < 2 || min_bitpool > 250 || max_bitpool < 2 || + max_bitpool > 250 || min_bitpool > max_bitpool) { + min_bitpool = 2; + max_bitpool = 250; + } + + min_bitpool = std::max(min_bitpool, uint8_t(lcaps.get(kMinimumBitpool))); + max_bitpool = std::max(max_bitpool, uint8_t(lcaps.get(kMaximumBitpool))); + + if (hint) { + min_bitpool = + std::max(min_bitpool, GetBitpool(*configuration, hint->minBitrate)); + if (hint->maxBitrate && hint->maxBitrate >= hint->minBitrate) + max_bitpool = + std::min(max_bitpool, GetBitpool(*configuration, hint->maxBitrate)); + } + + config.set(kMinimumBitpool, min_bitpool); + config.set(kMaximumBitpool, max_bitpool); + + return true; +} + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h new file mode 100644 index 0000000000..c380850cbb --- /dev/null +++ b/bluetooth/audio/aidl/default/A2dpOffloadCodecSbc.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#pragma once + +#include "A2dpOffloadCodec.h" + +namespace aidl::android::hardware::bluetooth::audio { + +struct SbcParameters : public CodecParameters { + enum class AllocationMethod { SNR, LOUDNESS }; + + AllocationMethod allocation_method; + int block_length; + int subbands; + int min_bitpool; + int max_bitpool; +}; + +class A2dpOffloadCodecSbc : public A2dpOffloadCodec { + CodecInfo info_; + + A2dpOffloadCodecSbc(); + + A2dpStatus ParseConfiguration(const std::vector& configuration, + CodecParameters* codec_parameters, + SbcParameters* sbc_parameters) const; + + public: + static const A2dpOffloadCodecSbc* GetInstance(); + + A2dpStatus ParseConfiguration( + const std::vector& configuration, + CodecParameters* codec_parameters) const override { + return ParseConfiguration(configuration, codec_parameters, nullptr); + } + + A2dpStatus ParseConfiguration(const std::vector& configuration, + SbcParameters* sbc_parameters) const { + return ParseConfiguration(configuration, sbc_parameters, sbc_parameters); + } + + bool BuildConfiguration(const std::vector& remote_capabilities, + const std::optional& hint, + std::vector* configuration) const override; +}; + +} // namespace aidl::android::hardware::bluetooth::audio diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp index ad4d8ce4be..69db1b3e38 100644 --- a/bluetooth/audio/aidl/default/Android.bp +++ b/bluetooth/audio/aidl/default/Android.bp @@ -18,6 +18,9 @@ cc_library_shared { "BluetoothAudioProvider.cpp", "BluetoothAudioProviderFactory.cpp", "A2dpOffloadAudioProvider.cpp", + "A2dpOffloadCodecAac.cpp", + "A2dpOffloadCodecFactory.cpp", + "A2dpOffloadCodecSbc.cpp", "A2dpSoftwareAudioProvider.cpp", "HearingAidAudioProvider.cpp", "HfpOffloadAudioProvider.cpp", diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp index fe77d862f3..b88e66aea0 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.cpp @@ -21,6 +21,8 @@ #include #include +#include "A2dpOffloadCodecFactory.h" + namespace aidl { namespace android { namespace hardware { @@ -165,39 +167,24 @@ ndk::ScopedAStatus BluetoothAudioProvider::setLowLatencyModeAllowed( } ndk::ScopedAStatus BluetoothAudioProvider::parseA2dpConfiguration( - const CodecId& codec_id, const std::vector& configuration, - CodecParameters* codec_parameters, A2dpStatus* _aidl_return) { - if (stack_iface_ == nullptr) { - LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) - << " has NO session"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } - - *_aidl_return = A2dpStatus::OK; - - (void)codec_id; - (void)configuration; - (void)codec_parameters; - - return ndk::ScopedAStatus::ok(); + [[maybe_unused]] const CodecId& codec_id, + [[maybe_unused]] const std::vector& configuration, + [[maybe_unused]] CodecParameters* codec_parameters, + [[maybe_unused]] A2dpStatus* _aidl_return) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " is illegal"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } ndk::ScopedAStatus BluetoothAudioProvider::getA2dpConfiguration( - const std::vector& remote_a2dp_capabilities, - const A2dpConfigurationHint& hint, - std::optional* _aidl_return) { - if (stack_iface_ == nullptr) { - LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) - << " has NO session"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } + [[maybe_unused]] const std::vector& + remote_a2dp_capabilities, + [[maybe_unused]] const A2dpConfigurationHint& hint, + [[maybe_unused]] std::optional* _aidl_return) { + LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) + << " is illegal"; - *_aidl_return = std::nullopt; - - (void)remote_a2dp_capabilities; - (void)hint; - - return ndk::ScopedAStatus::ok(); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } } // namespace audio diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp index 8c6421d590..3a64c4d046 100644 --- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp +++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp @@ -22,6 +22,7 @@ #include #include "A2dpOffloadAudioProvider.h" +#include "A2dpOffloadCodecFactory.h" #include "A2dpSoftwareAudioProvider.h" #include "BluetoothAudioProvider.h" #include "HearingAidAudioProvider.h" @@ -150,7 +151,14 @@ ndk::ScopedAStatus BluetoothAudioProviderFactory::getProviderInfo( SessionType session_type, std::optional* _aidl_return) { *_aidl_return = std::nullopt; - (void)session_type; + if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH || + session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) { + auto& provider_info = _aidl_return->emplace(); + + provider_info.name = A2dpOffloadCodecFactory::GetInstance()->name; + for (auto codec : A2dpOffloadCodecFactory::GetInstance()->codecs) + provider_info.codecInfos.push_back(codec->info); + } return ndk::ScopedAStatus::ok(); } diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp index 9dcfc13893..3519ace1f9 100644 --- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp +++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp @@ -304,7 +304,8 @@ bool BluetoothAudioSession::UpdateAudioConfig( audio_config_tag == AudioConfiguration::pcmConfig); bool is_a2dp_offload_audio_config = (is_offload_a2dp_session && - audio_config_tag == AudioConfiguration::a2dpConfig); + (audio_config_tag == AudioConfiguration::a2dp || + audio_config_tag == AudioConfiguration::a2dpConfig)); bool is_hfp_offload_audio_config = (is_offload_hfp_session && audio_config_tag == AudioConfiguration::hfpConfig);