/* * Copyright (C) 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. */ // To run this test (as root): // 1) Build it // 2) adb push to /vendor/bin // 3) adb shell /vendor/bin/r_submix_tests #define LOG_TAG "RemoteSubmixTest" #include <memory> #include <gtest/gtest.h> #include <hardware/audio.h> #include <utils/Errors.h> #include <utils/Log.h> using namespace android; static status_t load_audio_interface(const char* if_name, audio_hw_device_t **dev) { const hw_module_t *mod; int rc; rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); if (rc) { ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); goto out; } rc = audio_hw_device_open(mod, dev); if (rc) { ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); goto out; } if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); rc = BAD_VALUE; audio_hw_device_close(*dev); goto out; } return OK; out: *dev = NULL; return rc; } class RemoteSubmixTest : public testing::Test { protected: void SetUp() override; void TearDown() override; void GenerateData(char* buffer, size_t bufferSize); void OpenInputStream( const char* address, bool mono, uint32_t sampleRate, audio_stream_in_t** streamIn); void OpenOutputStream( const char* address, bool mono, uint32_t sampleRate, audio_stream_out_t** streamOut); void ReadFromStream(audio_stream_in_t* streamIn, char* buffer, size_t bufferSize); void VerifyBufferAllZeroes(char* buffer, size_t bufferSize); void VerifyBufferNotZeroes(char* buffer, size_t bufferSize); void VerifyOutputInput( audio_stream_out_t* streamOut, size_t outBufferSize, audio_stream_in_t* streamIn, size_t inBufferSize, size_t repeats); void WriteIntoStream(audio_stream_out_t* streamOut, const char* buffer, size_t bufferSize); void WriteSomethingIntoStream(audio_stream_out_t* streamOut, size_t bufferSize, size_t repeats); audio_hw_device_t* mDev; }; void RemoteSubmixTest::SetUp() { mDev = nullptr; ASSERT_EQ(OK, load_audio_interface(AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX, &mDev)); ASSERT_NE(nullptr, mDev); } void RemoteSubmixTest::TearDown() { if (mDev != nullptr) { int status = audio_hw_device_close(mDev); mDev = nullptr; ALOGE_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status)); ASSERT_EQ(0, status); } } void RemoteSubmixTest::GenerateData(char* buffer, size_t bufferSize) { for (size_t i = 0; i < bufferSize; ++i) { buffer[i] = static_cast<char>(i & 0x7f); } } void RemoteSubmixTest::OpenInputStream( const char* address, bool mono, uint32_t sampleRate, audio_stream_in_t** streamIn) { *streamIn = nullptr; struct audio_config configIn = {}; configIn.channel_mask = mono ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO; configIn.sample_rate = sampleRate; status_t result = mDev->open_input_stream(mDev, AUDIO_IO_HANDLE_NONE, AUDIO_DEVICE_NONE, &configIn, streamIn, AUDIO_INPUT_FLAG_NONE, address, AUDIO_SOURCE_DEFAULT); ASSERT_EQ(OK, result); ASSERT_NE(nullptr, *streamIn); } void RemoteSubmixTest::OpenOutputStream( const char* address, bool mono, uint32_t sampleRate, audio_stream_out_t** streamOut) { *streamOut = nullptr; struct audio_config configOut = {}; configOut.channel_mask = mono ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO; configOut.sample_rate = sampleRate; status_t result = mDev->open_output_stream(mDev, AUDIO_IO_HANDLE_NONE, AUDIO_DEVICE_NONE, AUDIO_OUTPUT_FLAG_NONE, &configOut, streamOut, address); ASSERT_EQ(OK, result); ASSERT_NE(nullptr, *streamOut); } void RemoteSubmixTest::ReadFromStream( audio_stream_in_t* streamIn, char* buffer, size_t bufferSize) { ssize_t result = streamIn->read(streamIn, buffer, bufferSize); EXPECT_EQ(bufferSize, static_cast<size_t>(result)); } void RemoteSubmixTest::VerifyBufferAllZeroes(char* buffer, size_t bufferSize) { for (size_t i = 0; i < bufferSize; ++i) { if (buffer[i]) { ADD_FAILURE(); return; } } } void RemoteSubmixTest::VerifyBufferNotZeroes(char* buffer, size_t bufferSize) { for (size_t i = 0; i < bufferSize; ++i) { if (buffer[i]) return; } ADD_FAILURE(); } void RemoteSubmixTest::VerifyOutputInput( audio_stream_out_t* streamOut, size_t outBufferSize, audio_stream_in_t* streamIn, size_t inBufferSize, size_t repeats) { std::unique_ptr<char[]> outBuffer(new char[outBufferSize]), inBuffer(new char[inBufferSize]); GenerateData(outBuffer.get(), outBufferSize); for (size_t i = 0; i < repeats; ++i) { WriteIntoStream(streamOut, outBuffer.get(), outBufferSize); memset(inBuffer.get(), 0, inBufferSize); ReadFromStream(streamIn, inBuffer.get(), inBufferSize); if (inBufferSize == outBufferSize) { ASSERT_EQ(0, memcmp(outBuffer.get(), inBuffer.get(), inBufferSize)); } else { VerifyBufferNotZeroes(inBuffer.get(), inBufferSize); } } } void RemoteSubmixTest::WriteIntoStream( audio_stream_out_t* streamOut, const char* buffer, size_t bufferSize) { ssize_t result = streamOut->write(streamOut, buffer, bufferSize); EXPECT_EQ(bufferSize, static_cast<size_t>(result)); } void RemoteSubmixTest::WriteSomethingIntoStream( audio_stream_out_t* streamOut, size_t bufferSize, size_t repeats) { std::unique_ptr<char[]> buffer(new char[bufferSize]); GenerateData(buffer.get(), bufferSize); for (size_t i = 0; i < repeats; ++i) { WriteIntoStream(streamOut, buffer.get(), bufferSize); } } TEST_F(RemoteSubmixTest, InitSuccess) { // SetUp must finish with no assertions. } // Verifies that when no input was opened, writing into an output stream does not block. TEST_F(RemoteSubmixTest, OutputDoesNotBlockWhenNoInput) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); WriteSomethingIntoStream(streamOut, 1024, 16); mDev->close_output_stream(mDev, streamOut); } // Verifies that when input is opened but not reading, writing into an output stream does not block. // !!! Currently does not finish because requires setting a parameter from another thread !!! // TEST_F(RemoteSubmixTest, OutputDoesNotBlockWhenInputStuck) { // const char* address = "1"; // audio_stream_out_t* streamOut; // OpenOutputStream(address, true /*mono*/, 48000, &streamOut); // audio_stream_in_t* streamIn; // OpenInputStream(address, true /*mono*/, 48000, &streamIn); // WriteSomethingIntoStream(streamOut, 1024, 16); // mDev->close_input_stream(mDev, streamIn); // mDev->close_output_stream(mDev, streamOut); // } TEST_F(RemoteSubmixTest, OutputAndInput) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); audio_stream_in_t* streamIn; OpenInputStream(address, true /*mono*/, 48000, &streamIn); const size_t bufferSize = 1024; VerifyOutputInput(streamOut, bufferSize, streamIn, bufferSize, 16); mDev->close_input_stream(mDev, streamIn); mDev->close_output_stream(mDev, streamOut); } // Verifies that reading and writing into a closed stream fails gracefully. TEST_F(RemoteSubmixTest, OutputAndInputAfterClose) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); audio_stream_in_t* streamIn; OpenInputStream(address, true /*mono*/, 48000, &streamIn); mDev->close_input_stream(mDev, streamIn); mDev->close_output_stream(mDev, streamOut); const size_t bufferSize = 1024; std::unique_ptr<char[]> buffer(new char[bufferSize]); memset(buffer.get(), 0, bufferSize); ASSERT_EQ(0, streamOut->write(streamOut, buffer.get(), bufferSize)); ASSERT_EQ(static_cast<ssize_t>(bufferSize), streamIn->read(streamIn, buffer.get(), bufferSize)); VerifyBufferAllZeroes(buffer.get(), bufferSize); } TEST_F(RemoteSubmixTest, PresentationPosition) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); uint64_t frames; struct timespec timestamp; EXPECT_EQ(0, streamOut->get_presentation_position(streamOut, &frames, ×tamp)); EXPECT_EQ(uint64_t{0}, frames); uint64_t prevFrames = frames; for (size_t i = 0; i < 16; ++i) { WriteSomethingIntoStream(streamOut, 1024, 1); EXPECT_EQ(0, streamOut->get_presentation_position(streamOut, &frames, ×tamp)); EXPECT_LE(prevFrames, frames); prevFrames = frames; } mDev->close_output_stream(mDev, streamOut); } TEST_F(RemoteSubmixTest, RenderPosition) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); uint32_t frames; EXPECT_EQ(0, streamOut->get_render_position(streamOut, &frames)); EXPECT_EQ(0U, frames); uint32_t prevFrames = frames; for (size_t i = 0; i < 16; ++i) { WriteSomethingIntoStream(streamOut, 1024, 1); EXPECT_EQ(0, streamOut->get_render_position(streamOut, &frames)); EXPECT_LE(prevFrames, frames); prevFrames = frames; } mDev->close_output_stream(mDev, streamOut); } // This requires ENABLE_CHANNEL_CONVERSION to be set in the HAL module TEST_F(RemoteSubmixTest, MonoToStereoConversion) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); audio_stream_in_t* streamIn; OpenInputStream(address, false /*mono*/, 48000, &streamIn); const size_t bufferSize = 1024; VerifyOutputInput(streamOut, bufferSize, streamIn, bufferSize * 2, 16); mDev->close_input_stream(mDev, streamIn); mDev->close_output_stream(mDev, streamOut); } // This requires ENABLE_CHANNEL_CONVERSION to be set in the HAL module TEST_F(RemoteSubmixTest, StereoToMonoConversion) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, false /*mono*/, 48000, &streamOut); audio_stream_in_t* streamIn; OpenInputStream(address, true /*mono*/, 48000, &streamIn); const size_t bufferSize = 1024; VerifyOutputInput(streamOut, bufferSize * 2, streamIn, bufferSize, 16); mDev->close_input_stream(mDev, streamIn); mDev->close_output_stream(mDev, streamOut); } // This requires ENABLE_RESAMPLING to be set in the HAL module TEST_F(RemoteSubmixTest, OutputAndInputResampling) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); audio_stream_in_t* streamIn; OpenInputStream(address, true /*mono*/, 24000, &streamIn); const size_t bufferSize = 1024; VerifyOutputInput(streamOut, bufferSize * 2, streamIn, bufferSize, 16); mDev->close_input_stream(mDev, streamIn); mDev->close_output_stream(mDev, streamOut); } // This requires ENABLE_LEGACY_INPUT_OPEN to be set in the HAL module TEST_F(RemoteSubmixTest, OpenInputMultipleTimes) { const char* address = "1"; audio_stream_out_t* streamOut; OpenOutputStream(address, true /*mono*/, 48000, &streamOut); const size_t streamInCount = 3; audio_stream_in_t* streamIn[streamInCount]; for (size_t i = 0; i < streamInCount; ++i) { OpenInputStream(address, true /*mono*/, 48000, &streamIn[i]); } const size_t bufferSize = 1024; for (size_t i = 0; i < streamInCount; ++i) { VerifyOutputInput(streamOut, bufferSize, streamIn[i], bufferSize, 16); mDev->close_input_stream(mDev, streamIn[i]); } mDev->close_output_stream(mDev, streamOut); }