From 56d54f165525bf9d135381a31ad12b33c45bd1fa Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Fri, 30 Nov 2018 16:29:47 +0800 Subject: [PATCH] VTS for Bluetooth Audio HAL v2 Initial VTS commit for the bluetooth.audio@2.0 HAL Bug: 111519504 Test: VTS (VtsHalBluetoothAudioV2_0TargetTest) passed Change-Id: Id607979724182de242c29747218f6b12880d6295 --- bluetooth/audio/2.0/vts/OWNERS | 3 + bluetooth/audio/2.0/vts/functional/Android.bp | 12 + .../VtsHalBluetoothAudioV2_0TargetTest.cpp | 915 ++++++++++++++++++ 3 files changed, 930 insertions(+) create mode 100644 bluetooth/audio/2.0/vts/OWNERS create mode 100644 bluetooth/audio/2.0/vts/functional/Android.bp create mode 100644 bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp diff --git a/bluetooth/audio/2.0/vts/OWNERS b/bluetooth/audio/2.0/vts/OWNERS new file mode 100644 index 0000000000..b6c0813977 --- /dev/null +++ b/bluetooth/audio/2.0/vts/OWNERS @@ -0,0 +1,3 @@ +include platform/system/bt:/OWNERS + +cheneyni@google.com diff --git a/bluetooth/audio/2.0/vts/functional/Android.bp b/bluetooth/audio/2.0/vts/functional/Android.bp new file mode 100644 index 0000000000..b672fe4970 --- /dev/null +++ b/bluetooth/audio/2.0/vts/functional/Android.bp @@ -0,0 +1,12 @@ +cc_test { + name: "VtsHalBluetoothAudioV2_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBluetoothAudioV2_0TargetTest.cpp"], + static_libs: [ + "android.hardware.audio.common@5.0", + "android.hardware.bluetooth.audio@2.0", + ], + shared_libs: [ + "libfmq", + ], +} diff --git a/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp new file mode 100644 index 0000000000..9572d3f47b --- /dev/null +++ b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp @@ -0,0 +1,915 @@ +/* + * Copyright 2018 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_audio_hidl_hal_test" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using ::android::sp; +using ::android::hardware::hidl_vec; +using ::android::hardware::kSynchronizedReadWrite; +using ::android::hardware::MessageQueue; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::audio::common::V5_0::SourceMetadata; +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::AudioCapabilities; +using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration; +using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample; +using ::android::hardware::bluetooth::audio::V2_0::ChannelMode; +using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities; +using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration; +using ::android::hardware::bluetooth::audio::V2_0::CodecType; +using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort; +using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider; +using ::android::hardware::bluetooth::audio::V2_0:: + IBluetoothAudioProvidersFactory; +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::PcmParameters; +using ::android::hardware::bluetooth::audio::V2_0::SampleRate; +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 ::android::hardware::bluetooth::audio::V2_0::SessionType; + +using DataMQ = MessageQueue; +using BluetoothAudioStatus = + ::android::hardware::bluetooth::audio::V2_0::Status; +using CodecSpecificConfig = ::android::hardware::bluetooth::audio::V2_0:: + CodecConfiguration::CodecSpecific; + +namespace { +constexpr SampleRate a2dp_sample_rates[5] = { + SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000, + SampleRate::RATE_88200, SampleRate::RATE_96000}; +constexpr BitsPerSample a2dp_bits_per_samples[4] = { + BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24, + BitsPerSample::BITS_32}; +constexpr ChannelMode a2dp_channel_modes[3] = { + ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO}; +constexpr CodecType a2dp_codec_types[6] = {CodecType::UNKNOWN, CodecType::SBC, + CodecType::AAC, CodecType::APTX, + CodecType::APTX_HD, CodecType::LDAC}; + +template +std::vector ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield, + bool supported) { + std::vector retval; + if (!supported) { + retval.push_back(static_cast(bitfield)); + } + uint32_t test_bit = 0x00000001; + while (test_bit <= static_cast(bitmasks) && test_bit <= bitfield) { + if ((bitfield & test_bit)) { + if ((!(bitmasks & test_bit) && !supported) || + ((bitmasks & test_bit) && supported)) { + retval.push_back(static_cast(test_bit)); + } + } + if (test_bit == 0x80000000) { + break; + } + test_bit <<= 1; + } + return retval; +} +} // namespace + +// Test environment for Bluetooth Audio HAL. +class BluetoothAudioHidlEnvironment + : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static BluetoothAudioHidlEnvironment* Instance() { + static BluetoothAudioHidlEnvironment* instance = + new BluetoothAudioHidlEnvironment; + return instance; + } + + virtual void registerTestServices() override { + registerTestService(); + } + + private: + BluetoothAudioHidlEnvironment() {} +}; + +// The base test class for Bluetooth Audio HAL. +class BluetoothAudioProvidersFactoryHidlTest + : public ::testing::VtsHalHidlTargetTestBase { + public: + virtual void SetUp() override { + providers_factory_ = ::testing::VtsHalHidlTargetTestBase::getService< + IBluetoothAudioProvidersFactory>( + BluetoothAudioHidlEnvironment::Instance() + ->getServiceName()); + ASSERT_NE(providers_factory_, nullptr); + } + + virtual void TearDown() override { providers_factory_ = nullptr; } + + // A simple test implementation of IBluetoothAudioPort. + class BluetoothAudioPort : public ::testing::VtsHalHidlTargetCallbackBase< + BluetoothAudioProvidersFactoryHidlTest>, + public IBluetoothAudioPort { + BluetoothAudioProvidersFactoryHidlTest& parent_; + + public: + BluetoothAudioPort(BluetoothAudioProvidersFactoryHidlTest& parent) + : parent_(parent) {} + virtual ~BluetoothAudioPort() = default; + + Return startStream() override { + parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS); + return Void(); + } + + Return suspendStream() override { + parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS); + return Void(); + } + + Return stopStream() override { return Void(); } + + Return getPresentationPosition(getPresentationPosition_cb _hidl_cb) { + _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0}); + return Void(); + } + + Return updateMetadata(const SourceMetadata& sourceMetadata __unused) { + return Void(); + } + }; + + void GetProviderCapabilitiesHelper(const SessionType& session_type) { + temp_provider_capabilities_.clear(); + auto hidl_cb = [& temp_capabilities = this->temp_provider_capabilities_]( + const hidl_vec& audioCapabilities) { + for (auto audioCapability : audioCapabilities) + temp_capabilities.push_back(audioCapability); + }; + auto hidl_retval = + providers_factory_->getProviderCapabilities(session_type, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + if (session_type == SessionType::UNKNOWN) { + ASSERT_TRUE(temp_provider_capabilities_.empty()); + } else if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) { + // All software paths are mandatory and must have exact 1 "PcmParameters" + ASSERT_EQ(temp_provider_capabilities_.size(), 1); + ASSERT_EQ(temp_provider_capabilities_[0].getDiscriminator(), + AudioCapabilities::hidl_discriminator::pcmCapabilities); + } else { + uint32_t codec_type_bitmask = 0x00000000; + // empty capability means offload is unsupported + for (auto audio_capability : temp_provider_capabilities_) { + ASSERT_EQ(audio_capability.getDiscriminator(), + AudioCapabilities::hidl_discriminator::codecCapabilities); + const CodecCapabilities& codec_capabilities = + audio_capability.codecCapabilities(); + // Every codec can present once at most + ASSERT_EQ(codec_type_bitmask & + static_cast(codec_capabilities.codecType), + 0); + switch (codec_capabilities.codecType) { + case CodecType::SBC: + ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(), + CodecCapabilities::Capabilities::hidl_discriminator:: + sbcCapabilities); + break; + case CodecType::AAC: + ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(), + CodecCapabilities::Capabilities::hidl_discriminator:: + aacCapabilities); + break; + case CodecType::APTX: + FALLTHROUGH_INTENDED; + case CodecType::APTX_HD: + ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(), + CodecCapabilities::Capabilities::hidl_discriminator:: + aptxCapabilities); + break; + case CodecType::LDAC: + ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(), + CodecCapabilities::Capabilities::hidl_discriminator:: + ldacCapabilities); + break; + case CodecType::UNKNOWN: + break; + } + codec_type_bitmask |= codec_capabilities.codecType; + } + } + } + + // This helps to open the specified provider and check the openProvider() + // has corruct return values. BUT, to keep it simple, it does not consider + // the capability, and please do so at the SetUp of each session's test. + void OpenProviderHelper(const SessionType& session_type) { + BluetoothAudioStatus cb_status; + auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_]( + BluetoothAudioStatus status, + const sp& provider) { + cb_status = status; + local_provider = provider; + }; + auto hidl_retval = providers_factory_->openProvider(session_type, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + if (cb_status == BluetoothAudioStatus::SUCCESS) { + ASSERT_NE(session_type, SessionType::UNKNOWN); + ASSERT_NE(audio_provider_, nullptr); + audio_port_ = new BluetoothAudioPort(*this); + } else { + // A2DP_HARDWARE_OFFLOAD_DATAPATH is optional + ASSERT_TRUE(session_type == SessionType::UNKNOWN || + session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); + ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE); + ASSERT_EQ(audio_provider_, nullptr); + } + } + + bool IsPcmParametersSupported(const PcmParameters& pcm_parameters) { + if (temp_provider_capabilities_.size() != 1 || + temp_provider_capabilities_[0].getDiscriminator() != + AudioCapabilities::hidl_discriminator::pcmCapabilities) { + return false; + } + auto pcm_capability = temp_provider_capabilities_[0].pcmCapabilities(); + bool is_parameter_valid = + (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN && + pcm_parameters.channelMode != ChannelMode::UNKNOWN && + pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN); + bool is_parameter_in_capability = + (pcm_capability.sampleRate & pcm_parameters.sampleRate && + pcm_capability.channelMode & pcm_parameters.channelMode && + pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample); + return is_parameter_valid && is_parameter_in_capability; + } + + sp providers_factory_; + + // temp storage saves the specified provider capability by + // GetProviderCapabilitiesHelper() + std::vector temp_provider_capabilities_; + + // audio_provider_ is for the Bluetooth stack to report session started/ended + // and handled audio stream started / suspended + sp audio_provider_; + + // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands + // to Bluetooth stack + sp audio_port_; + + static constexpr SessionType session_types_[4] = { + SessionType::UNKNOWN, SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH, + SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH, + SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH}; +}; + +/** + * Test whether we can get the FactoryService from HIDL + */ +TEST_F(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {} + +/** + * Test whether we can open a provider for each provider returned by + * getProviderCapabilities() with non-empty capabalities + */ +TEST_F(BluetoothAudioProvidersFactoryHidlTest, + OpenProviderAndCheckCapabilitiesBySession) { + for (auto session_type : session_types_) { + GetProviderCapabilitiesHelper(session_type); + OpenProviderHelper(session_type); + // We must be able to open a provider if its getProviderCapabilities() + // returns non-empty list. + EXPECT_TRUE(temp_provider_capabilities_.empty() || + audio_provider_ != nullptr); + } +} + +/** + * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH + */ +class BluetoothAudioProviderA2dpSoftwareHidlTest + : public BluetoothAudioProvidersFactoryHidlTest { + public: + virtual void SetUp() override { + BluetoothAudioProvidersFactoryHidlTest::SetUp(); + GetProviderCapabilitiesHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH); + OpenProviderHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH); + ASSERT_NE(audio_provider_, nullptr); + } + + virtual void TearDown() override { + audio_port_ = nullptr; + audio_provider_ = nullptr; + BluetoothAudioProvidersFactoryHidlTest::TearDown(); + } +}; + +/** + * Test whether we can open a provider of type + */ +TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {} + +/** + * Test whether each provider of type + * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with + * different PCM config + */ +TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest, + StartAndEndA2dpSoftwareSessionWithPossiblePcmConfig) { + bool is_codec_config_valid; + std::unique_ptr tempDataMQ; + auto hidl_cb = [&is_codec_config_valid, &tempDataMQ]( + BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + if (is_codec_config_valid) { + ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS); + ASSERT_TRUE(dataMQ.isHandleValid()); + tempDataMQ.reset(new DataMQ(dataMQ)); + } else { + EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION); + EXPECT_FALSE(dataMQ.isHandleValid()); + } + }; + AudioConfiguration audio_config = {}; + PcmParameters pcm_parameters = {}; + for (auto sample_rate : a2dp_sample_rates) { + pcm_parameters.sampleRate = sample_rate; + for (auto bits_per_sample : a2dp_bits_per_samples) { + pcm_parameters.bitsPerSample = bits_per_sample; + for (auto channel_mode : a2dp_channel_modes) { + pcm_parameters.channelMode = channel_mode; + is_codec_config_valid = IsPcmParametersSupported(pcm_parameters); + audio_config.pcmConfig(pcm_parameters); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + if (is_codec_config_valid) { + EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid()); + } + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } // ChannelMode + } // BitsPerSampple + } // SampleRate +} + +/** + * openProvider A2DP_HARDWARE_OFFLOAD_DATAPATH + */ +class BluetoothAudioProviderA2dpHardwareHidlTest + : public BluetoothAudioProvidersFactoryHidlTest { + public: + virtual void SetUp() override { + BluetoothAudioProvidersFactoryHidlTest::SetUp(); + GetProviderCapabilitiesHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); + OpenProviderHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH); + ASSERT_TRUE(temp_provider_capabilities_.empty() || + audio_provider_ != nullptr); + } + + virtual void TearDown() override { + audio_port_ = nullptr; + audio_provider_ = nullptr; + BluetoothAudioProvidersFactoryHidlTest::TearDown(); + } + + bool IsOffloadSupported() { return (temp_provider_capabilities_.size() > 0); } + + void GetOffloadCodecCapabilityHelper(const CodecType& codec_type) { + temp_codec_capabilities_ = {}; + for (auto codec_capability : temp_provider_capabilities_) { + if (codec_capability.codecCapabilities().codecType != codec_type) { + continue; + } + temp_codec_capabilities_ = codec_capability.codecCapabilities(); + } + } + + std::vector GetSbcCodecSpecificSupportedList( + bool supported) { + std::vector sbc_codec_specifics; + GetOffloadCodecCapabilityHelper(CodecType::SBC); + if (temp_codec_capabilities_.codecType != CodecType::SBC) { + return sbc_codec_specifics; + } + // parse the capability + SbcParameters sbc_capability = + temp_codec_capabilities_.capabilities.sbcCapabilities(); + if (sbc_capability.minBitpool > sbc_capability.maxBitpool) { + return sbc_codec_specifics; + } + std::vector sample_rates = ExtractValuesFromBitmask( + sbc_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(sbc_capability.channelMode, + 0x0f, supported); + std::vector block_lengths = + ExtractValuesFromBitmask(sbc_capability.blockLength, + 0xf0, supported); + std::vector num_subbandss = + ExtractValuesFromBitmask(sbc_capability.numSubbands, + 0x0c, supported); + std::vector alloc_methods = + ExtractValuesFromBitmask(sbc_capability.allocMethod, + 0x03, supported); + std::vector bits_per_samples = + ExtractValuesFromBitmask(sbc_capability.bitsPerSample, + 0x07, supported); + // combine those parameters into one list of + // CodecConfiguration::CodecSpecific + CodecSpecificConfig codec_specific = {}; + SbcParameters sbc_data; + for (auto sample_rate : sample_rates) { + for (auto channel_mode : channel_modes) { + for (auto block_length : block_lengths) { + for (auto num_subbands : num_subbandss) { + for (auto alloc_method : alloc_methods) { + for (auto bits_per_sample : bits_per_samples) { + sbc_data = {.sampleRate = sample_rate, + .channelMode = channel_mode, + .blockLength = block_length, + .numSubbands = num_subbands, + .allocMethod = alloc_method, + .bitsPerSample = bits_per_sample, + .minBitpool = sbc_capability.minBitpool, + .maxBitpool = sbc_capability.maxBitpool}; + codec_specific.sbcConfig(sbc_data); + sbc_codec_specifics.push_back(codec_specific); + } + } + } + } + } + } + return sbc_codec_specifics; + } + + std::vector GetAacCodecSpecificSupportedList( + bool supported) { + std::vector aac_codec_specifics; + GetOffloadCodecCapabilityHelper(CodecType::AAC); + if (temp_codec_capabilities_.codecType != CodecType::AAC) { + return aac_codec_specifics; + } + // parse the capability + AacParameters aac_capability = + temp_codec_capabilities_.capabilities.aacCapabilities(); + std::vector object_types = + ExtractValuesFromBitmask(aac_capability.objectType, 0xf0, + supported); + std::vector sample_rates = ExtractValuesFromBitmask( + aac_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(aac_capability.channelMode, 0x03, + supported); + std::vector variable_bit_rate_enableds = { + AacVariableBitRate::DISABLED}; + if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) { + variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED); + } + std::vector bits_per_samples = + ExtractValuesFromBitmask(aac_capability.bitsPerSample, + 0x07, supported); + // combine those parameters into one list of + // CodecConfiguration::CodecSpecific + CodecSpecificConfig codec_specific = {}; + AacParameters aac_data; + for (auto object_type : object_types) { + for (auto sample_rate : sample_rates) { + for (auto channel_mode : channel_modes) { + for (auto variable_bit_rate_enabled : variable_bit_rate_enableds) { + for (auto bits_per_sample : bits_per_samples) { + aac_data = {.objectType = object_type, + .sampleRate = sample_rate, + .channelMode = channel_mode, + .variableBitRateEnabled = variable_bit_rate_enabled, + .bitsPerSample = bits_per_sample}; + codec_specific.aacConfig(aac_data); + aac_codec_specifics.push_back(codec_specific); + } + } + } + } + } + return aac_codec_specifics; + } + + std::vector GetLdacCodecSpecificSupportedList( + bool supported) { + std::vector ldac_codec_specifics; + GetOffloadCodecCapabilityHelper(CodecType::LDAC); + if (temp_codec_capabilities_.codecType != CodecType::LDAC) { + return ldac_codec_specifics; + } + // parse the capability + LdacParameters ldac_capability = + temp_codec_capabilities_.capabilities.ldacCapabilities(); + std::vector sample_rates = ExtractValuesFromBitmask( + ldac_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(ldac_capability.channelMode, + 0x07, supported); + std::vector quality_indexes = { + LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID, + LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR}; + std::vector bits_per_samples = + ExtractValuesFromBitmask(ldac_capability.bitsPerSample, + 0x07, supported); + // combine those parameters into one list of + // CodecConfiguration::CodecSpecific + CodecSpecificConfig codec_specific = {}; + LdacParameters ldac_data; + for (auto sample_rate : sample_rates) { + for (auto channel_mode : channel_modes) { + for (auto quality_index : quality_indexes) { + for (auto bits_per_sample : bits_per_samples) { + ldac_data = {.sampleRate = sample_rate, + .channelMode = channel_mode, + .qualityIndex = quality_index, + .bitsPerSample = bits_per_sample}; + codec_specific.ldacConfig(ldac_data); + ldac_codec_specifics.push_back(codec_specific); + } + } + } + } + return ldac_codec_specifics; + } + + std::vector GetAptxCodecSpecificSupportedList( + bool is_hd, bool supported) { + std::vector aptx_codec_specifics; + GetOffloadCodecCapabilityHelper( + (is_hd ? CodecType::APTX_HD : CodecType::APTX)); + if ((is_hd && temp_codec_capabilities_.codecType != CodecType::APTX_HD) || + (!is_hd && temp_codec_capabilities_.codecType != CodecType::APTX)) { + return aptx_codec_specifics; + } + // parse the capability + AptxParameters aptx_capability = + temp_codec_capabilities_.capabilities.aptxCapabilities(); + std::vector sample_rates = ExtractValuesFromBitmask( + aptx_capability.sampleRate, 0xff, supported); + std::vector channel_modes = + ExtractValuesFromBitmask(aptx_capability.channelMode, 0x03, + supported); + std::vector bits_per_samples = + ExtractValuesFromBitmask(aptx_capability.bitsPerSample, + 0x07, supported); + // combine those parameters into one list of + // CodecConfiguration::CodecSpecific + CodecSpecificConfig codec_specific = {}; + AptxParameters aptx_data; + for (auto sample_rate : sample_rates) { + for (auto channel_mode : channel_modes) { + for (auto bits_per_sample : bits_per_samples) { + aptx_data = {.sampleRate = sample_rate, + .channelMode = channel_mode, + .bitsPerSample = bits_per_sample}; + codec_specific.aptxConfig(aptx_data); + aptx_codec_specifics.push_back(codec_specific); + } + } + } + return aptx_codec_specifics; + } + + // temp storage saves the specified codec capability by + // GetOffloadCodecCapabilityHelper() + CodecCapabilities temp_codec_capabilities_; +}; + +/** + * Test whether we can open a provider of type + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {} + +/** + * Test whether each provider of type + * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with + * SBC hardware encoding config + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, + StartAndEndA2dpSbcHardwareSession) { + if (!IsOffloadSupported()) { + return; + } + + CodecConfiguration codec_config = {}; + codec_config.codecType = CodecType::SBC; + codec_config.encodedAudioBitrate = 328000; + codec_config.peerMtu = 1005; + codec_config.isScmstEnabled = false; + AudioConfiguration audio_config = {}; + std::vector sbc_codec_specifics = + GetSbcCodecSpecificSupportedList(true); + auto hidl_cb = [](BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS); + EXPECT_FALSE(dataMQ.isHandleValid()); + }; + for (auto codec_specific : sbc_codec_specifics) { + codec_config.config = codec_specific; + audio_config.codecConfig(codec_config); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } +} + +/** + * Test whether each provider of type + * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with + * AAC hardware encoding config + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, + StartAndEndA2dpAacHardwareSession) { + if (!IsOffloadSupported()) { + return; + } + + CodecConfiguration codec_config = {}; + codec_config.codecType = CodecType::AAC; + codec_config.encodedAudioBitrate = 320000; + codec_config.peerMtu = 1005; + codec_config.isScmstEnabled = false; + AudioConfiguration audio_config = {}; + std::vector aac_codec_specifics = + GetAacCodecSpecificSupportedList(true); + auto hidl_cb = [](BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS); + EXPECT_FALSE(dataMQ.isHandleValid()); + }; + for (auto codec_specific : aac_codec_specifics) { + codec_config.config = codec_specific; + audio_config.codecConfig(codec_config); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } +} + +/** + * Test whether each provider of type + * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with + * LDAC hardware encoding config + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, + StartAndEndA2dpLdacHardwareSession) { + if (!IsOffloadSupported()) { + return; + } + + CodecConfiguration codec_config = {}; + codec_config.codecType = CodecType::LDAC; + codec_config.encodedAudioBitrate = 990000; + codec_config.peerMtu = 1005; + codec_config.isScmstEnabled = false; + AudioConfiguration audio_config = {}; + std::vector ldac_codec_specifics = + GetLdacCodecSpecificSupportedList(true); + auto hidl_cb = [](BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS); + EXPECT_FALSE(dataMQ.isHandleValid()); + }; + for (auto codec_specific : ldac_codec_specifics) { + codec_config.config = codec_specific; + audio_config.codecConfig(codec_config); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } +} + +/** + * Test whether each provider of type + * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with + * AptX hardware encoding config + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, + StartAndEndA2dpAptxHardwareSession) { + if (!IsOffloadSupported()) { + return; + } + + for (auto codec_type : {CodecType::APTX, CodecType::APTX_HD}) { + CodecConfiguration codec_config = {}; + codec_config.codecType = codec_type; + codec_config.encodedAudioBitrate = + (codec_type == CodecType::APTX ? 352000 : 576000); + codec_config.peerMtu = 1005; + codec_config.isScmstEnabled = false; + AudioConfiguration audio_config = {}; + std::vector aptx_codec_specifics = + GetAptxCodecSpecificSupportedList( + (codec_type == CodecType::APTX_HD ? true : false), true); + auto hidl_cb = [](BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS); + EXPECT_FALSE(dataMQ.isHandleValid()); + }; + for (auto codec_specific : aptx_codec_specifics) { + codec_config.config = codec_specific; + audio_config.codecConfig(codec_config); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } + } +} + +/** + * Test whether each provider of type + * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with + * an invalid codec config + */ +TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, + StartAndEndA2dpHardwareSessionInvalidCodecConfig) { + if (!IsOffloadSupported()) { + return; + } + ASSERT_NE(audio_provider_, nullptr); + + std::vector codec_specifics; + for (auto codec_type : a2dp_codec_types) { + switch (codec_type) { + case CodecType::SBC: + codec_specifics = GetSbcCodecSpecificSupportedList(false); + break; + case CodecType::AAC: + codec_specifics = GetAacCodecSpecificSupportedList(false); + break; + case CodecType::LDAC: + codec_specifics = GetLdacCodecSpecificSupportedList(false); + break; + case CodecType::APTX: + codec_specifics = GetAptxCodecSpecificSupportedList(false, false); + break; + case CodecType::APTX_HD: + codec_specifics = GetAptxCodecSpecificSupportedList(true, false); + break; + case CodecType::UNKNOWN: + codec_specifics.clear(); + break; + } + if (codec_specifics.empty()) { + continue; + } + + CodecConfiguration codec_config = {}; + codec_config.codecType = codec_type; + codec_config.encodedAudioBitrate = 328000; + codec_config.peerMtu = 1005; + codec_config.isScmstEnabled = false; + AudioConfiguration audio_config = {}; + auto hidl_cb = [](BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION); + EXPECT_FALSE(dataMQ.isHandleValid()); + }; + for (auto codec_specific : codec_specifics) { + codec_config.config = codec_specific; + audio_config.codecConfig(codec_config); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } + } +} + +/** + * openProvider HEARING_AID_SOFTWARE_ENCODING_DATAPATH + */ +class BluetoothAudioProviderHearingAidSoftwareHidlTest + : public BluetoothAudioProvidersFactoryHidlTest { + public: + virtual void SetUp() override { + BluetoothAudioProvidersFactoryHidlTest::SetUp(); + GetProviderCapabilitiesHelper( + SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH); + OpenProviderHelper(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH); + ASSERT_NE(audio_provider_, nullptr); + } + + virtual void TearDown() override { + audio_port_ = nullptr; + audio_provider_ = nullptr; + BluetoothAudioProvidersFactoryHidlTest::TearDown(); + } + + static constexpr SampleRate hearing_aid_sample_rates_[3] = { + SampleRate::RATE_UNKNOWN, SampleRate::RATE_16000, SampleRate::RATE_24000}; + static constexpr BitsPerSample hearing_aid_bits_per_samples_[3] = { + BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, + BitsPerSample::BITS_24}; + static constexpr ChannelMode hearing_aid_channel_modes_[3] = { + ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO}; +}; + +/** + * Test whether each provider of type + * SessionType::HEARING_AID_HARDWARE_ENCODING_DATAPATH can be started and + * stopped with SBC hardware encoding config + */ +TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest, + OpenHearingAidSoftwareProvider) {} + +/** + * Test whether each provider of type + * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and + * stopped with different PCM config + */ +TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest, + StartAndEndHearingAidSessionWithPossiblePcmConfig) { + bool is_codec_config_valid; + std::unique_ptr tempDataMQ; + auto hidl_cb = [&is_codec_config_valid, &tempDataMQ]( + BluetoothAudioStatus status, + const DataMQ::Descriptor& dataMQ) { + if (is_codec_config_valid) { + ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS); + ASSERT_TRUE(dataMQ.isHandleValid()); + tempDataMQ.reset(new DataMQ(dataMQ)); + } else { + EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION); + EXPECT_FALSE(dataMQ.isHandleValid()); + } + }; + AudioConfiguration audio_config = {}; + PcmParameters pcm_parameters = {}; + for (auto sample_rate : hearing_aid_sample_rates_) { + pcm_parameters.sampleRate = sample_rate; + for (auto bits_per_sample : hearing_aid_bits_per_samples_) { + pcm_parameters.bitsPerSample = bits_per_sample; + for (auto channel_mode : hearing_aid_channel_modes_) { + pcm_parameters.channelMode = channel_mode; + is_codec_config_valid = IsPcmParametersSupported(pcm_parameters); + audio_config.pcmConfig(pcm_parameters); + auto hidl_retval = + audio_provider_->startSession(audio_port_, audio_config, hidl_cb); + // HIDL calls should not be failed and callback has to be executed + ASSERT_TRUE(hidl_retval.isOk()); + if (is_codec_config_valid) { + EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid()); + } + EXPECT_TRUE(audio_provider_->endSession().isOk()); + } // ChannelMode + } // BitsPerSampple + } // SampleRate +} + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment( + BluetoothAudioHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + BluetoothAudioHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +}