/*
 * Copyright 2020 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 <VtsHalHidlTargetCallbackBase.h>
#include <android-base/logging.h>
#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
#include <fmq/MessageQueue.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/MQDescriptor.h>
#include <hidl/ServiceManagement.h>
#include <utils/Log.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::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::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 ::android::hardware::bluetooth::audio::V2_1::
    IBluetoothAudioProvidersFactory;
using ::android::hardware::bluetooth::audio::V2_1::SampleRate;

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 android::hardware::bluetooth::audio::V2_0::SampleRate
    a2dp_sample_rates[5] = {
        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000,
        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200,
        android::hardware::bluetooth::audio::V2_0::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

// The base test class for Bluetooth Audio HAL.
class BluetoothAudioProvidersFactoryHidlTest
    : public ::testing::TestWithParam<std::string> {
 public:
  virtual void SetUp() override {
    providers_factory_ =
        IBluetoothAudioProvidersFactory::getService(GetParam());
    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;
      }
    }
  }

  void GetProviderCapabilitiesHelper_2_1(
      const android::hardware::bluetooth::audio::V2_1::SessionType&
          session_type) {
    temp_provider_capabilities_2_1_.clear();
    auto hidl_cb =
        [&temp_capabilities = this->temp_provider_capabilities_2_1_](
            const hidl_vec<
                android::hardware::bluetooth::audio::V2_1::AudioCapabilities>&
                audioCapabilities) {
          for (auto audioCapability : audioCapabilities)
            temp_capabilities.push_back(audioCapability);
        };
    auto hidl_retval =
        providers_factory_->getProviderCapabilities_2_1(session_type, hidl_cb);
    // HIDL calls should not be failed and callback has to be executed
    ASSERT_TRUE(hidl_retval.isOk());

    // All software paths are mandatory and must have exact 1 "PcmParameters"
    ASSERT_EQ(temp_provider_capabilities_2_1_.size(), 1);
    ASSERT_EQ(temp_provider_capabilities_2_1_[0].getDiscriminator(),
              android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
                  hidl_discriminator::pcmCapabilities);
  }

  // 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);
    }
  }

  // This helps to open the specified provider and check the openProvider_2_1()
  // 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_2_1(
      const android::hardware::bluetooth::audio::V2_1::SessionType&
          session_type) {
    BluetoothAudioStatus cb_status;
    auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_2_1_](
                       BluetoothAudioStatus status,
                       const sp<android::hardware::bluetooth::audio::V2_1::
                                    IBluetoothAudioProvider>& provider) {
      cb_status = status;
      local_provider = provider;
    };
    auto hidl_retval =
        providers_factory_->openProvider_2_1(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,
          android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
      ASSERT_NE(audio_provider_2_1_, nullptr);
      audio_port_ = new BluetoothAudioPort(*this);
    } else {
      ASSERT_TRUE(
          session_type ==
          android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
      ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
      ASSERT_EQ(audio_provider_2_1_, 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 != android::hardware::bluetooth::audio::
                                          V2_0::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;
  }

  bool IsPcmParametersSupported_2_1(
      const android::hardware::bluetooth::audio::V2_1::PcmParameters&
          pcm_parameters) {
    if (temp_provider_capabilities_2_1_.size() != 1 ||
        temp_provider_capabilities_2_1_[0].getDiscriminator() !=
            android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
                hidl_discriminator::pcmCapabilities) {
      return false;
    }
    auto pcm_capability = temp_provider_capabilities_2_1_[0].pcmCapabilities();
    bool is_parameter_valid =
        (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN &&
         pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
         pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
         pcm_parameters.dataIntervalUs != 0);
    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_;
  std::vector<android::hardware::bluetooth::audio::V2_1::AudioCapabilities>
      temp_provider_capabilities_2_1_;

  // audio_provider_ is for the Bluetooth stack to report session started/ended
  // and handled audio stream started / suspended
  sp<IBluetoothAudioProvider> audio_provider_;
  sp<android::hardware::bluetooth::audio::V2_1::IBluetoothAudioProvider>
      audio_provider_2_1_;

  // 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_P(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {}

/**
 * Test whether we can open a provider for each provider returned by
 * getProviderCapabilities() with non-empty capabalities
 */
TEST_P(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_P(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {}

/**
 * Test whether each provider of type
 * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with
 * different PCM config
 */
TEST_P(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<android::hardware::bluetooth::audio::V2_0::SampleRate>
        sample_rates = ExtractValuesFromBitmask<
            android::hardware::bluetooth::audio::V2_0::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<android::hardware::bluetooth::audio::V2_0::SampleRate>
        sample_rates = ExtractValuesFromBitmask<
            android::hardware::bluetooth::audio::V2_0::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<android::hardware::bluetooth::audio::V2_0::SampleRate>
        sample_rates = ExtractValuesFromBitmask<
            android::hardware::bluetooth::audio::V2_0::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<android::hardware::bluetooth::audio::V2_0::SampleRate>
        sample_rates = ExtractValuesFromBitmask<
            android::hardware::bluetooth::audio::V2_0::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_P(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {}

/**
 * Test whether each provider of type
 * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
 * SBC hardware encoding config
 */
TEST_P(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_P(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_P(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_P(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_P(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 android::hardware::bluetooth::audio::V2_0::SampleRate
      hearing_aid_sample_rates_[3] = {
          android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
          android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000,
          android::hardware::bluetooth::audio::V2_0::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_P(BluetoothAudioProviderHearingAidSoftwareHidlTest,
       OpenHearingAidSoftwareProvider) {}

/**
 * Test whether each provider of type
 * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and
 * stopped with different PCM config
 */
TEST_P(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
}

/**
 * openProvider LE_AUDIO_SOFTWARE_ENCODING_DATAPATH
 */
class BluetoothAudioProviderLeAudioOutputSoftwareHidlTest
    : public BluetoothAudioProvidersFactoryHidlTest {
 public:
  virtual void SetUp() override {
    BluetoothAudioProvidersFactoryHidlTest::SetUp();
    GetProviderCapabilitiesHelper_2_1(
        android::hardware::bluetooth::audio::V2_1::SessionType::
            LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
    OpenProviderHelper_2_1(
        android::hardware::bluetooth::audio::V2_1::SessionType::
            LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
    ASSERT_NE(audio_provider_2_1_, nullptr);
  }

  virtual void TearDown() override {
    audio_port_ = nullptr;
    audio_provider_2_1_ = nullptr;
    BluetoothAudioProvidersFactoryHidlTest::TearDown();
  }

  static constexpr SampleRate le_audio_output_sample_rates_[11] = {
      SampleRate::RATE_UNKNOWN, SampleRate::RATE_8000,  SampleRate::RATE_16000,
      SampleRate::RATE_24000,   SampleRate::RATE_32000, SampleRate::RATE_44100,
      SampleRate::RATE_48000};
  static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
      BitsPerSample::BITS_24};
  static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
  static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
      0 /* Invalid */, 10000 /* Valid 10ms */};
};

/**
 * Test whether each provider of type
 * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
 * stopped
 */
TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
       OpenLeAudioOutputSoftwareProvider) {}

/**
 * Test whether each provider of type
 * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
 * stopped with different PCM config
 */
TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
       DISABLED_StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
  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());
      tempDataMQ.reset(nullptr);
    }
  };
  android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
      {};
  android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
  for (auto sample_rate : le_audio_output_sample_rates_) {
    pcm_parameters.sampleRate = sample_rate;
    for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
      pcm_parameters.bitsPerSample = bits_per_sample;
      for (auto channel_mode : le_audio_output_channel_modes_) {
        pcm_parameters.channelMode = channel_mode;
        for (auto data_interval_us : le_audio_output_data_interval_us_) {
          pcm_parameters.dataIntervalUs = data_interval_us;
          is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
          audio_config.pcmConfig(pcm_parameters);
          auto hidl_retval = audio_provider_2_1_->startSession_2_1(
              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());
          } else {
            EXPECT_TRUE(tempDataMQ == nullptr);
          }
          EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
        }  // uint32_t (data interval in microseconds)
      }    // ChannelMode
    }      // BitsPerSampple
  }        // SampleRate
}

/**
 * openProvider LE_AUDIO_SOFTWARE_DECODED_DATAPATH
 */
class BluetoothAudioProviderLeAudioInputSoftwareHidlTest
    : public BluetoothAudioProvidersFactoryHidlTest {
 public:
  virtual void SetUp() override {
    BluetoothAudioProvidersFactoryHidlTest::SetUp();
    GetProviderCapabilitiesHelper_2_1(
        android::hardware::bluetooth::audio::V2_1::SessionType::
            LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
    OpenProviderHelper_2_1(android::hardware::bluetooth::audio::V2_1::
                               SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
    ASSERT_NE(audio_provider_2_1_, nullptr);
  }

  virtual void TearDown() override {
    audio_port_ = nullptr;
    audio_provider_2_1_ = nullptr;
    BluetoothAudioProvidersFactoryHidlTest::TearDown();
  }

  static constexpr SampleRate le_audio_output_sample_rates_[11] = {
      SampleRate::RATE_UNKNOWN, SampleRate::RATE_8000,  SampleRate::RATE_16000,
      SampleRate::RATE_24000,   SampleRate::RATE_32000, SampleRate::RATE_44100,
      SampleRate::RATE_48000};
  static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
      BitsPerSample::BITS_24};
  static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
  static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
      0 /* Invalid */, 10000 /* Valid 10ms */};
};

/**
 * Test whether each provider of type
 * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
 * stopped
 */
TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
       OpenLeAudioInputSoftwareProvider) {}

/**
 * Test whether each provider of type
 * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
 * stopped with different PCM config
 */
TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
       DISABLED_StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
  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());
      tempDataMQ.reset(nullptr);
    }
  };
  android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
      {};
  android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
  for (auto sample_rate : le_audio_output_sample_rates_) {
    pcm_parameters.sampleRate = sample_rate;
    for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
      pcm_parameters.bitsPerSample = bits_per_sample;
      for (auto channel_mode : le_audio_output_channel_modes_) {
        pcm_parameters.channelMode = channel_mode;
        for (auto data_interval_us : le_audio_output_data_interval_us_) {
          pcm_parameters.dataIntervalUs = data_interval_us;
          is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
          audio_config.pcmConfig(pcm_parameters);
          auto hidl_retval = audio_provider_2_1_->startSession_2_1(
              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());
          } else {
            EXPECT_TRUE(tempDataMQ == nullptr);
          }
          EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
        }  // uint32_t (data interval in microseconds)
      }    // ChannelMode
    }      // BitsPerSampple
  }        // SampleRate
}

static const std::vector<std::string> kAudioInstances =
    android::hardware::getAllHalInstanceNames(
        IBluetoothAudioProvidersFactory::descriptor);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProvidersFactoryHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProvidersFactoryHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProviderA2dpSoftwareHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance,
                         BluetoothAudioProviderA2dpSoftwareHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProviderA2dpHardwareHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance,
                         BluetoothAudioProviderA2dpHardwareHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProviderHearingAidSoftwareHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance,
                         BluetoothAudioProviderHearingAidSoftwareHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProviderLeAudioOutputSoftwareHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance,
                         BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
    BluetoothAudioProviderLeAudioInputSoftwareHidlTest);
INSTANTIATE_TEST_SUITE_P(PerInstance,
                         BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
                         testing::ValuesIn(kAudioInstances),
                         android::hardware::PrintInstanceNameToString);
