/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "client_interface.h"
namespace {
using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
using ::android::hardware::bluetooth::audio::V2_0::CodecType;
using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
using ::bluetooth::audio::AudioCapabilities;
using ::bluetooth::audio::AudioConfiguration;
using ::bluetooth::audio::BitsPerSample;
using ::bluetooth::audio::BluetoothAudioClientInterface;
using ::bluetooth::audio::BluetoothAudioStatus;
using ::bluetooth::audio::ChannelMode;
using ::bluetooth::audio::PcmParameters;
using ::bluetooth::audio::SampleRate;
using ::bluetooth::audio::SessionType;
using ::testing::Test;
constexpr SampleRate kSampleRates[9] = {
SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000,
SampleRate::RATE_88200, SampleRate::RATE_96000, SampleRate::RATE_176400,
SampleRate::RATE_192000, SampleRate::RATE_16000, SampleRate::RATE_24000};
constexpr BitsPerSample kBitsPerSamples[4] = {
BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
BitsPerSample::BITS_32};
constexpr ChannelMode kChannelModes[3] = {
ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
constexpr uint16_t kPeerMtus[5] = {660, 663, 883, 1005, 1500};
class TestTransport : public bluetooth::audio::IBluetoothTransportInstance {
private:
static constexpr uint64_t kRemoteDelayReportMs = 200;
public:
TestTransport(SessionType session_type)
: bluetooth::audio::IBluetoothTransportInstance(session_type, {}){};
bluetooth::audio::BluetoothAudioCtrlAck StartRequest() {
return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
}
bluetooth::audio::BluetoothAudioCtrlAck SuspendRequest() {
return bluetooth::audio::BluetoothAudioCtrlAck::SUCCESS_FINISHED;
}
void StopRequest() {}
bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
uint64_t* total_bytes_readed,
timespec* data_position) {
if (remote_delay_report_ns) {
*remote_delay_report_ns = kRemoteDelayReportMs * 1000000;
}
if (total_bytes_readed) {
*total_bytes_readed = 0;
}
if (data_position) {
clock_gettime(CLOCK_MONOTONIC, data_position);
}
return true;
}
void MetadataChanged(const source_metadata_t& source_metadata __unused) {}
void ResetPresentationPosition(){};
void LogBytesRead(size_t bytes_readed __unused){};
};
class BluetoothAudioClientInterfaceTest : public Test {
protected:
TestTransport* test_transport_;
BluetoothAudioClientInterface* clientif_;
static constexpr int kClientIfReturnSuccess = 0;
virtual void SetUp() override {}
virtual void TearDown() override {
clientif_ = nullptr;
test_transport_ = nullptr;
}
bool IsSoftwarePcmParametersSupported(const PcmParameters& pcm_config) {
const std::vector<AudioCapabilities>& capabilities =
clientif_->GetAudioCapabilities();
PcmParameters pcm_capabilities = capabilities[0].pcmCapabilities();
bool is_pcm_config_valid =
(pcm_config.sampleRate != SampleRate::RATE_UNKNOWN &&
pcm_config.bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
pcm_config.channelMode != ChannelMode::UNKNOWN);
bool is_pcm_config_supported =
(pcm_config.sampleRate & pcm_capabilities.sampleRate &&
pcm_config.bitsPerSample & pcm_capabilities.bitsPerSample &&
pcm_config.channelMode & pcm_capabilities.channelMode);
return (is_pcm_config_valid && is_pcm_config_supported);
}
bool IsOffloadCodecConfigurationSupported(
const CodecConfiguration& codec_config) {
CodecCapabilities codec_capability = {};
for (auto audio_capability : clientif_->GetAudioCapabilities()) {
if (audio_capability.codecCapabilities().codecType ==
codec_config.codecType) {
codec_capability = audio_capability.codecCapabilities();
}
}
if (codec_capability.codecType != codec_config.codecType) {
// codec is unsupported
return false;
}
bool is_codec_config_supported = false;
switch (codec_config.codecType) {
case CodecType::SBC: {
SbcParameters sbc_config = codec_config.config.sbcConfig();
SbcParameters sbc_capability =
codec_capability.capabilities.sbcCapabilities();
is_codec_config_supported =
(sbc_config.sampleRate & sbc_capability.sampleRate &&
sbc_config.channelMode & sbc_capability.channelMode &&
sbc_config.blockLength & sbc_capability.blockLength &&
sbc_config.numSubbands & sbc_capability.numSubbands &&
sbc_config.allocMethod & sbc_capability.allocMethod &&
sbc_config.bitsPerSample & sbc_capability.bitsPerSample &&
(sbc_capability.minBitpool <= sbc_config.minBitpool &&
sbc_config.minBitpool <= sbc_config.maxBitpool &&
sbc_config.maxBitpool <= sbc_capability.maxBitpool));
return is_codec_config_supported;
}
case CodecType::AAC: {
AacParameters aac_config = codec_config.config.aacConfig();
AacParameters aac_capability =
codec_capability.capabilities.aacCapabilities();
is_codec_config_supported =
(aac_config.objectType & aac_capability.objectType &&
aac_config.sampleRate & aac_capability.sampleRate &&
aac_config.channelMode & aac_capability.channelMode &&
(aac_config.variableBitRateEnabled ==
AacVariableBitRate::DISABLED ||
aac_capability.variableBitRateEnabled ==
AacVariableBitRate::ENABLED) &&
aac_config.bitsPerSample & aac_capability.bitsPerSample);
return is_codec_config_supported;
}
case CodecType::LDAC: {
LdacParameters ldac_config = codec_config.config.ldacConfig();
LdacParameters ldac_capability =
codec_capability.capabilities.ldacCapabilities();
is_codec_config_supported =
(ldac_config.sampleRate & ldac_capability.sampleRate &&
ldac_config.channelMode & ldac_capability.channelMode &&
ldac_config.bitsPerSample & ldac_capability.bitsPerSample);
return is_codec_config_supported;
}
case CodecType::APTX:
[[fallthrough]];
case CodecType::APTX_HD: {
AptxParameters aptx_config = codec_config.config.aptxConfig();
AptxParameters aptx_capability =
codec_capability.capabilities.aptxCapabilities();
is_codec_config_supported =
(aptx_config.sampleRate & aptx_capability.sampleRate &&
aptx_config.channelMode & aptx_capability.channelMode &&
aptx_config.bitsPerSample & aptx_capability.bitsPerSample);
return is_codec_config_supported;
}
case CodecType::UNKNOWN:
return false;
}
}
};
} // namespace
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpSoftwareSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
PcmParameters pcm_config = {};
for (auto sample_rate : kSampleRates) {
pcm_config.sampleRate = sample_rate;
for (auto bits_per_sample : kBitsPerSamples) {
pcm_config.bitsPerSample = bits_per_sample;
for (auto channel_mode : kChannelModes) {
pcm_config.channelMode = channel_mode;
audio_config.pcmConfig(pcm_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsSoftwarePcmParametersSupported(pcm_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadSbcSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
SbcBlockLength block_lengths[4] = {
SbcBlockLength::BLOCKS_4, SbcBlockLength::BLOCKS_8,
SbcBlockLength::BLOCKS_12, SbcBlockLength::BLOCKS_16};
SbcNumSubbands num_subbands[2] = {SbcNumSubbands::SUBBAND_4,
SbcNumSubbands::SUBBAND_8};
SbcAllocMethod alloc_methods[2] = {SbcAllocMethod::ALLOC_MD_S,
SbcAllocMethod::ALLOC_MD_L};
for (auto sample_rate : kSampleRates) {
for (auto bits_per_sample : kBitsPerSamples) {
for (auto channel_mode : kChannelModes) {
for (auto peer_mtu : kPeerMtus) {
for (auto block_length : block_lengths) {
for (auto num_subband : num_subbands) {
for (auto alloc_method : alloc_methods) {
codec_config.codecType = CodecType::SBC;
codec_config.peerMtu = peer_mtu;
codec_config.isScmstEnabled = false;
// A2DP_SBC_DEFAULT_BITRATE
codec_config.encodedAudioBitrate = 328000;
SbcParameters sbc = {
.sampleRate = sample_rate,
.channelMode = (channel_mode == ChannelMode::MONO
? SbcChannelMode::MONO
: SbcChannelMode::JOINT_STEREO),
.blockLength = block_length,
.numSubbands = num_subband,
.allocMethod = alloc_method,
.bitsPerSample = bits_per_sample,
.minBitpool = 2,
.maxBitpool = 53};
codec_config.config.sbcConfig(sbc);
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // SbcAllocMethod
} // SbcNumSubbands
} // SbcBlockLength
} // peerMtu
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAacSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
AacObjectType object_types[4] = {
AacObjectType::MPEG2_LC, AacObjectType::MPEG4_LC,
AacObjectType::MPEG4_LTP, AacObjectType::MPEG4_SCALABLE};
AacVariableBitRate variable_bitrates[2] = {AacVariableBitRate::DISABLED,
AacVariableBitRate::ENABLED};
for (auto sample_rate : kSampleRates) {
for (auto bits_per_sample : kBitsPerSamples) {
for (auto channel_mode : kChannelModes) {
for (auto peer_mtu : kPeerMtus) {
for (auto object_type : object_types) {
for (auto variable_bitrate : variable_bitrates) {
codec_config.codecType = CodecType::AAC;
codec_config.peerMtu = peer_mtu;
codec_config.isScmstEnabled = false;
// A2DP_AAC_DEFAULT_BITRATE
codec_config.encodedAudioBitrate = 320000;
AacParameters aac = {.objectType = object_type,
.sampleRate = sample_rate,
.channelMode = channel_mode,
.variableBitRateEnabled = variable_bitrate,
.bitsPerSample = bits_per_sample};
codec_config.config.aacConfig(aac);
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // AacVariableBitRate
} // AacObjectType
} // peerMtu
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadLdacSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
LdacQualityIndex quality_indexes[4] = {
LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
for (auto sample_rate : kSampleRates) {
for (auto bits_per_sample : kBitsPerSamples) {
for (auto channel_mode : kChannelModes) {
for (auto peer_mtu : kPeerMtus) {
for (auto quality_index : quality_indexes) {
codec_config.codecType = CodecType::LDAC;
codec_config.peerMtu = peer_mtu;
codec_config.isScmstEnabled = false;
codec_config.encodedAudioBitrate = 990000;
LdacParameters ldac = {
.sampleRate = sample_rate,
.channelMode = (channel_mode == ChannelMode::MONO
? LdacChannelMode::MONO
: LdacChannelMode::STEREO),
.qualityIndex = quality_index,
.bitsPerSample = bits_per_sample};
codec_config.config.ldacConfig(ldac);
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // LdacQualityIndex
} // peerMtu
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
for (auto sample_rate : kSampleRates) {
for (auto bits_per_sample : kBitsPerSamples) {
for (auto channel_mode : kChannelModes) {
for (auto peer_mtu : kPeerMtus) {
codec_config.codecType = CodecType::APTX;
codec_config.peerMtu = peer_mtu;
codec_config.isScmstEnabled = false;
codec_config.encodedAudioBitrate = 352000;
AptxParameters aptx = {.sampleRate = sample_rate,
.channelMode = channel_mode,
.bitsPerSample = bits_per_sample};
codec_config.config.aptxConfig(aptx);
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // peerMtu
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest, StartAndEndA2dpOffloadAptxHdSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
for (auto sample_rate : kSampleRates) {
for (auto bits_per_sample : kBitsPerSamples) {
for (auto channel_mode : kChannelModes) {
for (auto peer_mtu : kPeerMtus) {
codec_config.codecType = CodecType::APTX_HD;
codec_config.peerMtu = peer_mtu;
codec_config.isScmstEnabled = false;
codec_config.encodedAudioBitrate = 576000;
AptxParameters aptx = {.sampleRate = sample_rate,
.channelMode = channel_mode,
.bitsPerSample = bits_per_sample};
codec_config.config.aptxConfig(aptx);
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // peerMtu
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}
TEST_F(BluetoothAudioClientInterfaceTest,
StartAndEndA2dpOffloadUnknownSession) {
test_transport_ =
new TestTransport(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
CodecConfiguration codec_config = {};
codec_config.codecType = CodecType::UNKNOWN;
codec_config.peerMtu = 1005;
codec_config.isScmstEnabled = false;
codec_config.encodedAudioBitrate = 328000;
codec_config.config = {};
audio_config.codecConfig(codec_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsOffloadCodecConfigurationSupported(codec_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
}
TEST_F(BluetoothAudioClientInterfaceTest,
StartAndEndHearingAidSoftwareSession) {
test_transport_ =
new TestTransport(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
clientif_ = new BluetoothAudioClientInterface(test_transport_, nullptr);
AudioConfiguration audio_config = {};
PcmParameters pcm_config = {};
for (auto sample_rate : kSampleRates) {
pcm_config.sampleRate = sample_rate;
for (auto bits_per_sample : kBitsPerSamples) {
pcm_config.bitsPerSample = bits_per_sample;
for (auto channel_mode : kChannelModes) {
pcm_config.channelMode = channel_mode;
audio_config.pcmConfig(pcm_config);
clientif_->UpdateAudioConfig(audio_config);
if (IsSoftwarePcmParametersSupported(pcm_config)) {
EXPECT_EQ(clientif_->StartSession(), kClientIfReturnSuccess);
} else {
EXPECT_NE(clientif_->StartSession(), kClientIfReturnSuccess);
}
EXPECT_EQ(clientif_->EndSession(), kClientIfReturnSuccess);
} // ChannelMode
} // BitsPerSampple
} // SampleRate
}