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
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 0000000..b672fe4
--- /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 0000000..9572d3f
--- /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 <android-base/logging.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <utils/Log.h>
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+
+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<uint8_t, kSynchronizedReadWrite>;
+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 <typename T>
+std::vector<T> ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield,
+ bool supported) {
+ std::vector<T> retval;
+ if (!supported) {
+ retval.push_back(static_cast<T>(bitfield));
+ }
+ uint32_t test_bit = 0x00000001;
+ while (test_bit <= static_cast<uint32_t>(bitmasks) && test_bit <= bitfield) {
+ if ((bitfield & test_bit)) {
+ if ((!(bitmasks & test_bit) && !supported) ||
+ ((bitmasks & test_bit) && supported)) {
+ retval.push_back(static_cast<T>(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<IBluetoothAudioProvidersFactory>();
+ }
+
+ 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<IBluetoothAudioProvidersFactory>());
+ 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<void> startStream() override {
+ parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> suspendStream() override {
+ parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> stopStream() override { return Void(); }
+
+ Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0});
+ return Void();
+ }
+
+ Return<void> 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>& 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<uint32_t>(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<IBluetoothAudioProvider>& 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<IBluetoothAudioProvidersFactory> providers_factory_;
+
+ // temp storage saves the specified provider capability by
+ // GetProviderCapabilitiesHelper()
+ std::vector<AudioCapabilities> temp_provider_capabilities_;
+
+ // audio_provider_ is for the Bluetooth stack to report session started/ended
+ // and handled audio stream started / suspended
+ sp<IBluetoothAudioProvider> audio_provider_;
+
+ // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands
+ // to Bluetooth stack
+ sp<IBluetoothAudioPort> 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<DataMQ> 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<CodecSpecificConfig> GetSbcCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> 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<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ sbc_capability.sampleRate, 0xff, supported);
+ std::vector<SbcChannelMode> channel_modes =
+ ExtractValuesFromBitmask<SbcChannelMode>(sbc_capability.channelMode,
+ 0x0f, supported);
+ std::vector<SbcBlockLength> block_lengths =
+ ExtractValuesFromBitmask<SbcBlockLength>(sbc_capability.blockLength,
+ 0xf0, supported);
+ std::vector<SbcNumSubbands> num_subbandss =
+ ExtractValuesFromBitmask<SbcNumSubbands>(sbc_capability.numSubbands,
+ 0x0c, supported);
+ std::vector<SbcAllocMethod> alloc_methods =
+ ExtractValuesFromBitmask<SbcAllocMethod>(sbc_capability.allocMethod,
+ 0x03, supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(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<CodecSpecificConfig> GetAacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> 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<AacObjectType> object_types =
+ ExtractValuesFromBitmask<AacObjectType>(aac_capability.objectType, 0xf0,
+ supported);
+ std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ aac_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aac_capability.channelMode, 0x03,
+ supported);
+ std::vector<AacVariableBitRate> variable_bit_rate_enableds = {
+ AacVariableBitRate::DISABLED};
+ if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) {
+ variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED);
+ }
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(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<CodecSpecificConfig> GetLdacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> 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<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ ldac_capability.sampleRate, 0xff, supported);
+ std::vector<LdacChannelMode> channel_modes =
+ ExtractValuesFromBitmask<LdacChannelMode>(ldac_capability.channelMode,
+ 0x07, supported);
+ std::vector<LdacQualityIndex> quality_indexes = {
+ LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
+ LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(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<CodecSpecificConfig> GetAptxCodecSpecificSupportedList(
+ bool is_hd, bool supported) {
+ std::vector<CodecSpecificConfig> 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<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ aptx_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aptx_capability.channelMode, 0x03,
+ supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(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<CodecSpecificConfig> 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<CodecSpecificConfig> 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<CodecSpecificConfig> 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<CodecSpecificConfig> 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<CodecSpecificConfig> 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<DataMQ> 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;
+}