/* * Copyright 2022 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 "stack/include/a2dp_aac.h" #include #include #include #include #include #include #include #include #include #include "common/init_flags.h" #include "common/testing/log_capture.h" #include "common/time_util.h" #include "os/log.h" #include "osi/include/allocator.h" #include "osi/test/AllocationTestHarness.h" #include "stack/include/bt_hdr.h" #include "stack/include/a2dp_aac_decoder.h" #include "stack/include/a2dp_aac_encoder.h" #include "stack/include/avdt_api.h" #include "test_util.h" #include "wav_reader.h" extern void allocation_tracker_uninit(void); namespace { constexpr uint32_t kAacReadSize = 1024 * 2 * 2; constexpr uint32_t kA2dpTickUs = 23 * 1000; constexpr char kDecodedDataCallbackIsInvoked[] = "A2DP decoded data callback is invoked."; constexpr char kEnqueueCallbackIsInvoked[] = "A2DP source enqueue callback is invoked."; constexpr uint16_t kPeerMtu = 1000; constexpr char kWavFile[] = "test/a2dp/raw_data/pcm1644s.wav"; constexpr uint8_t kCodecInfoAacCapability[AVDT_CODEC_SIZE] = { 8, // Length (A2DP_AAC_INFO_LEN) 0, // Media Type: AVDT_MEDIA_TYPE_AUDIO 2, // Media Codec Type: A2DP_MEDIA_CT_AAC 0x80, // Object Type: A2DP_AAC_OBJECT_TYPE_MPEG2_LC 0x01, // Sampling Frequency: A2DP_AAC_SAMPLING_FREQ_44100 0x04, // Channels: A2DP_AAC_CHANNEL_MODE_STEREO 0x00 | 0x4, // Variable Bit Rate: // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED // Bit Rate: 320000 = 0x4e200 0xe2, // Bit Rate: 320000 = 0x4e200 0x00, // Bit Rate: 320000 = 0x4e200 7, // Unused 8, // Unused 9 // Unused }; uint8_t* Data(BT_HDR* packet) { return packet->data + packet->offset; } } // namespace namespace bluetooth { namespace testing { static BT_HDR* packet = nullptr; static WavReader wav_reader = WavReader(GetWavFilePath(kWavFile).c_str()); class A2dpAacTest : public AllocationTestHarness { protected: void SetUp() override { AllocationTestHarness::SetUp(); common::InitFlags::SetAllForTesting(); // Disable our allocation tracker to allow ASAN full range allocation_tracker_uninit(); SetCodecConfig(); encoder_iface_ = const_cast( A2DP_GetEncoderInterfaceAac(kCodecInfoAacCapability)); ASSERT_NE(encoder_iface_, nullptr); decoder_iface_ = const_cast( A2DP_GetDecoderInterfaceAac(kCodecInfoAacCapability)); ASSERT_NE(decoder_iface_, nullptr); } void TearDown() override { if (a2dp_codecs_ != nullptr) { delete a2dp_codecs_; } if (encoder_iface_ != nullptr) { encoder_iface_->encoder_cleanup(); } A2DP_UnloadEncoderAac(); if (decoder_iface_ != nullptr) { decoder_iface_->decoder_cleanup(); } A2DP_UnloadDecoderAac(); AllocationTestHarness::TearDown(); } void SetCodecConfig() { uint8_t codec_info_result[AVDT_CODEC_SIZE]; btav_a2dp_codec_index_t peer_codec_index; a2dp_codecs_ = new A2dpCodecs(std::vector()); ASSERT_TRUE(a2dp_codecs_->init()); // Create the codec capability - AAC Sink memset(codec_info_result, 0, sizeof(codec_info_result)); ASSERT_TRUE(A2DP_IsSinkCodecSupportedAac(kCodecInfoAacCapability)); peer_codec_index = A2DP_SinkCodecIndex(kCodecInfoAacCapability); ASSERT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX); sink_codec_config_ = a2dp_codecs_->findSinkCodecConfig(kCodecInfoAacCapability); ASSERT_NE(sink_codec_config_, nullptr); ASSERT_TRUE(a2dp_codecs_->setSinkCodecConfig(kCodecInfoAacCapability, true, codec_info_result, true)); ASSERT_TRUE(a2dp_codecs_->setPeerSinkCodecCapabilities(kCodecInfoAacCapability)); // Compare the result codec with the local test codec info for (size_t i = 0; i < kCodecInfoAacCapability[0] + 1; i++) { ASSERT_EQ(codec_info_result[i], kCodecInfoAacCapability[i]); } ASSERT_TRUE(a2dp_codecs_->setCodecConfig(kCodecInfoAacCapability, true, codec_info_result, true)); source_codec_config_ = a2dp_codecs_->getCurrentCodecConfig(); } void InitializeEncoder(bool peer_supports_3mbps, a2dp_source_read_callback_t read_cb, a2dp_source_enqueue_callback_t enqueue_cb) { tA2DP_ENCODER_INIT_PEER_PARAMS peer_params = {true, peer_supports_3mbps, kPeerMtu}; encoder_iface_->encoder_init(&peer_params, sink_codec_config_, read_cb, enqueue_cb); } void InitializeDecoder(decoded_data_callback_t data_cb) { decoder_iface_->decoder_init(data_cb); } BT_HDR* AllocateL2capPacket(const std::vector data) const { auto packet = AllocatePacket(data.size()); std::copy(data.cbegin(), data.cend(), Data(packet)); return packet; } BT_HDR* AllocatePacket(size_t packet_length) const { BT_HDR* packet = static_cast(osi_calloc(sizeof(BT_HDR) + packet_length)); packet->len = packet_length; return packet; } A2dpCodecConfig* sink_codec_config_; A2dpCodecConfig* source_codec_config_; A2dpCodecs* a2dp_codecs_; tA2DP_ENCODER_INTERFACE* encoder_iface_; tA2DP_DECODER_INTERFACE* decoder_iface_; std::unique_ptr log_capture_; }; TEST_F(A2dpAacTest, a2dp_source_read_underflow) { log_capture_ = std::make_unique(); auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t { // underflow return 0; }; auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool { return false; }; InitializeEncoder(true, read_cb, enqueue_cb); uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); usleep(kA2dpTickUs); timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); std::promise promise; log_capture_->WaitUntilLogContains(&promise, "a2dp_aac_encode_frames: underflow"); } TEST_F(A2dpAacTest, a2dp_enqueue_cb_is_invoked) { log_capture_ = std::make_unique(); auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t { ASSERT(kAacReadSize == len); return len; }; auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool { LOG_DEBUG("%s", kEnqueueCallbackIsInvoked); osi_free(p_buf); return false; }; InitializeEncoder(true, read_cb, enqueue_cb); uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); usleep(kA2dpTickUs); timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); std::promise promise; log_capture_->WaitUntilLogContains(&promise, kEnqueueCallbackIsInvoked); } TEST_F(A2dpAacTest, decoded_data_cb_not_invoked_when_empty_packet) { auto data_cb = +[](uint8_t* p_buf, uint32_t len) { FAIL(); }; InitializeDecoder(data_cb); std::vector data; BT_HDR* packet = AllocateL2capPacket(data); decoder_iface_->decode_packet(packet); osi_free(packet); } TEST_F(A2dpAacTest, decoded_data_cb_invoked) { log_capture_ = std::make_unique(); auto data_cb = +[](uint8_t* p_buf, uint32_t len) { LOG_DEBUG("%s", kDecodedDataCallbackIsInvoked); }; InitializeDecoder(data_cb); auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t { static uint32_t counter = 0; memcpy(p_buf, wav_reader.GetSamples() + counter, len); counter += len; return len; }; auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool { packet = p_buf; LOG_DEBUG("%s", kEnqueueCallbackIsInvoked); return false; }; InitializeEncoder(true, read_cb, enqueue_cb); uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); usleep(kA2dpTickUs); timestamp_us = bluetooth::common::time_gettimeofday_us(); encoder_iface_->send_frames(timestamp_us); std::promise promise; log_capture_->WaitUntilLogContains(&promise, kEnqueueCallbackIsInvoked); decoder_iface_->decode_packet(packet); osi_free(packet); ASSERT_TRUE(log_capture_->Find(kDecodedDataCallbackIsInvoked)); } TEST_F(A2dpAacTest, set_source_codec_config_works) { uint8_t codec_info_result[AVDT_CODEC_SIZE]; ASSERT_TRUE(a2dp_codecs_->setCodecConfig(kCodecInfoAacCapability, true, codec_info_result, true)); ASSERT_TRUE(A2DP_CodecTypeEqualsAac(codec_info_result, kCodecInfoAacCapability)); ASSERT_TRUE(A2DP_CodecEqualsAac(codec_info_result, kCodecInfoAacCapability)); auto* codec_config = a2dp_codecs_->findSourceCodecConfig(kCodecInfoAacCapability); ASSERT_EQ(codec_config->name(), source_codec_config_->name()); ASSERT_EQ(codec_config->getAudioBitsPerSample(), source_codec_config_->getAudioBitsPerSample()); } TEST_F(A2dpAacTest, sink_supports_aac) { ASSERT_TRUE(A2DP_IsSinkCodecSupportedAac(kCodecInfoAacCapability)); } TEST_F(A2dpAacTest, effective_mtu_when_peer_supports_3mbps) { auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t { ASSERT(kAacReadSize == len); return len; }; auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool { osi_free(p_buf); return false; }; InitializeEncoder(true, read_cb, enqueue_cb); ASSERT_EQ(a2dp_aac_get_effective_frame_size(), kPeerMtu); } TEST_F(A2dpAacTest, effective_mtu_when_peer_does_not_support_3mbps) { auto read_cb = +[](uint8_t* p_buf, uint32_t len) -> uint32_t { ASSERT(kAacReadSize == len); return len; }; auto enqueue_cb = +[](BT_HDR* p_buf, size_t frames_n, uint32_t len) -> bool { osi_free(p_buf); return false; }; InitializeEncoder(false, read_cb, enqueue_cb); ASSERT_EQ(a2dp_aac_get_effective_frame_size(), 663 /* MAX_2MBPS_AVDTP_MTU */); } TEST_F(A2dpAacTest, debug_codec_dump) { log_capture_ = std::make_unique(); a2dp_codecs_->debug_codec_dump(2); std::promise promise; log_capture_->WaitUntilLogContains(&promise, "Current Codec: AAC"); } TEST_F(A2dpAacTest, codec_info_string) { auto codec_info = A2DP_CodecInfoString(kCodecInfoAacCapability); ASSERT_NE(codec_info.find("samp_freq: 44100"), std::string::npos); ASSERT_NE(codec_info.find("ch_mode: Stereo"), std::string::npos); } TEST_F(A2dpAacTest, get_track_bits_per_sample) { ASSERT_EQ(A2DP_GetTrackBitsPerSampleAac(kCodecInfoAacCapability), 16); } } // namespace testing } // namespace bluetooth