/* * Copyright 2019 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 "bluetooth" #include #include "client_interface.h" #include "codec_status.h" namespace { using ::android::hardware::bluetooth::audio::V2_0::AacObjectType; using ::android::hardware::bluetooth::audio::V2_0::AacParameters; using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate; using ::android::hardware::bluetooth::audio::V2_0::AptxParameters; using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities; using ::android::hardware::bluetooth::audio::V2_0::CodecType; using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode; using ::android::hardware::bluetooth::audio::V2_0::LdacParameters; using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex; using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod; using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength; using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode; using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands; using ::android::hardware::bluetooth::audio::V2_0::SbcParameters; using ::bluetooth::audio::AudioCapabilities; using ::bluetooth::audio::AudioCapabilities_2_1; using ::bluetooth::audio::AudioConfiguration; using ::bluetooth::audio::AudioConfiguration_2_1; using ::bluetooth::audio::BluetoothAudioClientInterface; using ::bluetooth::audio::BluetoothAudioSinkClientInterface; using ::bluetooth::audio::BluetoothAudioSourceClientInterface; using ::bluetooth::audio::BluetoothAudioStatus; using ::bluetooth::audio::PcmParameters; using ::bluetooth::audio::PcmParameters_2_1; using ::bluetooth::audio::SampleRate; using ::bluetooth::audio::SampleRate_2_1; using ::bluetooth::audio::SessionType; using ::bluetooth::audio::SessionType_2_1; using ::bluetooth::audio::codec::A2dpCodecToHalBitsPerSample; using ::bluetooth::audio::codec::A2dpCodecToHalChannelMode; using ::bluetooth::audio::codec::A2dpCodecToHalSampleRate; using ::bluetooth::audio::codec::BitsPerSample; using ::bluetooth::audio::codec::ChannelMode; using ::bluetooth::audio::codec::CodecConfiguration; using ::bluetooth::audio::codec::IsCodecOffloadingEnabled; using ::bluetooth::audio::codec::UpdateOffloadingCapabilities; using ::testing::Test; struct SampleRatePair { SampleRate hal_sample_rate_; btav_a2dp_codec_sample_rate_t btav_sample_rate_; }; constexpr SampleRatePair kSampleRatePairs[9] = { {.hal_sample_rate_ = SampleRate::RATE_UNKNOWN, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE}, {.hal_sample_rate_ = SampleRate::RATE_44100, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_44100}, {.hal_sample_rate_ = SampleRate::RATE_48000, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_48000}, {.hal_sample_rate_ = SampleRate::RATE_88200, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_88200}, {.hal_sample_rate_ = SampleRate::RATE_96000, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_96000}, {.hal_sample_rate_ = SampleRate::RATE_176400, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_176400}, {.hal_sample_rate_ = SampleRate::RATE_192000, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_192000}, {.hal_sample_rate_ = SampleRate::RATE_16000, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_16000}, {.hal_sample_rate_ = SampleRate::RATE_24000, .btav_sample_rate_ = BTAV_A2DP_CODEC_SAMPLE_RATE_24000}}; constexpr SampleRate_2_1 kSampleRates_2_1[] = { SampleRate_2_1::RATE_UNKNOWN, SampleRate_2_1::RATE_8000, SampleRate_2_1::RATE_16000, SampleRate_2_1::RATE_24000, SampleRate_2_1::RATE_32000, SampleRate_2_1::RATE_44100, SampleRate_2_1::RATE_48000}; constexpr uint32_t kDataIntervalUs[] = {0 /* Invalid */, 10000 /* Valid 10ms */}; struct BitsPerSamplePair { BitsPerSample hal_bits_per_sample_; btav_a2dp_codec_bits_per_sample_t btav_bits_per_sample_; }; constexpr BitsPerSamplePair kBitsPerSamplePairs[4] = { {.hal_bits_per_sample_ = BitsPerSample::BITS_UNKNOWN, .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE}, {.hal_bits_per_sample_ = BitsPerSample::BITS_16, .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16}, {.hal_bits_per_sample_ = BitsPerSample::BITS_24, .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24}, {.hal_bits_per_sample_ = BitsPerSample::BITS_32, .btav_bits_per_sample_ = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32}}; struct ChannelModePair { ChannelMode hal_channel_mode_; btav_a2dp_codec_channel_mode_t btav_channel_mode_; }; constexpr ChannelModePair kChannelModePairs[3] = { {.hal_channel_mode_ = ChannelMode::UNKNOWN, .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE}, {.hal_channel_mode_ = ChannelMode::MONO, .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO}, {.hal_channel_mode_ = ChannelMode::STEREO, .btav_channel_mode_ = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO}}; constexpr btav_a2dp_codec_index_t codec_indexes[] = { BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC, BTAV_A2DP_CODEC_INDEX_SINK_AAC, BTAV_A2DP_CODEC_INDEX_SINK_LDAC}; constexpr uint16_t kPeerMtus[5] = {660, 663, 883, 1005, 1500}; class TestSinkTransport : public bluetooth::audio::IBluetoothSinkTransportInstance { private: static constexpr uint64_t kRemoteDelayReportMs = 200; public: TestSinkTransport(SessionType session_type) : bluetooth::audio::IBluetoothSinkTransportInstance(session_type, {}){}; TestSinkTransport(SessionType_2_1 session_type_2_1) : bluetooth::audio::IBluetoothSinkTransportInstance(session_type_2_1, {}){}; bluetooth::audio::BluetoothAudioCtrlAck StartRequest() override { return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED; } bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() override { return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED; } void StopRequest() override {} bool GetPresentationPosition(uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed, timespec* data_position) override { if (remote_delay_report_ns) { *remote_delay_report_ns = kRemoteDelayReportMs * 1000000; } if (total_bytes_readed) { *total_bytes_readed = 0; } if (data_position) { clock_gettime(CLOCK_MONOTONIC, data_position); } return true; } void MetadataChanged( const source_metadata_t& source_metadata __unused) override {} void ResetPresentationPosition() override{}; void LogBytesRead(size_t bytes_readed __unused) override{}; }; class TestSourceTransport : public bluetooth::audio::IBluetoothSourceTransportInstance { private: static constexpr uint64_t kRemoteDelayReportMs = 200; public: TestSourceTransport(SessionType session_type) : bluetooth::audio::IBluetoothSourceTransportInstance(session_type, {}){}; TestSourceTransport(SessionType_2_1 session_type_2_1) : bluetooth::audio::IBluetoothSourceTransportInstance(session_type_2_1, {}){}; bluetooth::audio::BluetoothAudioCtrlAck StartRequest() override { return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED; } bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() override { return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED; } void StopRequest() override {} bool GetPresentationPosition(uint64_t* remote_delay_report_ns, uint64_t* total_bytes_written, timespec* data_position) override { if (remote_delay_report_ns) { *remote_delay_report_ns = kRemoteDelayReportMs * 1000000; } if (total_bytes_written) { *total_bytes_written = 0; } if (data_position) { clock_gettime(CLOCK_MONOTONIC, data_position); } return true; } void MetadataChanged( const source_metadata_t& source_metadata __unused) override {} void ResetPresentationPosition() override{}; void LogBytesWritten(size_t bytes_written __unused) override{}; }; class BluetoothAudioClientInterfaceTest : public Test { protected: TestSinkTransport* test_sink_transport_ = nullptr; TestSourceTransport* test_source_transport_ = nullptr; BluetoothAudioSinkClientInterface* clientif_sink_ = nullptr; BluetoothAudioSourceClientInterface* clientif_source_ = nullptr; static constexpr int kClientIfReturnSuccess = 0; void SetUp() override {} void TearDown() override { if (clientif_sink_ != nullptr) delete clientif_sink_; clientif_sink_ = nullptr; if (test_sink_transport_ != nullptr) delete test_sink_transport_; test_sink_transport_ = nullptr; if (clientif_source_ != nullptr) delete clientif_source_; clientif_source_ = nullptr; if (test_source_transport_ != nullptr) delete test_source_transport_; test_source_transport_ = nullptr; } bool IsSoftwarePcmParametersSupported(const PcmParameters& pcm_config) { const std::vector& capabilities = clientif_sink_->GetAudioCapabilities(); PcmParameters pcm_capabilities = capabilities[0].pcmCapabilities(); bool is_pcm_config_valid = (pcm_config.sampleRate != SampleRate::RATE_UNKNOWN && pcm_config.bitsPerSample != BitsPerSample::BITS_UNKNOWN && pcm_config.channelMode != ChannelMode::UNKNOWN); bool is_pcm_config_supported = (pcm_config.sampleRate & pcm_capabilities.sampleRate && pcm_config.bitsPerSample & pcm_capabilities.bitsPerSample && pcm_config.channelMode & pcm_capabilities.channelMode); return (is_pcm_config_valid && is_pcm_config_supported); } bool IsSinkSoftwarePcmParameters_2_1_Supported( const PcmParameters_2_1& pcm_config) { return IsSoftwarePcmParameters_2_1_Supported(pcm_config, clientif_sink_); } bool IsSourceSoftwarePcmParameters_2_1_Supported( const PcmParameters_2_1& pcm_config) { return IsSoftwarePcmParameters_2_1_Supported(pcm_config, clientif_source_); } bool IsCodecOffloadingSupported(const CodecConfiguration& codec_config) { CodecCapabilities codec_capability = {}; for (auto audio_capability : clientif_sink_->GetAudioCapabilities()) { if (audio_capability.codecCapabilities().codecType == codec_config.codecType) { codec_capability = audio_capability.codecCapabilities(); } } if (codec_capability.codecType != codec_config.codecType) { // codec is unsupported return false; } bool is_codec_config_supported = false; switch (codec_config.codecType) { case CodecType::SBC: { SbcParameters sbc_config = codec_config.config.sbcConfig(); SbcParameters sbc_capability = codec_capability.capabilities.sbcCapabilities(); is_codec_config_supported = (sbc_config.sampleRate & sbc_capability.sampleRate && sbc_config.channelMode & sbc_capability.channelMode && sbc_config.blockLength & sbc_capability.blockLength && sbc_config.numSubbands & sbc_capability.numSubbands && sbc_config.allocMethod & sbc_capability.allocMethod && sbc_config.bitsPerSample & sbc_capability.bitsPerSample && (sbc_capability.minBitpool <= sbc_config.minBitpool && sbc_config.minBitpool <= sbc_config.maxBitpool && sbc_config.maxBitpool <= sbc_capability.maxBitpool)); return is_codec_config_supported; } case CodecType::AAC: { AacParameters aac_config = codec_config.config.aacConfig(); AacParameters aac_capability = codec_capability.capabilities.aacCapabilities(); is_codec_config_supported = (aac_config.objectType & aac_capability.objectType && aac_config.sampleRate & aac_capability.sampleRate && aac_config.channelMode & aac_capability.channelMode && (aac_config.variableBitRateEnabled == AacVariableBitRate::DISABLED || aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) && aac_config.bitsPerSample & aac_capability.bitsPerSample); return is_codec_config_supported; } case CodecType::LDAC: { LdacParameters ldac_config = codec_config.config.ldacConfig(); LdacParameters ldac_capability = codec_capability.capabilities.ldacCapabilities(); is_codec_config_supported = (ldac_config.sampleRate & ldac_capability.sampleRate && ldac_config.channelMode & ldac_capability.channelMode && ldac_config.bitsPerSample & ldac_capability.bitsPerSample); return is_codec_config_supported; } case CodecType::APTX: [[fallthrough]]; case CodecType::APTX_HD: { AptxParameters aptx_config = codec_config.config.aptxConfig(); AptxParameters aptx_capability = codec_capability.capabilities.aptxCapabilities(); is_codec_config_supported = (aptx_config.sampleRate & aptx_capability.sampleRate && aptx_config.channelMode & aptx_capability.channelMode && aptx_config.bitsPerSample & aptx_capability.bitsPerSample); return is_codec_config_supported; } case CodecType::UNKNOWN: return false; } } private: bool IsSoftwarePcmParameters_2_1_Supported( const PcmParameters_2_1& pcm_config, const BluetoothAudioClientInterface* clientif_) { const std::vector& capabilities = clientif_->GetAudioCapabilities_2_1(); PcmParameters_2_1 pcm_capabilities = capabilities[0].pcmCapabilities(); bool is_pcm_config_valid = (pcm_config.sampleRate != SampleRate_2_1::RATE_UNKNOWN && pcm_config.bitsPerSample != BitsPerSample::BITS_UNKNOWN && pcm_config.channelMode != ChannelMode::UNKNOWN && pcm_config.dataIntervalUs != 0); bool is_pcm_config_supported = (pcm_config.sampleRate & pcm_capabilities.sampleRate && pcm_config.bitsPerSample & pcm_capabilities.bitsPerSample && pcm_config.channelMode & pcm_capabilities.channelMode); return (is_pcm_config_valid && is_pcm_config_supported); } }; } // namespace TEST_F(BluetoothAudioClientInterfaceTest, A2dpCodecToHalPcmConfig) { btav_a2dp_codec_config_t a2dp_codec_config = {}; for (auto sample_rate_pair : kSampleRatePairs) { a2dp_codec_config.sample_rate = sample_rate_pair.btav_sample_rate_; for (auto bits_per_sample_pair : kBitsPerSamplePairs) { a2dp_codec_config.bits_per_sample = bits_per_sample_pair.btav_bits_per_sample_; for (auto channel_mode_pair : kChannelModePairs) { a2dp_codec_config.channel_mode = channel_mode_pair.btav_channel_mode_; ASSERT_EQ(A2dpCodecToHalSampleRate(a2dp_codec_config), sample_rate_pair.hal_sample_rate_); ASSERT_EQ(A2dpCodecToHalBitsPerSample(a2dp_codec_config), bits_per_sample_pair.hal_bits_per_sample_); ASSERT_EQ(A2dpCodecToHalChannelMode(a2dp_codec_config), channel_mode_pair.hal_channel_mode_); } // ChannelMode } // BitsPerSampple } // SampleRate } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpSoftwareSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; PcmParameters pcm_config = {}; for (auto sample_rate_pair : kSampleRatePairs) { pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_; for (auto bits_per_sample_pair : kBitsPerSamplePairs) { pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_; for (auto channel_mode_pair : kChannelModePairs) { pcm_config.channelMode = channel_mode_pair.hal_channel_mode_; audio_config.pcmConfig(pcm_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsSoftwarePcmParametersSupported(pcm_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } // ChannelMode } // BitsPerSampple } // SampleRate } struct CodecOffloadingPreference { bool is_target_codec_included_; std::vector preference_; }; std::vector CodecOffloadingPreferenceGenerator( btav_a2dp_codec_index_t target_codec_index) { std::vector codec_offloading_preferences = { {.is_target_codec_included_ = false, .preference_ = std::vector(0)}}; btav_a2dp_codec_config_t a2dp_codec_config = {}; for (auto codec_index : codec_indexes) { a2dp_codec_config.codec_type = codec_index; auto duplicated_preferences = codec_offloading_preferences; for (auto iter = duplicated_preferences.begin(); iter != duplicated_preferences.end(); ++iter) { if (codec_index == target_codec_index) { iter->is_target_codec_included_ = true; } iter->preference_.push_back(a2dp_codec_config); } codec_offloading_preferences.insert(codec_offloading_preferences.end(), duplicated_preferences.begin(), duplicated_preferences.end()); } return codec_offloading_preferences; } std::vector SbcCodecConfigurationsGenerator() { std::vector sbc_codec_configs; CodecConfiguration codec_config = {}; SbcBlockLength block_lengths[4] = { SbcBlockLength::BLOCKS_4, SbcBlockLength::BLOCKS_8, SbcBlockLength::BLOCKS_12, SbcBlockLength::BLOCKS_16}; SbcNumSubbands num_subbands[2] = {SbcNumSubbands::SUBBAND_4, SbcNumSubbands::SUBBAND_8}; SbcAllocMethod alloc_methods[2] = {SbcAllocMethod::ALLOC_MD_S, SbcAllocMethod::ALLOC_MD_L}; for (auto sample_rate_pair : kSampleRatePairs) { for (auto bits_per_sample_pair : kBitsPerSamplePairs) { for (auto channel_mode_pair : kChannelModePairs) { for (auto peer_mtu : kPeerMtus) { for (auto block_length : block_lengths) { for (auto num_subband : num_subbands) { for (auto alloc_method : alloc_methods) { codec_config.codecType = CodecType::SBC; codec_config.peerMtu = peer_mtu; codec_config.isScmstEnabled = false; // A2DP_SBC_DEFAULT_BITRATE codec_config.encodedAudioBitrate = 328000; SbcParameters sbc = { .sampleRate = sample_rate_pair.hal_sample_rate_, .channelMode = (channel_mode_pair.hal_channel_mode_ == ChannelMode::MONO ? SbcChannelMode::MONO : SbcChannelMode::JOINT_STEREO), .blockLength = block_length, .numSubbands = num_subband, .allocMethod = alloc_method, .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_, .minBitpool = 2, .maxBitpool = 53}; codec_config.config.sbcConfig(sbc); sbc_codec_configs.push_back(codec_config); } // SbcAllocMethod } // SbcNumSubbands } // SbcBlockLength } // peerMtu } // ChannelMode } // BitsPerSampple } // SampleRate return sbc_codec_configs; } TEST_F(BluetoothAudioClientInterfaceTest, A2dpSbcCodecOffloadingState) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); auto sbc_codec_configs = SbcCodecConfigurationsGenerator(); for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC)) { UpdateOffloadingCapabilities(codec_offloading_preference.preference_); for (CodecConfiguration codec_config : sbc_codec_configs) { if (IsCodecOffloadingSupported(codec_config) && codec_offloading_preference.is_target_codec_included_) { ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config)); } else { ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config)); } } } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; for (CodecConfiguration codec_config : SbcCodecConfigurationsGenerator()) { audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } } std::vector AacCodecConfigurationsGenerator() { std::vector aac_codec_configs; CodecConfiguration codec_config = {}; AacObjectType object_types[4] = { AacObjectType::MPEG2_LC, AacObjectType::MPEG4_LC, AacObjectType::MPEG4_LTP, AacObjectType::MPEG4_SCALABLE}; AacVariableBitRate variable_bitrates[2] = {AacVariableBitRate::DISABLED, AacVariableBitRate::ENABLED}; for (auto sample_rate_pair : kSampleRatePairs) { for (auto bits_per_sample_pair : kBitsPerSamplePairs) { for (auto channel_mode_pair : kChannelModePairs) { for (auto peer_mtu : kPeerMtus) { for (auto object_type : object_types) { for (auto variable_bitrate : variable_bitrates) { codec_config.codecType = CodecType::AAC; codec_config.peerMtu = peer_mtu; codec_config.isScmstEnabled = false; // A2DP_AAC_DEFAULT_BITRATE codec_config.encodedAudioBitrate = 320000; AacParameters aac = { .objectType = object_type, .sampleRate = sample_rate_pair.hal_sample_rate_, .channelMode = channel_mode_pair.hal_channel_mode_, .variableBitRateEnabled = variable_bitrate, .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_}; codec_config.config.aacConfig(aac); aac_codec_configs.push_back(codec_config); } // AacVariableBitRate } // AacObjectType } // peerMtu } // ChannelMode } // BitsPerSampple } // SampleRate return aac_codec_configs; } TEST_F(BluetoothAudioClientInterfaceTest, A2dpAacCodecOffloadingState) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); auto aac_codec_configs = AacCodecConfigurationsGenerator(); for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC)) { UpdateOffloadingCapabilities(codec_offloading_preference.preference_); for (CodecConfiguration codec_config : aac_codec_configs) { if (IsCodecOffloadingSupported(codec_config) && codec_offloading_preference.is_target_codec_included_) { ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config)); } else { ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config)); } } } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; for (CodecConfiguration codec_config : AacCodecConfigurationsGenerator()) { audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } } std::vector LdacCodecConfigurationsGenerator() { std::vector ldac_codec_configs; CodecConfiguration codec_config = {}; LdacQualityIndex quality_indexes[4] = { LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID, LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR}; for (auto sample_rate_pair : kSampleRatePairs) { for (auto bits_per_sample_pair : kBitsPerSamplePairs) { for (auto channel_mode_pair : kChannelModePairs) { for (auto peer_mtu : kPeerMtus) { for (auto quality_index : quality_indexes) { codec_config.codecType = CodecType::LDAC; codec_config.peerMtu = peer_mtu; codec_config.isScmstEnabled = false; codec_config.encodedAudioBitrate = 990000; LdacParameters ldac = { .sampleRate = sample_rate_pair.hal_sample_rate_, .channelMode = (channel_mode_pair.hal_channel_mode_ == ChannelMode::MONO ? LdacChannelMode::MONO : LdacChannelMode::STEREO), .qualityIndex = quality_index, .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_}; codec_config.config.ldacConfig(ldac); ldac_codec_configs.push_back(codec_config); } // LdacQualityIndex } // peerMtu } // ChannelMode } // BitsPerSampple } // SampleRate return ldac_codec_configs; } TEST_F(BluetoothAudioClientInterfaceTest, A2dpLdacCodecOffloadingState) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); auto ldac_codec_configs = LdacCodecConfigurationsGenerator(); for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC)) { UpdateOffloadingCapabilities(codec_offloading_preference.preference_); for (CodecConfiguration codec_config : ldac_codec_configs) { if (IsCodecOffloadingSupported(codec_config) && codec_offloading_preference.is_target_codec_included_) { ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config)); } else { ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config)); } } } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; for (CodecConfiguration codec_config : LdacCodecConfigurationsGenerator()) { audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } } std::vector AptxCodecConfigurationsGenerator( CodecType codec_type) { std::vector aptx_codec_configs; if (codec_type != CodecType::APTX && codec_type != CodecType::APTX_HD) return aptx_codec_configs; CodecConfiguration codec_config = {}; for (auto sample_rate_pair : kSampleRatePairs) { for (auto bits_per_sample_pair : kBitsPerSamplePairs) { for (auto channel_mode_pair : kChannelModePairs) { for (auto peer_mtu : kPeerMtus) { codec_config.codecType = codec_type; codec_config.peerMtu = peer_mtu; codec_config.isScmstEnabled = false; codec_config.encodedAudioBitrate = (codec_type == CodecType::APTX ? 352000 : 576000); AptxParameters aptx = { .sampleRate = sample_rate_pair.hal_sample_rate_, .channelMode = channel_mode_pair.hal_channel_mode_, .bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_}; codec_config.config.aptxConfig(aptx); aptx_codec_configs.push_back(codec_config); } // peerMtu } // ChannelMode } // BitsPerSampple } // SampleRate return aptx_codec_configs; } TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxCodecOffloadingState) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); auto aptx_codec_configs = AptxCodecConfigurationsGenerator(CodecType::APTX); for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX)) { UpdateOffloadingCapabilities(codec_offloading_preference.preference_); for (CodecConfiguration codec_config : aptx_codec_configs) { if (IsCodecOffloadingSupported(codec_config) && codec_offloading_preference.is_target_codec_included_) { ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config)); } else { ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config)); } } } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; for (CodecConfiguration codec_config : AptxCodecConfigurationsGenerator(CodecType::APTX)) { audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } } TEST_F(BluetoothAudioClientInterfaceTest, A2dpAptxHdCodecOffloadingState) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); auto aptx_hd_codec_configs = AptxCodecConfigurationsGenerator(CodecType::APTX_HD); for (auto codec_offloading_preference : CodecOffloadingPreferenceGenerator( BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD)) { UpdateOffloadingCapabilities(codec_offloading_preference.preference_); for (CodecConfiguration codec_config : aptx_hd_codec_configs) { if (IsCodecOffloadingSupported(codec_config) && codec_offloading_preference.is_target_codec_included_) { ASSERT_TRUE(IsCodecOffloadingEnabled(codec_config)); } else { ASSERT_FALSE(IsCodecOffloadingEnabled(codec_config)); } } } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxHdSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; for (CodecConfiguration codec_config : AptxCodecConfigurationsGenerator(CodecType::APTX_HD)) { audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadUnknownSession) { test_sink_transport_ = new TestSinkTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; CodecConfiguration codec_config = {}; codec_config.codecType = CodecType::UNKNOWN; codec_config.peerMtu = 1005; codec_config.isScmstEnabled = false; codec_config.encodedAudioBitrate = 328000; codec_config.config = {}; audio_config.codecConfig(codec_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsCodecOffloadingSupported(codec_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndHearingAidSoftwareSession) { test_sink_transport_ = new TestSinkTransport( SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration audio_config = {}; PcmParameters pcm_config = {}; for (auto sample_rate_pair : kSampleRatePairs) { pcm_config.sampleRate = sample_rate_pair.hal_sample_rate_; for (auto bits_per_sample_pair : kBitsPerSamplePairs) { pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_; for (auto channel_mode_pair : kChannelModePairs) { pcm_config.channelMode = channel_mode_pair.hal_channel_mode_; audio_config.pcmConfig(pcm_config); clientif_sink_->UpdateAudioConfig(audio_config); if (IsSoftwarePcmParametersSupported(pcm_config)) { ASSERT_EQ(clientif_sink_->StartSession(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } // ChannelMode } // BitsPerSampple } // SampleRate } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndLeAudioEncodingSoftwareSession) { test_sink_transport_ = new TestSinkTransport( SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH); clientif_sink_ = new BluetoothAudioSinkClientInterface(test_sink_transport_, nullptr); AudioConfiguration_2_1 audio_config = {}; PcmParameters_2_1 pcm_config = {}; for (auto sample_rate : kSampleRates_2_1) { pcm_config.sampleRate = sample_rate; for (auto bits_per_sample_pair : kBitsPerSamplePairs) { pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_; for (auto channel_mode_pair : kChannelModePairs) { pcm_config.channelMode = channel_mode_pair.hal_channel_mode_; for (auto data_interval_us : kDataIntervalUs) { pcm_config.dataIntervalUs = data_interval_us; audio_config.pcmConfig(pcm_config); clientif_sink_->UpdateAudioConfig_2_1(audio_config); if (IsSinkSoftwarePcmParameters_2_1_Supported(pcm_config)) { ASSERT_EQ(clientif_sink_->StartSession_2_1(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_sink_->StartSession_2_1(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_sink_->EndSession(), kClientIfReturnSuccess); } // dataIntervalUs } // ChannelMode } // BitsPerSampple } // SampleRate } TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndLeAudioDecodedSoftwareSession) { test_source_transport_ = new TestSourceTransport( SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH); clientif_source_ = new BluetoothAudioSourceClientInterface(test_source_transport_, nullptr); AudioConfiguration_2_1 audio_config = {}; PcmParameters_2_1 pcm_config = {}; for (auto sample_rate : kSampleRates_2_1) { pcm_config.sampleRate = sample_rate; for (auto bits_per_sample_pair : kBitsPerSamplePairs) { pcm_config.bitsPerSample = bits_per_sample_pair.hal_bits_per_sample_; for (auto channel_mode_pair : kChannelModePairs) { pcm_config.channelMode = channel_mode_pair.hal_channel_mode_; for (auto data_interval_us : kDataIntervalUs) { pcm_config.dataIntervalUs = data_interval_us; audio_config.pcmConfig(pcm_config); clientif_source_->UpdateAudioConfig_2_1(audio_config); if (IsSourceSoftwarePcmParameters_2_1_Supported(pcm_config)) { ASSERT_EQ(clientif_source_->StartSession_2_1(), kClientIfReturnSuccess); } else { ASSERT_NE(clientif_source_->StartSession_2_1(), kClientIfReturnSuccess); } ASSERT_EQ(clientif_source_->EndSession(), kClientIfReturnSuccess); } // dataIntervalUs } // ChannelMode } // BitsPerSampple } // SampleRate }