From 18f463eeaeb44099761c848804e03c0daaab994a Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 8 Mar 2018 18:04:27 -0800 Subject: [PATCH 1/3] Audio V4: Duplicate V2 tests Because VTS must no be modified for old API version (except bug fixes), as that could break retrocompatiblity. As a result, a full copy is made. This commit is only: $ cp -r core/2.0/vts/ core/4.0/vts $ cp -r effect/2.0/vts/ effect/4.0/vts With a V2 replaced by V4 test target name to avoid conflict. Bug: 38184704 Test: Compile Change-Id: Iddf9e7a9d42bfe84cf067a08d249a9cc0e6b7241 Signed-off-by: Kevin Rocard --- audio/core/4.0/vts/OWNERS | 5 + audio/core/4.0/vts/functional/Android.bp | 33 + .../functional/AudioPrimaryHidlHalTest.cpp | 1280 +++++++++++++++++ .../functional/ValidateAudioConfiguration.cpp | 30 + audio/effect/4.0/vts/OWNERS | 5 + audio/effect/4.0/vts/functional/Android.bp | 36 + .../ValidateAudioEffectsConfiguration.cpp | 32 + .../VtsHalAudioEffectV4_0TargetTest.cpp | 849 +++++++++++ 8 files changed, 2270 insertions(+) create mode 100644 audio/core/4.0/vts/OWNERS create mode 100644 audio/core/4.0/vts/functional/Android.bp create mode 100644 audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp create mode 100644 audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp create mode 100644 audio/effect/4.0/vts/OWNERS create mode 100644 audio/effect/4.0/vts/functional/Android.bp create mode 100644 audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp create mode 100644 audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp diff --git a/audio/core/4.0/vts/OWNERS b/audio/core/4.0/vts/OWNERS new file mode 100644 index 0000000000..8711a9ff6a --- /dev/null +++ b/audio/core/4.0/vts/OWNERS @@ -0,0 +1,5 @@ +elaurent@google.com +krocard@google.com +mnaganov@google.com +yim@google.com +zhuoyao@google.com \ No newline at end of file diff --git a/audio/core/4.0/vts/functional/Android.bp b/audio/core/4.0/vts/functional/Android.bp new file mode 100644 index 0000000000..f0327272e4 --- /dev/null +++ b/audio/core/4.0/vts/functional/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2017 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. +// + +cc_test { + name: "VtsHalAudioV4_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "AudioPrimaryHidlHalTest.cpp", + "ValidateAudioConfiguration.cpp" + ], + static_libs: [ + "android.hardware.audio.common.test.utility", + "android.hardware.audio@2.0", + "android.hardware.audio.common@2.0", + "libxml2", + ], + shared_libs: [ + "libicuuc", + ], +} diff --git a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp new file mode 100644 index 0000000000..bb1d26f928 --- /dev/null +++ b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp @@ -0,0 +1,1280 @@ +/* + * Copyright (C) 2017 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 "VtsHalAudioV2_0TargetTest" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include "utility/AssertOk.h" +#include "utility/Documentation.h" +#include "utility/EnvironmentTearDown.h" +#define AUDIO_HAL_VERSION V2_0 +#include "utility/PrettyPrintAudioTypes.h" +#include "utility/ReturnIn.h" + +using std::initializer_list; +using std::string; +using std::to_string; +using std::vector; + +using ::android::sp; +using ::android::hardware::Return; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::audio::V2_0::AudioDrain; +using ::android::hardware::audio::V2_0::DeviceAddress; +using ::android::hardware::audio::V2_0::IDevice; +using ::android::hardware::audio::V2_0::IPrimaryDevice; +using TtyMode = ::android::hardware::audio::V2_0::IPrimaryDevice::TtyMode; +using ::android::hardware::audio::V2_0::IDevicesFactory; +using ::android::hardware::audio::V2_0::IStream; +using ::android::hardware::audio::V2_0::IStreamIn; +using ::android::hardware::audio::V2_0::TimeSpec; +using ReadParameters = ::android::hardware::audio::V2_0::IStreamIn::ReadParameters; +using ReadStatus = ::android::hardware::audio::V2_0::IStreamIn::ReadStatus; +using ::android::hardware::audio::V2_0::IStreamOut; +using ::android::hardware::audio::V2_0::IStreamOutCallback; +using ::android::hardware::audio::V2_0::MmapBufferInfo; +using ::android::hardware::audio::V2_0::MmapPosition; +using ::android::hardware::audio::V2_0::ParameterValue; +using ::android::hardware::audio::V2_0::Result; +using ::android::hardware::audio::common::V2_0::AudioChannelMask; +using ::android::hardware::audio::common::V2_0::AudioConfig; +using ::android::hardware::audio::common::V2_0::AudioDevice; +using ::android::hardware::audio::common::V2_0::AudioFormat; +using ::android::hardware::audio::common::V2_0::AudioHandleConsts; +using ::android::hardware::audio::common::V2_0::AudioInputFlag; +using ::android::hardware::audio::common::V2_0::AudioIoHandle; +using ::android::hardware::audio::common::V2_0::AudioMode; +using ::android::hardware::audio::common::V2_0::AudioOffloadInfo; +using ::android::hardware::audio::common::V2_0::AudioOutputFlag; +using ::android::hardware::audio::common::V2_0::AudioSource; +using ::android::hardware::audio::common::V2_0::ThreadInfo; + +using namespace ::android::hardware::audio::common::test::utility; + +class AudioHidlTestEnvironment : public ::Environment { + public: + virtual void registerTestServices() override { registerTestService(); } +}; + +// Instance to register global tearDown +static AudioHidlTestEnvironment* environment; + +class HidlTest : public ::testing::VtsHalHidlTargetTestBase { + protected: + // Convenient member to store results + Result res; +}; + +////////////////////////////////////////////////////////////////////////////// +////////////////////// getService audio_devices_factory ////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// Test all audio devices +class AudioHidlTest : public HidlTest { + public: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base + + if (devicesFactory == nullptr) { + environment->registerTearDown([] { devicesFactory.clear(); }); + devicesFactory = ::testing::VtsHalHidlTargetTestBase::getService( + environment->getServiceName("default")); + } + ASSERT_TRUE(devicesFactory != nullptr); + } + + protected: + // Cache the devicesFactory retrieval to speed up each test by ~0.5s + static sp devicesFactory; +}; +sp AudioHidlTest::devicesFactory; + +TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) { + doc::test("test the getService (called in SetUp)"); +} + +TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) { + doc::test("test passing an invalid parameter to openDevice"); + IDevicesFactory::Result result; + sp device; + ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device(-1), returnIn(result, device))); + ASSERT_EQ(IDevicesFactory::Result::INVALID_ARGUMENTS, result); + ASSERT_TRUE(device == nullptr); +} + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// openDevice primary /////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// Test the primary device +class AudioPrimaryHidlTest : public AudioHidlTest { + public: + /** Primary HAL test are NOT thread safe. */ + void SetUp() override { + ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base + + if (device == nullptr) { + IDevicesFactory::Result result; + sp baseDevice; + ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY, + returnIn(result, baseDevice))); + ASSERT_OK(result); + ASSERT_TRUE(baseDevice != nullptr); + + environment->registerTearDown([] { device.clear(); }); + device = IPrimaryDevice::castFrom(baseDevice); + ASSERT_TRUE(device != nullptr); + } + } + + protected: + // Cache the device opening to speed up each test by ~0.5s + static sp device; +}; +sp AudioPrimaryHidlTest::device; + +TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) { + doc::test("Test the openDevice (called in SetUp)"); +} + +TEST_F(AudioPrimaryHidlTest, Init) { + doc::test("Test that the audio primary hal initialized correctly"); + ASSERT_OK(device->initCheck()); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////// {set,get}{Master,Mic}{Mute,Volume} ///////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest { + protected: + /** Test a property getter and setter. */ + template + void testAccessors(const string& propertyName, const vector& valuesToTest, + Setter setter, Getter getter, const vector& invalidValues = {}) { + Property initialValue; // Save initial value to restore it at the end + // of the test + ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue))); + ASSERT_OK(res); + + for (Property setValue : valuesToTest) { + SCOPED_TRACE("Test " + propertyName + " getter and setter for " + + testing::PrintToString(setValue)); + ASSERT_OK((device.get()->*setter)(setValue)); + Property getValue; + // Make sure the getter returns the same value just set + ASSERT_OK((device.get()->*getter)(returnIn(res, getValue))); + ASSERT_OK(res); + EXPECT_EQ(setValue, getValue); + } + + for (Property invalidValue : invalidValues) { + SCOPED_TRACE("Try to set " + propertyName + " with the invalid value " + + testing::PrintToString(invalidValue)); + EXPECT_RESULT(Result::INVALID_ARGUMENTS, (device.get()->*setter)(invalidValue)); + } + + ASSERT_OK((device.get()->*setter)(initialValue)); // restore initial value + } + + /** Test the getter and setter of an optional feature. */ + template + void testOptionalAccessors(const string& propertyName, const vector& valuesToTest, + Setter setter, Getter getter, + const vector& invalidValues = {}) { + doc::test("Test the optional " + propertyName + " getters and setter"); + { + SCOPED_TRACE("Test feature support by calling the getter"); + Property initialValue; + ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue))); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest(propertyName + " getter is not supported"); + return; + } + ASSERT_OK(res); // If it is supported it must succeed + } + // The feature is supported, test it + testAccessors(propertyName, valuesToTest, setter, getter, invalidValues); + } +}; + +using BoolAccessorPrimaryHidlTest = AccessorPrimaryHidlTest; + +TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) { + doc::test("Check that the mic can be muted and unmuted"); + testAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute); + // TODO: check that the mic is really muted (all sample are 0) +} + +TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) { + doc::test( + "If master mute is supported, try to mute and unmute the master " + "output"); + testOptionalAccessors("master mute", {true, false, true}, &IDevice::setMasterMute, + &IDevice::getMasterMute); + // TODO: check that the master volume is really muted +} + +using FloatAccessorPrimaryHidlTest = AccessorPrimaryHidlTest; +TEST_F(FloatAccessorPrimaryHidlTest, MasterVolumeTest) { + doc::test("Test the master volume if supported"); + testOptionalAccessors( + "master volume", {0, 0.5, 1}, &IDevice::setMasterVolume, &IDevice::getMasterVolume, + {-0.1, 1.1, NAN, INFINITY, -INFINITY, 1 + std::numeric_limits::epsilon()}); + // TODO: check that the master volume is really changed +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// AudioPatches //////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class AudioPatchPrimaryHidlTest : public AudioPrimaryHidlTest { + protected: + bool areAudioPatchesSupported() { + auto result = device->supportsAudioPatches(); + EXPECT_IS_OK(result); + return result; + } +}; + +TEST_F(AudioPatchPrimaryHidlTest, AudioPatches) { + doc::test("Test if audio patches are supported"); + if (!areAudioPatchesSupported()) { + doc::partialTest("Audio patches are not supported"); + return; + } + // TODO: test audio patches +} + +////////////////////////////////////////////////////////////////////////////// +//////////////// Required and recommended audio format support /////////////// +// From: +// https://source.android.com/compatibility/android-cdd.html#5_4_audio_recording +// From: +// https://source.android.com/compatibility/android-cdd.html#5_5_audio_playback +/////////// TODO: move to the beginning of the file for easier update //////// +////////////////////////////////////////////////////////////////////////////// + +class AudioConfigPrimaryTest : public AudioPatchPrimaryHidlTest { + public: + // Cache result ? + static const vector getRequiredSupportPlaybackAudioConfig() { + return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO}, + {8000, 11025, 16000, 22050, 32000, 44100}, + {AudioFormat::PCM_16_BIT}); + } + + static const vector getRecommendedSupportPlaybackAudioConfig() { + return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO}, + {24000, 48000}, {AudioFormat::PCM_16_BIT}); + } + + static const vector getSupportedPlaybackAudioConfig() { + // TODO: retrieve audio config supported by the platform + // as declared in the policy configuration + return {}; + } + + static const vector getRequiredSupportCaptureAudioConfig() { + return combineAudioConfig({AudioChannelMask::IN_MONO}, {8000, 11025, 16000, 44100}, + {AudioFormat::PCM_16_BIT}); + } + static const vector getRecommendedSupportCaptureAudioConfig() { + return combineAudioConfig({AudioChannelMask::IN_STEREO}, {22050, 48000}, + {AudioFormat::PCM_16_BIT}); + } + static const vector getSupportedCaptureAudioConfig() { + // TODO: retrieve audio config supported by the platform + // as declared in the policy configuration + return {}; + } + + private: + static const vector combineAudioConfig(vector channelMasks, + vector sampleRates, + vector formats) { + vector configs; + for (auto channelMask : channelMasks) { + for (auto sampleRate : sampleRates) { + for (auto format : formats) { + AudioConfig config{}; + // leave offloadInfo to 0 + config.channelMask = channelMask; + config.sampleRateHz = sampleRate; + config.format = format; + // FIXME: leave frameCount to 0 ? + configs.push_back(config); + } + } + } + return configs; + } +}; + +/** Generate a test name based on an audio config. + * + * As the only parameter changing are channel mask and sample rate, + * only print those ones in the test name. + */ +static string generateTestName(const testing::TestParamInfo& info) { + const AudioConfig& config = info.param; + return to_string(info.index) + "__" + to_string(config.sampleRateHz) + "_" + + // "MONO" is more clear than "FRONT_LEFT" + ((config.channelMask == AudioChannelMask::OUT_MONO || + config.channelMask == AudioChannelMask::IN_MONO) + ? "MONO" + : toString(config.channelMask)); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////// getInputBufferSize ///////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// FIXME: execute input test only if platform declares +// android.hardware.microphone +// how to get this value ? is it a property ??? + +class AudioCaptureConfigPrimaryTest : public AudioConfigPrimaryTest, + public ::testing::WithParamInterface { + protected: + void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) { + uint64_t bufferSize; + ASSERT_OK(device->getInputBufferSize(audioConfig, returnIn(res, bufferSize))); + + switch (res) { + case Result::INVALID_ARGUMENTS: + EXPECT_FALSE(supportRequired); + break; + case Result::OK: + // Check that the buffer is of a sane size + // For now only that it is > 0 + EXPECT_GT(bufferSize, uint64_t(0)); + break; + default: + FAIL() << "Invalid return status: " << ::testing::PrintToString(res); + } + } +}; + +// Test that the required capture config and those declared in the policy are +// indeed supported +class RequiredInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {}; +TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) { + doc::test( + "Input buffer size must be retrievable for a format with required " + "support."); + inputBufferSizeTest(GetParam(), true); +} +INSTANTIATE_TEST_CASE_P( + RequiredInputBufferSize, RequiredInputBufferSizeTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()), + &generateTestName); +INSTANTIATE_TEST_CASE_P( + SupportedInputBufferSize, RequiredInputBufferSizeTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()), + &generateTestName); + +// Test that the recommended capture config are supported or lead to a +// INVALID_ARGUMENTS return +class OptionalInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {}; +TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) { + doc::test( + "Input buffer size should be retrievable for a format with recommended " + "support."); + inputBufferSizeTest(GetParam(), false); +} +INSTANTIATE_TEST_CASE_P( + RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()), + &generateTestName); + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// setScreenState /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_F(AudioPrimaryHidlTest, setScreenState) { + doc::test("Check that the hal can receive the screen state"); + for (bool turnedOn : {false, true, true, false, false}) { + auto ret = device->setScreenState(turnedOn); + ASSERT_IS_OK(ret); + Result result = ret; + auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED}; + ASSERT_RESULT(okOrNotSupported, result); + } +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////// {get,set}Parameters ///////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_F(AudioPrimaryHidlTest, getParameters) { + doc::test("Check that the hal can set and get parameters"); + hidl_vec keys; + hidl_vec values; + ASSERT_OK(device->getParameters(keys, returnIn(res, values))); + ASSERT_OK(device->setParameters(values)); + values.resize(0); + ASSERT_OK(device->setParameters(values)); +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// debugDebug ////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +static void testDebugDump(DebugDump debugDump) { + // File descriptors to our pipe. fds[0] corresponds to the read end and + // fds[1] to the write end. + int fds[2]; + ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno; + + // Make sure that the pipe is at least 1 MB in size. The test process runs + // in su domain, so it should be safe to make this call. + fcntl(fds[0], F_SETPIPE_SZ, 1 << 20); + + // Wrap the temporary file file descriptor in a native handle + auto* nativeHandle = native_handle_create(1, 0); + ASSERT_NE(nullptr, nativeHandle); + nativeHandle->data[0] = fds[1]; + + // Wrap this native handle in a hidl handle + hidl_handle handle; + handle.setTo(nativeHandle, false /*take ownership*/); + + ASSERT_OK(debugDump(handle)); + + // Check that at least one bit was written by the hal + // TODO: debugDump does not return a Result. + // This mean that the hal can not report that it not implementing the + // function. + char buff; + if (read(fds[0], &buff, 1) != 1) { + doc::note("debugDump does not seem implemented"); + } + EXPECT_EQ(0, close(fds[0])) << errno; + EXPECT_EQ(0, close(fds[1])) << errno; +} + +TEST_F(AudioPrimaryHidlTest, DebugDump) { + doc::test("Check that the hal can dump its state without error"); + testDebugDump([](const auto& handle) { return device->debugDump(handle); }); +} + +TEST_F(AudioPrimaryHidlTest, DebugDumpInvalidArguments) { + doc::test("Check that the hal dump doesn't crash on invalid arguments"); + ASSERT_OK(device->debugDump(hidl_handle())); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////// open{Output,Input}Stream ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +template +class OpenStreamTest : public AudioConfigPrimaryTest, + public ::testing::WithParamInterface { + protected: + template + void testOpen(Open openStream, const AudioConfig& config) { + // FIXME: Open a stream without an IOHandle + // This is not required to be accepted by hal implementations + AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE; + AudioConfig suggestedConfig{}; + ASSERT_OK(openStream(ioHandle, config, returnIn(res, stream, suggestedConfig))); + + // TODO: only allow failure for RecommendedPlaybackAudioConfig + switch (res) { + case Result::OK: + ASSERT_TRUE(stream != nullptr); + audioConfig = config; + break; + case Result::INVALID_ARGUMENTS: + ASSERT_TRUE(stream == nullptr); + AudioConfig suggestedConfigRetry; + // Could not open stream with config, try again with the + // suggested one + ASSERT_OK(openStream(ioHandle, suggestedConfig, + returnIn(res, stream, suggestedConfigRetry))); + // This time it must succeed + ASSERT_OK(res); + ASSERT_TRUE(stream != nullptr); + audioConfig = suggestedConfig; + break; + default: + FAIL() << "Invalid return status: " << ::testing::PrintToString(res); + } + open = true; + } + + Return closeStream() { + open = false; + return stream->close(); + } + + private: + void TearDown() override { + if (open) { + ASSERT_OK(stream->close()); + } + } + + protected: + AudioConfig audioConfig; + DeviceAddress address = {}; + sp stream; + bool open = false; +}; + +////////////////////////////// openOutputStream ////////////////////////////// + +class OutputStreamTest : public OpenStreamTest { + virtual void SetUp() override { + ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base + address.device = AudioDevice::OUT_DEFAULT; + const AudioConfig& config = GetParam(); + AudioOutputFlag flags = AudioOutputFlag::NONE; // TODO: test all flag combination + testOpen( + [&](AudioIoHandle handle, AudioConfig config, auto cb) { + return device->openOutputStream(handle, address, config, flags, cb); + }, + config); + } +}; +TEST_P(OutputStreamTest, OpenOutputStreamTest) { + doc::test( + "Check that output streams can be open with the required and " + "recommended config"); + // Open done in SetUp +} +INSTANTIATE_TEST_CASE_P( + RequiredOutputStreamConfigSupport, OutputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportPlaybackAudioConfig()), + &generateTestName); +INSTANTIATE_TEST_CASE_P( + SupportedOutputStreamConfig, OutputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()), + &generateTestName); + +INSTANTIATE_TEST_CASE_P( + RecommendedOutputStreamConfigSupport, OutputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()), + &generateTestName); + +////////////////////////////// openInputStream ////////////////////////////// + +class InputStreamTest : public OpenStreamTest { + virtual void SetUp() override { + ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base + address.device = AudioDevice::IN_DEFAULT; + const AudioConfig& config = GetParam(); + AudioInputFlag flags = AudioInputFlag::NONE; // TODO: test all flag combination + AudioSource source = AudioSource::DEFAULT; // TODO: test all flag combination + testOpen( + [&](AudioIoHandle handle, AudioConfig config, auto cb) { + return device->openInputStream(handle, address, config, flags, source, cb); + }, + config); + } +}; + +TEST_P(InputStreamTest, OpenInputStreamTest) { + doc::test( + "Check that input streams can be open with the required and " + "recommended config"); + // Open done in setup +} +INSTANTIATE_TEST_CASE_P( + RequiredInputStreamConfigSupport, InputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()), + &generateTestName); +INSTANTIATE_TEST_CASE_P( + SupportedInputStreamConfig, InputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()), + &generateTestName); + +INSTANTIATE_TEST_CASE_P( + RecommendedInputStreamConfigSupport, InputStreamTest, + ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()), + &generateTestName); + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// IStream getters /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +/** Unpack the provided result. + * If the result is not OK, register a failure and return an undefined value. */ +template +static R extract(Return ret) { + if (!ret.isOk()) { + EXPECT_IS_OK(ret); + return R{}; + } + return ret; +} + +/* Could not find a way to write a test for two parametrized class fixure + * thus use this macro do duplicate tests for Input and Output stream */ +#define TEST_IO_STREAM(test_name, documentation, code) \ + TEST_P(InputStreamTest, test_name) { \ + doc::test(documentation); \ + code; \ + } \ + TEST_P(OutputStreamTest, test_name) { \ + doc::test(documentation); \ + code; \ + } + +TEST_IO_STREAM(GetFrameCount, "Check that the stream frame count == the one it was opened with", + ASSERT_EQ(audioConfig.frameCount, extract(stream->getFrameCount()))) + +TEST_IO_STREAM(GetSampleRate, "Check that the stream sample rate == the one it was opened with", + ASSERT_EQ(audioConfig.sampleRateHz, extract(stream->getSampleRate()))) + +TEST_IO_STREAM(GetChannelMask, "Check that the stream channel mask == the one it was opened with", + ASSERT_EQ(audioConfig.channelMask, extract(stream->getChannelMask()))) + +TEST_IO_STREAM(GetFormat, "Check that the stream format == the one it was opened with", + ASSERT_EQ(audioConfig.format, extract(stream->getFormat()))) + +// TODO: for now only check that the framesize is not incoherent +TEST_IO_STREAM(GetFrameSize, "Check that the stream frame size == the one it was opened with", + ASSERT_GT(extract(stream->getFrameSize()), 0U)) + +TEST_IO_STREAM(GetBufferSize, "Check that the stream buffer size== the one it was opened with", + ASSERT_GE(extract(stream->getBufferSize()), extract(stream->getFrameSize()))); + +template +static void testCapabilityGetter(const string& name, IStream* stream, + CapabilityGetter capablityGetter, + Return (IStream::*getter)(), + Return (IStream::*setter)(Property), + bool currentMustBeSupported = true) { + hidl_vec capabilities; + ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities))); + if (capabilities.size() == 0) { + // The default hal should probably return a NOT_SUPPORTED if the hal + // does not expose + // capability retrieval. For now it returns an empty list if not + // implemented + doc::partialTest(name + " is not supported"); + return; + }; + + if (currentMustBeSupported) { + Property currentValue = extract((stream->*getter)()); + EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue), + capabilities.end()) + << "current " << name << " is not in the list of the supported ones " + << toString(capabilities); + } + + // Check that all declared supported values are indeed supported + for (auto capability : capabilities) { + auto ret = (stream->*setter)(capability); + ASSERT_TRUE(ret.isOk()); + if (ret == Result::NOT_SUPPORTED) { + doc::partialTest("Setter is not supported"); + return; + } + ASSERT_OK(ret); + ASSERT_EQ(capability, extract((stream->*getter)())); + } +} + +TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declared as supported", + testCapabilityGetter("getSupportedSampleRate", stream.get(), + &IStream::getSupportedSampleRates, &IStream::getSampleRate, + &IStream::setSampleRate, + // getSupportedSampleRate returns the native sampling rates, + // (the sampling rates that can be played without resampling) + // but other sampling rates can be supported by the HAL. + false)) + +TEST_IO_STREAM(SupportedChannelMask, "Check that the stream channel mask is declared as supported", + testCapabilityGetter("getSupportedChannelMask", stream.get(), + &IStream::getSupportedChannelMasks, &IStream::getChannelMask, + &IStream::setChannelMask)) + +TEST_IO_STREAM(SupportedFormat, "Check that the stream format is declared as supported", + testCapabilityGetter("getSupportedFormat", stream.get(), + &IStream::getSupportedFormats, &IStream::getFormat, + &IStream::setFormat)) + +static void testGetDevice(IStream* stream, AudioDevice expectedDevice) { + // Unfortunately the interface does not allow the implementation to return + // NOT_SUPPORTED + // Thus allow NONE as signaling that the call is not supported. + auto ret = stream->getDevice(); + ASSERT_IS_OK(ret); + AudioDevice device = ret; + ASSERT_TRUE(device == expectedDevice || device == AudioDevice::NONE) + << "Expected: " << ::testing::PrintToString(expectedDevice) + << "\n Actual: " << ::testing::PrintToString(device); +} + +TEST_IO_STREAM(GetDevice, "Check that the stream device == the one it was opened with", + areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported") + : testGetDevice(stream.get(), address.device)) + +static void testSetDevice(IStream* stream, const DeviceAddress& address) { + DeviceAddress otherAddress = address; + otherAddress.device = (address.device & AudioDevice::BIT_IN) == 0 ? AudioDevice::OUT_SPEAKER + : AudioDevice::IN_BUILTIN_MIC; + EXPECT_OK(stream->setDevice(otherAddress)); + + ASSERT_OK(stream->setDevice(address)); // Go back to the original value +} + +TEST_IO_STREAM(SetDevice, "Check that the stream can be rerouted to SPEAKER or BUILTIN_MIC", + areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported") + : testSetDevice(stream.get(), address)) + +static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) { + uint32_t sampleRateHz; + AudioChannelMask mask; + AudioFormat format; + + stream->getAudioProperties(returnIn(sampleRateHz, mask, format)); + + // FIXME: the qcom hal it does not currently negotiate the sampleRate & + // channel mask + EXPECT_EQ(expectedConfig.sampleRateHz, sampleRateHz); + EXPECT_EQ(expectedConfig.channelMask, mask); + EXPECT_EQ(expectedConfig.format, format); +} + +TEST_IO_STREAM(GetAudioProperties, + "Check that the stream audio properties == the ones it was opened with", + testGetAudioProperties(stream.get(), audioConfig)) + +static void testConnectedState(IStream* stream) { + DeviceAddress address = {}; + using AD = AudioDevice; + for (auto device : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) { + address.device = device; + + ASSERT_OK(stream->setConnectedState(address, true)); + ASSERT_OK(stream->setConnectedState(address, false)); + } +} +TEST_IO_STREAM(SetConnectedState, + "Check that the stream can be notified of device connection and " + "deconnection", + testConnectedState(stream.get())) + +static auto invalidArgsOrNotSupportedOrOK = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED, + Result::OK}; +TEST_IO_STREAM(SetHwAvSync, "Try to set hardware sync to an invalid value", + ASSERT_RESULT(invalidArgsOrNotSupportedOrOK, stream->setHwAvSync(666))) + +TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", ASSERT_IS_OK(device->getHwAvSync())); + +static void checkGetNoParameter(IStream* stream, hidl_vec keys, + initializer_list expectedResults) { + hidl_vec parameters; + Result res; + ASSERT_OK(stream->getParameters(keys, returnIn(res, parameters))); + ASSERT_RESULT(expectedResults, res); + if (res == Result::OK) { + for (auto& parameter : parameters) { + ASSERT_EQ(0U, parameter.value.size()) << toString(parameter); + } + } +} + +/* Get/Set parameter is intended to be an opaque channel between vendors app and + * their HALs. + * Thus can not be meaningfully tested. + */ +TEST_IO_STREAM(getEmptySetParameter, "Retrieve the values of an empty set", + checkGetNoParameter(stream.get(), {} /* keys */, {Result::OK})) + +TEST_IO_STREAM(getNonExistingParameter, "Retrieve the values of an non existing parameter", + checkGetNoParameter(stream.get(), {"Non existing key"} /* keys */, + {Result::NOT_SUPPORTED})) + +TEST_IO_STREAM(setEmptySetParameter, "Set the values of an empty set of parameters", + ASSERT_RESULT(Result::OK, stream->setParameters({}))) + +TEST_IO_STREAM(setNonExistingParameter, "Set the values of an non existing parameter", + // Unfortunately, the set_parameter legacy interface did not return any + // error code when a key is not supported. + // To allow implementation to just wrapped the legacy one, consider OK as a + // valid result for setting a non existing parameter. + ASSERT_RESULT(invalidArgsOrNotSupportedOrOK, + stream->setParameters({{"non existing key", "0"}}))) + +TEST_IO_STREAM(DebugDump, "Check that a stream can dump its state without error", + testDebugDump([this](const auto& handle) { return stream->debugDump(handle); })) + +TEST_IO_STREAM(DebugDumpInvalidArguments, + "Check that the stream dump doesn't crash on invalid arguments", + ASSERT_OK(stream->debugDump(hidl_handle()))) + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// addRemoveEffect /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_IO_STREAM(AddNonExistingEffect, "Adding a non existing effect should fail", + ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->addEffect(666))) +TEST_IO_STREAM(RemoveNonExistingEffect, "Removing a non existing effect should fail", + ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->removeEffect(666))) + +// TODO: positive tests + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Control //////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_IO_STREAM(standby, "Make sure the stream can be put in stanby", + ASSERT_OK(stream->standby())) // can not fail + +static constexpr auto invalidStateOrNotSupported = {Result::INVALID_STATE, Result::NOT_SUPPORTED}; + +TEST_IO_STREAM(startNoMmap, "Starting a mmaped stream before mapping it should fail", + ASSERT_RESULT(invalidStateOrNotSupported, stream->start())) + +TEST_IO_STREAM(stopNoMmap, "Stopping a mmaped stream before mapping it should fail", + ASSERT_RESULT(invalidStateOrNotSupported, stream->stop())) + +TEST_IO_STREAM(getMmapPositionNoMmap, "Get a stream Mmap position before mapping it should fail", + ASSERT_RESULT(invalidStateOrNotSupported, stream->stop())) + +TEST_IO_STREAM(close, "Make sure a stream can be closed", ASSERT_OK(closeStream())) +TEST_IO_STREAM(closeTwice, "Make sure a stream can not be closed twice", ASSERT_OK(closeStream()); + ASSERT_RESULT(Result::INVALID_STATE, closeStream())) + +static auto invalidArgsOrNotSupported = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED}; +static void testCreateTooBigMmapBuffer(IStream* stream) { + MmapBufferInfo info; + Result res; + // Assume that int max is a value too big to be allocated + // This is true currently with a 32bit media server, but might not when it + // will run in 64 bit + auto minSizeFrames = std::numeric_limits::max(); + ASSERT_OK(stream->createMmapBuffer(minSizeFrames, returnIn(res, info))); + ASSERT_RESULT(invalidArgsOrNotSupported, res); +} + +TEST_IO_STREAM(CreateTooBigMmapBuffer, "Create mmap buffer too big should fail", + testCreateTooBigMmapBuffer(stream.get())) + +static void testGetMmapPositionOfNonMmapedStream(IStream* stream) { + Result res; + MmapPosition position; + ASSERT_OK(stream->getMmapPosition(returnIn(res, position))); + ASSERT_RESULT(invalidArgsOrNotSupported, res); +} + +TEST_IO_STREAM(GetMmapPositionOfNonMmapedStream, + "Retrieving the mmap position of a non mmaped stream should fail", + testGetMmapPositionOfNonMmapedStream(stream.get())) + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// StreamIn /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_P(InputStreamTest, GetAudioSource) { + doc::test("Retrieving the audio source of an input stream should always succeed"); + AudioSource source; + ASSERT_OK(stream->getAudioSource(returnIn(res, source))); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest("getAudioSource is not supported"); + return; + } + ASSERT_OK(res); + ASSERT_EQ(AudioSource::DEFAULT, source); +} + +static void testUnitaryGain(std::function(float)> setGain) { + for (float value : (float[]){-INFINITY, -1.0, 1.0 + std::numeric_limits::epsilon(), 2.0, + INFINITY, NAN}) { + EXPECT_RESULT(Result::INVALID_ARGUMENTS, setGain(value)) << "value=" << value; + } + // Do not consider -0.0 as an invalid value as it is == with 0.0 + for (float value : {-0.0, 0.0, 0.01, 0.5, 0.09, 1.0 /* Restore volume*/}) { + EXPECT_OK(setGain(value)) << "value=" << value; + } +} + +static void testOptionalUnitaryGain(std::function(float)> setGain, + string debugName) { + auto result = setGain(1); + ASSERT_IS_OK(result); + if (result == Result::NOT_SUPPORTED) { + doc::partialTest(debugName + " is not supported"); + return; + } + testUnitaryGain(setGain); +} + +TEST_P(InputStreamTest, SetGain) { + doc::test("The gain of an input stream should only be set between [0,1]"); + testOptionalUnitaryGain([this](float volume) { return stream->setGain(volume); }, + "InputStream::setGain"); +} + +static void testPrepareForReading(IStreamIn* stream, uint32_t frameSize, uint32_t framesCount) { + Result res; + // Ignore output parameters as the call should fail + ASSERT_OK(stream->prepareForReading(frameSize, framesCount, + [&res](auto r, auto&, auto&, auto&, auto&) { res = r; })); + EXPECT_RESULT(Result::INVALID_ARGUMENTS, res); +} + +TEST_P(InputStreamTest, PrepareForReadingWithZeroBuffer) { + doc::test("Preparing a stream for reading with a 0 sized buffer should fail"); + testPrepareForReading(stream.get(), 0, 0); +} + +TEST_P(InputStreamTest, PrepareForReadingWithHugeBuffer) { + doc::test("Preparing a stream for reading with a 2^32 sized buffer should fail"); + testPrepareForReading(stream.get(), 1, std::numeric_limits::max()); +} + +TEST_P(InputStreamTest, PrepareForReadingCheckOverflow) { + doc::test( + "Preparing a stream for reading with a overflowing sized buffer should " + "fail"); + auto uintMax = std::numeric_limits::max(); + testPrepareForReading(stream.get(), uintMax, uintMax); +} + +TEST_P(InputStreamTest, GetInputFramesLost) { + doc::test("The number of frames lost on a never started stream should be 0"); + auto ret = stream->getInputFramesLost(); + ASSERT_IS_OK(ret); + uint32_t framesLost{ret}; + ASSERT_EQ(0U, framesLost); +} + +TEST_P(InputStreamTest, getCapturePosition) { + doc::test( + "The capture position of a non prepared stream should not be " + "retrievable"); + uint64_t frames; + uint64_t time; + ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, time))); + ASSERT_RESULT(invalidStateOrNotSupported, res); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// StreamIn /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_P(OutputStreamTest, getLatency) { + doc::test("Make sure latency is over 0"); + auto result = stream->getLatency(); + ASSERT_IS_OK(result); + ASSERT_GT(result, 0U); +} + +TEST_P(OutputStreamTest, setVolume) { + doc::test("Try to set the output volume"); + testOptionalUnitaryGain([this](float volume) { return stream->setVolume(volume, volume); }, + "setVolume"); +} + +static void testPrepareForWriting(IStreamOut* stream, uint32_t frameSize, uint32_t framesCount) { + Result res; + // Ignore output parameters as the call should fail + ASSERT_OK(stream->prepareForWriting(frameSize, framesCount, + [&res](auto r, auto&, auto&, auto&, auto&) { res = r; })); + EXPECT_RESULT(Result::INVALID_ARGUMENTS, res); +} + +TEST_P(OutputStreamTest, PrepareForWriteWithZeroBuffer) { + doc::test("Preparing a stream for writing with a 0 sized buffer should fail"); + testPrepareForWriting(stream.get(), 0, 0); +} + +TEST_P(OutputStreamTest, PrepareForWriteWithHugeBuffer) { + doc::test("Preparing a stream for writing with a 2^32 sized buffer should fail"); + testPrepareForWriting(stream.get(), 1, std::numeric_limits::max()); +} + +TEST_P(OutputStreamTest, PrepareForWritingCheckOverflow) { + doc::test( + "Preparing a stream for writing with a overflowing sized buffer should " + "fail"); + auto uintMax = std::numeric_limits::max(); + testPrepareForWriting(stream.get(), uintMax, uintMax); +} + +struct Capability { + Capability(IStreamOut* stream) { + EXPECT_OK(stream->supportsPauseAndResume(returnIn(pause, resume))); + auto ret = stream->supportsDrain(); + EXPECT_IS_OK(ret); + if (ret.isOk()) { + drain = ret; + } + } + bool pause = false; + bool resume = false; + bool drain = false; +}; + +TEST_P(OutputStreamTest, SupportsPauseAndResumeAndDrain) { + doc::test("Implementation must expose pause, resume and drain capabilities"); + Capability(stream.get()); +} + +template +static void checkInvalidStateOr0(Result res, Value value) { + switch (res) { + case Result::INVALID_STATE: + break; + case Result::OK: + ASSERT_EQ(0U, value); + break; + default: + FAIL() << "Unexpected result " << toString(res); + } +} + +TEST_P(OutputStreamTest, GetRenderPosition) { + doc::test("A new stream render position should be 0 or INVALID_STATE"); + uint32_t dspFrames; + ASSERT_OK(stream->getRenderPosition(returnIn(res, dspFrames))); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest("getRenderPosition is not supported"); + return; + } + checkInvalidStateOr0(res, dspFrames); +} + +TEST_P(OutputStreamTest, GetNextWriteTimestamp) { + doc::test("A new stream next write timestamp should be 0 or INVALID_STATE"); + uint64_t timestampUs; + ASSERT_OK(stream->getNextWriteTimestamp(returnIn(res, timestampUs))); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest("getNextWriteTimestamp is not supported"); + return; + } + checkInvalidStateOr0(res, timestampUs); +} + +/** Stub implementation of out stream callback. */ +class MockOutCallbacks : public IStreamOutCallback { + Return onWriteReady() override { return {}; } + Return onDrainReady() override { return {}; } + Return onError() override { return {}; } +}; + +static bool isAsyncModeSupported(IStreamOut* stream) { + auto res = stream->setCallback(new MockOutCallbacks); + stream->clearCallback(); // try to restore the no callback state, ignore + // any error + auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED}; + EXPECT_RESULT(okOrNotSupported, res); + return res.isOk() ? res == Result::OK : false; +} + +TEST_P(OutputStreamTest, SetCallback) { + doc::test( + "If supported, registering callback for async operation should never " + "fail"); + if (!isAsyncModeSupported(stream.get())) { + doc::partialTest("The stream does not support async operations"); + return; + } + ASSERT_OK(stream->setCallback(new MockOutCallbacks)); + ASSERT_OK(stream->setCallback(new MockOutCallbacks)); +} + +TEST_P(OutputStreamTest, clearCallback) { + doc::test( + "If supported, clearing a callback to go back to sync operation should " + "not fail"); + if (!isAsyncModeSupported(stream.get())) { + doc::partialTest("The stream does not support async operations"); + return; + } + // TODO: Clarify if clearing a non existing callback should fail + ASSERT_OK(stream->setCallback(new MockOutCallbacks)); + ASSERT_OK(stream->clearCallback()); +} + +TEST_P(OutputStreamTest, Resume) { + doc::test( + "If supported, a stream should fail to resume if not previously " + "paused"); + if (!Capability(stream.get()).resume) { + doc::partialTest("The output stream does not support resume"); + return; + } + ASSERT_RESULT(Result::INVALID_STATE, stream->resume()); +} + +TEST_P(OutputStreamTest, Pause) { + doc::test( + "If supported, a stream should fail to pause if not previously " + "started"); + if (!Capability(stream.get()).pause) { + doc::partialTest("The output stream does not support pause"); + return; + } + ASSERT_RESULT(Result::INVALID_STATE, stream->resume()); +} + +static void testDrain(IStreamOut* stream, AudioDrain type) { + if (!Capability(stream).drain) { + doc::partialTest("The output stream does not support drain"); + return; + } + ASSERT_RESULT(Result::OK, stream->drain(type)); +} + +TEST_P(OutputStreamTest, DrainAll) { + doc::test("If supported, a stream should always succeed to drain"); + testDrain(stream.get(), AudioDrain::ALL); +} + +TEST_P(OutputStreamTest, DrainEarlyNotify) { + doc::test("If supported, a stream should always succeed to drain"); + testDrain(stream.get(), AudioDrain::EARLY_NOTIFY); +} + +TEST_P(OutputStreamTest, FlushStop) { + doc::test("If supported, a stream should always succeed to flush"); + auto ret = stream->flush(); + ASSERT_IS_OK(ret); + if (ret == Result::NOT_SUPPORTED) { + doc::partialTest("Flush is not supported"); + return; + } + ASSERT_OK(ret); +} + +TEST_P(OutputStreamTest, GetPresentationPositionStop) { + doc::test( + "If supported, a stream should always succeed to retrieve the " + "presentation position"); + uint64_t frames; + TimeSpec mesureTS; + ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, mesureTS))); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest("getpresentationPosition is not supported"); + return; + } + ASSERT_EQ(0U, frames); + + if (mesureTS.tvNSec == 0 && mesureTS.tvSec == 0) { + // As the stream has never written a frame yet, + // the timestamp does not really have a meaning, allow to return 0 + return; + } + + // Make sure the return measure is not more than 1s old. + struct timespec currentTS; + ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, ¤tTS)) << errno; + + auto toMicroSec = [](uint64_t sec, auto nsec) { return sec * 1e+6 + nsec / 1e+3; }; + auto currentTime = toMicroSec(currentTS.tv_sec, currentTS.tv_nsec); + auto mesureTime = toMicroSec(mesureTS.tvSec, mesureTS.tvNSec); + ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, mesureTime); +} + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// PrimaryDevice //////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +TEST_F(AudioPrimaryHidlTest, setVoiceVolume) { + doc::test("Make sure setVoiceVolume only succeed if volume is in [0,1]"); + testUnitaryGain([](float volume) { return device->setVoiceVolume(volume); }); +} + +TEST_F(AudioPrimaryHidlTest, setMode) { + doc::test( + "Make sure setMode always succeeds if mode is valid " + "and fails otherwise"); + // Test Invalid values + for (AudioMode mode : {AudioMode::INVALID, AudioMode::CURRENT, AudioMode::CNT}) { + SCOPED_TRACE("mode=" + toString(mode)); + ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(mode)); + } + // Test valid values + for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE, + AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) { + SCOPED_TRACE("mode=" + toString(mode)); + ASSERT_OK(device->setMode(mode)); + } +} + +TEST_F(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) { + doc::test("Query and set the BT SCO NR&EC state"); + testOptionalAccessors("BtScoNrecEnabled", {true, false, true}, + &IPrimaryDevice::setBtScoNrecEnabled, + &IPrimaryDevice::getBtScoNrecEnabled); +} + +TEST_F(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) { + doc::test("Query and set the SCO whideband state"); + testOptionalAccessors("BtScoWideband", {true, false, true}, + &IPrimaryDevice::setBtScoWidebandEnabled, + &IPrimaryDevice::getBtScoWidebandEnabled); +} + +using TtyModeAccessorPrimaryHidlTest = AccessorPrimaryHidlTest; +TEST_F(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) { + doc::test("Query and set the TTY mode state"); + testOptionalAccessors("TTY mode", {TtyMode::OFF, TtyMode::HCO, TtyMode::VCO, TtyMode::FULL}, + &IPrimaryDevice::setTtyMode, &IPrimaryDevice::getTtyMode); +} + +TEST_F(BoolAccessorPrimaryHidlTest, setGetHac) { + doc::test("Query and set the HAC state"); + testOptionalAccessors("HAC", {true, false, true}, &IPrimaryDevice::setHacEnabled, + &IPrimaryDevice::getHacEnabled); +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////// Clean caches on global tear down //////////////////////// +////////////////////////////////////////////////////////////////////////////// + +int main(int argc, char** argv) { + environment = new AudioHidlTestEnvironment; + ::testing::AddGlobalTestEnvironment(environment); + ::testing::InitGoogleTest(&argc, argv); + environment->init(&argc, argv); + int status = RUN_ALL_TESTS(); + return status; +} diff --git a/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp b/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp new file mode 100644 index 0000000000..bef0e8276c --- /dev/null +++ b/audio/core/4.0/vts/functional/ValidateAudioConfiguration.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 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 +#include + +#include "utility/ValidateXml.h" + +TEST(CheckConfig, audioPolicyConfigurationValidation) { + RecordProperty("description", + "Verify that the audio policy configuration file " + "is valid according to the schema"); + + std::vector locations = {"/odm/etc", "/vendor/etc", "/system/etc"}; + EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS("audio_policy_configuration.xml", locations, + "/data/local/tmp/audio_policy_configuration.xsd"); +} diff --git a/audio/effect/4.0/vts/OWNERS b/audio/effect/4.0/vts/OWNERS new file mode 100644 index 0000000000..8711a9ff6a --- /dev/null +++ b/audio/effect/4.0/vts/OWNERS @@ -0,0 +1,5 @@ +elaurent@google.com +krocard@google.com +mnaganov@google.com +yim@google.com +zhuoyao@google.com \ No newline at end of file diff --git a/audio/effect/4.0/vts/functional/Android.bp b/audio/effect/4.0/vts/functional/Android.bp new file mode 100644 index 0000000000..86c0afc8b7 --- /dev/null +++ b/audio/effect/4.0/vts/functional/Android.bp @@ -0,0 +1,36 @@ +// +// Copyright (C) 2016 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. +// + +cc_test { + name: "VtsHalAudioEffectV4_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "VtsHalAudioEffectV4_0TargetTest.cpp", + "ValidateAudioEffectsConfiguration.cpp" + ], + static_libs: [ + "android.hardware.audio.common.test.utility", + "android.hardware.audio.common@2.0", + "android.hardware.audio.effect@2.0", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libxml2", + ], + shared_libs: [ + "libeffectsconfig", + "libicuuc", + ], +} diff --git a/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp new file mode 100644 index 0000000000..d0bc6908d6 --- /dev/null +++ b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 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 +#include + +#include + +#include "utility/ValidateXml.h" + +TEST(CheckConfig, audioEffectsConfigurationValidation) { + RecordProperty("description", + "Verify that the effects configuration file is valid according to the schema"); + using namespace android::effectsConfig; + + std::vector locations(std::begin(DEFAULT_LOCATIONS), std::end(DEFAULT_LOCATIONS)); + EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(DEFAULT_NAME, locations, + "/data/local/tmp/audio_effects_conf_V2_0.xsd"); +} diff --git a/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp new file mode 100644 index 0000000000..c90c4fab2e --- /dev/null +++ b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp @@ -0,0 +1,849 @@ +/* + * Copyright (C) 2017 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 "AudioEffectHidlHalTest" +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using android::hardware::audio::common::V2_0::AudioDevice; +using android::hardware::audio::common::V2_0::AudioHandleConsts; +using android::hardware::audio::common::V2_0::AudioMode; +using android::hardware::audio::common::V2_0::AudioSource; +using android::hardware::audio::common::V2_0::Uuid; +using android::hardware::audio::effect::V2_0::AudioBuffer; +using android::hardware::audio::effect::V2_0::EffectAuxChannelsConfig; +using android::hardware::audio::effect::V2_0::EffectBufferConfig; +using android::hardware::audio::effect::V2_0::EffectConfig; +using android::hardware::audio::effect::V2_0::EffectDescriptor; +using android::hardware::audio::effect::V2_0::EffectOffloadParameter; +using android::hardware::audio::effect::V2_0::IEffect; +using android::hardware::audio::effect::V2_0::IEffectsFactory; +using android::hardware::audio::effect::V2_0::IEqualizerEffect; +using android::hardware::audio::effect::V2_0::ILoudnessEnhancerEffect; +using android::hardware::audio::effect::V2_0::Result; +using android::hardware::MQDescriptorSync; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::hidl_handle; +using android::hardware::hidl_memory; +using android::hardware::hidl_string; +using android::hardware::hidl_vec; +using android::hidl::allocator::V1_0::IAllocator; +using android::hidl::memory::V1_0::IMemory; +using android::sp; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#endif + +// Test environment for Audio Effects Factory HIDL HAL. +class AudioEffectsFactoryHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static AudioEffectsFactoryHidlEnvironment* Instance() { + static AudioEffectsFactoryHidlEnvironment* instance = + new AudioEffectsFactoryHidlEnvironment; + return instance; + } + + virtual void registerTestServices() override { registerTestService(); } +}; + +// The main test class for Audio Effects Factory HIDL HAL. +class AudioEffectsFactoryHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + void SetUp() override { + effectsFactory = ::testing::VtsHalHidlTargetTestBase::getService( + AudioEffectsFactoryHidlEnvironment::Instance()->getServiceName()); + ASSERT_NE(effectsFactory, nullptr); + } + + void TearDown() override { effectsFactory.clear(); } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + sp effectsFactory; +}; + +TEST_F(AudioEffectsFactoryHidlTest, EnumerateEffects) { + description("Verify that EnumerateEffects returns at least one effect"); + Result retval = Result::NOT_INITIALIZED; + size_t effectCount = 0; + Return ret = effectsFactory->getAllDescriptors( + [&](Result r, const hidl_vec& result) { + retval = r; + effectCount = result.size(); + }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_GT(effectCount, 0u); +} + +TEST_F(AudioEffectsFactoryHidlTest, CreateEffect) { + description("Verify that an effect can be created via CreateEffect"); + bool gotEffect = false; + Uuid effectUuid; + Return ret = effectsFactory->getAllDescriptors( + [&](Result r, const hidl_vec& result) { + if (r == Result::OK && result.size() > 0) { + gotEffect = true; + effectUuid = result[0].uuid; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_TRUE(gotEffect); + Result retval = Result::NOT_INITIALIZED; + sp effect; + ret = effectsFactory->createEffect( + effectUuid, 1 /*session*/, 1 /*ioHandle*/, + [&](Result r, const sp& result, uint64_t /*effectId*/) { + retval = r; + if (r == Result::OK) { + effect = result; + } + }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_NE(nullptr, effect.get()); +} + +TEST_F(AudioEffectsFactoryHidlTest, GetDescriptor) { + description( + "Verify that effects factory can provide an effect descriptor via " + "GetDescriptor"); + hidl_vec allDescriptors; + Return ret = effectsFactory->getAllDescriptors( + [&](Result r, const hidl_vec& result) { + if (r == Result::OK) { + allDescriptors = result; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_GT(allDescriptors.size(), 0u); + for (size_t i = 0; i < allDescriptors.size(); ++i) { + ret = effectsFactory->getDescriptor( + allDescriptors[i].uuid, [&](Result r, const EffectDescriptor& result) { + EXPECT_EQ(r, Result::OK); + EXPECT_EQ(result, allDescriptors[i]); + }); + } + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectsFactoryHidlTest, DebugDumpInvalidArgument) { + description("Verify that debugDump doesn't crash on invalid arguments"); + Return ret = effectsFactory->debugDump(hidl_handle()); + ASSERT_TRUE(ret.isOk()); +} + +// Equalizer effect is required by CDD, but only the type is fixed. +// This is the same UUID as AudioEffect.EFFECT_TYPE_EQUALIZER in Java. +static const Uuid EQUALIZER_EFFECT_TYPE = { + 0x0bed4300, 0xddd6, 0x11db, 0x8f34, + std::array{{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}}; +// Loudness Enhancer effect is required by CDD, but only the type is fixed. +// This is the same UUID as AudioEffect.EFFECT_TYPE_LOUDNESS_ENHANCER in Java. +static const Uuid LOUDNESS_ENHANCER_EFFECT_TYPE = { + 0xfe3199be, 0xaed0, 0x413f, 0x87bb, + std::array{{0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}}; + +// The main test class for Audio Effect HIDL HAL. +class AudioEffectHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + void SetUp() override { + effectsFactory = + ::testing::VtsHalHidlTargetTestBase::getService(); + ASSERT_NE(nullptr, effectsFactory.get()); + + findAndCreateEffect(getEffectType()); + ASSERT_NE(nullptr, effect.get()); + + Return ret = effect->init(); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, ret); + } + + void TearDown() override { + effect.clear(); + effectsFactory.clear(); + } + + protected: + static void description(const std::string& description) { + RecordProperty("description", description); + } + + virtual Uuid getEffectType() { return EQUALIZER_EFFECT_TYPE; } + + void findAndCreateEffect(const Uuid& type); + void findEffectInstance(const Uuid& type, Uuid* uuid); + void getChannelCount(uint32_t* channelCount); + + sp effectsFactory; + sp effect; +}; + +void AudioEffectHidlTest::findAndCreateEffect(const Uuid& type) { + Uuid effectUuid; + findEffectInstance(type, &effectUuid); + Return ret = effectsFactory->createEffect( + effectUuid, 1 /*session*/, 1 /*ioHandle*/, + [&](Result r, const sp& result, uint64_t /*effectId*/) { + if (r == Result::OK) { + effect = result; + } + }); + ASSERT_TRUE(ret.isOk()); +} + +void AudioEffectHidlTest::findEffectInstance(const Uuid& type, Uuid* uuid) { + bool effectFound = false; + Return ret = effectsFactory->getAllDescriptors( + [&](Result r, const hidl_vec& result) { + if (r == Result::OK) { + for (const auto& desc : result) { + if (desc.type == type) { + effectFound = true; + *uuid = desc.uuid; + break; + } + } + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_TRUE(effectFound); +} + +void AudioEffectHidlTest::getChannelCount(uint32_t* channelCount) { + Result retval; + EffectConfig currentConfig; + Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + currentConfig = conf; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); + ASSERT_TRUE(audio_channel_mask_is_valid( + static_cast(currentConfig.outputCfg.channels))); + *channelCount = audio_channel_count_from_out_mask( + static_cast(currentConfig.outputCfg.channels)); +} + +TEST_F(AudioEffectHidlTest, Close) { + description("Verify that an effect can be closed"); + Return ret = effect->close(); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); +} + +TEST_F(AudioEffectHidlTest, GetDescriptor) { + description( + "Verify that an effect can return its own descriptor via GetDescriptor"); + Result retval = Result::NOT_INITIALIZED; + Uuid actualType; + Return ret = + effect->getDescriptor([&](Result r, const EffectDescriptor& desc) { + retval = r; + if (r == Result::OK) { + actualType = desc.type; + } + }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(getEffectType(), actualType); +} + +TEST_F(AudioEffectHidlTest, GetSetConfig) { + description( + "Verify that it is possible to manipulate effect config via Get / " + "SetConfig"); + Result retval = Result::NOT_INITIALIZED; + EffectConfig currentConfig; + Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + currentConfig = conf; + } + }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); + Return ret2 = effect->setConfig(currentConfig, nullptr, nullptr); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, ret2); +} + +TEST_F(AudioEffectHidlTest, GetConfigReverse) { + description("Verify that GetConfigReverse does not crash"); + Return ret = + effect->getConfigReverse([&](Result, const EffectConfig&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, GetSupportedAuxChannelsConfigs) { + description("Verify that GetSupportedAuxChannelsConfigs does not crash"); + Return ret = effect->getSupportedAuxChannelsConfigs( + 0, [&](Result, const hidl_vec&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, GetAuxChannelsConfig) { + description("Verify that GetAuxChannelsConfig does not crash"); + Return ret = effect->getAuxChannelsConfig( + [&](Result, const EffectAuxChannelsConfig&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, SetAuxChannelsConfig) { + description("Verify that SetAuxChannelsConfig does not crash"); + Return ret = effect->setAuxChannelsConfig(EffectAuxChannelsConfig()); + EXPECT_TRUE(ret.isOk()); +} + +// Not generated automatically because AudioBuffer contains +// instances of hidl_memory which can't be compared properly +// in general case due to presence of handles. +// +// However, in this particular case, handles must not present +// thus comparison is possible. +// +// operator== must be defined in the same namespace as the structures. +namespace android { +namespace hardware { +namespace audio { +namespace effect { +namespace V2_0 { +inline bool operator==(const AudioBuffer& lhs, const AudioBuffer& rhs) { + return lhs.id == rhs.id && lhs.frameCount == rhs.frameCount && + lhs.data.handle() == nullptr && rhs.data.handle() == nullptr; +} + +inline bool operator==(const EffectBufferConfig& lhs, + const EffectBufferConfig& rhs) { + return lhs.buffer == rhs.buffer && lhs.samplingRateHz == rhs.samplingRateHz && + lhs.channels == rhs.channels && lhs.format == rhs.format && + lhs.accessMode == rhs.accessMode && lhs.mask == rhs.mask; +} + +inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) { + return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg; +} +} // namespace V2_0 +} // namespace effect +} // namespace audio +} // namespace hardware +} // namespace android + +TEST_F(AudioEffectHidlTest, Reset) { + description("Verify that Reset preserves effect configuration"); + Result retval = Result::NOT_INITIALIZED; + EffectConfig originalConfig; + Return ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + originalConfig = conf; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); + Return ret2 = effect->reset(); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, ret2); + EffectConfig configAfterReset; + ret = effect->getConfig([&](Result r, const EffectConfig& conf) { + retval = r; + if (r == Result::OK) { + configAfterReset = conf; + } + }); + EXPECT_EQ(originalConfig, configAfterReset); +} + +TEST_F(AudioEffectHidlTest, DisableEnableDisable) { + description("Verify Disable -> Enable -> Disable sequence for an effect"); + Return ret = effect->disable(); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::INVALID_ARGUMENTS, ret); + ret = effect->enable(); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + ret = effect->disable(); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); +} + +TEST_F(AudioEffectHidlTest, SetDevice) { + description("Verify that SetDevice works for an output chain effect"); + Return ret = effect->setDevice(AudioDevice::OUT_SPEAKER); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); +} + +TEST_F(AudioEffectHidlTest, SetAndGetVolume) { + description("Verify that SetAndGetVolume method works for an effect"); + uint32_t channelCount; + getChannelCount(&channelCount); + hidl_vec volumes; + volumes.resize(channelCount); + for (uint32_t i = 0; i < channelCount; ++i) { + volumes[i] = 0; + } + Result retval = Result::NOT_INITIALIZED; + Return ret = effect->setAndGetVolume( + volumes, [&](Result r, const hidl_vec&) { retval = r; }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); +} + +TEST_F(AudioEffectHidlTest, VolumeChangeNotification) { + description("Verify that effect accepts VolumeChangeNotification"); + uint32_t channelCount; + getChannelCount(&channelCount); + hidl_vec volumes; + volumes.resize(channelCount); + for (uint32_t i = 0; i < channelCount; ++i) { + volumes[i] = 0; + } + Return ret = effect->volumeChangeNotification(volumes); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); +} + +TEST_F(AudioEffectHidlTest, SetAudioMode) { + description("Verify that SetAudioMode works for an effect"); + Return ret = effect->setAudioMode(AudioMode::NORMAL); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); +} + +TEST_F(AudioEffectHidlTest, SetConfigReverse) { + description("Verify that SetConfigReverse does not crash"); + Return ret = + effect->setConfigReverse(EffectConfig(), nullptr, nullptr); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, SetInputDevice) { + description("Verify that SetInputDevice does not crash"); + Return ret = effect->setInputDevice(AudioDevice::IN_BUILTIN_MIC); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, SetAudioSource) { + description("Verify that SetAudioSource does not crash"); + Return ret = effect->setAudioSource(AudioSource::MIC); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, Offload) { + description("Verify that calling Offload method does not crash"); + EffectOffloadParameter offloadParam; + offloadParam.isOffload = false; + offloadParam.ioHandle = + static_cast(AudioHandleConsts::AUDIO_IO_HANDLE_NONE); + Return ret = effect->offload(offloadParam); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, PrepareForProcessing) { + description("Verify that PrepareForProcessing method works for an effect"); + Result retval = Result::NOT_INITIALIZED; + Return ret = effect->prepareForProcessing( + [&](Result r, const MQDescriptorSync&) { retval = r; }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); +} + +TEST_F(AudioEffectHidlTest, SetProcessBuffers) { + description("Verify that SetProcessBuffers works for an effect"); + sp ashmem = IAllocator::getService("ashmem"); + ASSERT_NE(nullptr, ashmem.get()); + bool success = false; + AudioBuffer buffer; + Return ret = + ashmem->allocate(1024, [&](bool s, const hidl_memory& memory) { + success = s; + if (s) { + buffer.data = memory; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_TRUE(success); + Return ret2 = effect->setProcessBuffers(buffer, buffer); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, ret2); +} + +TEST_F(AudioEffectHidlTest, Command) { + description("Verify that Command does not crash"); + Return ret = effect->command(0, hidl_vec(), 0, + [&](int32_t, const hidl_vec&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, SetParameter) { + description("Verify that SetParameter does not crash"); + Return ret = + effect->setParameter(hidl_vec(), hidl_vec()); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, GetParameter) { + description("Verify that GetParameter does not crash"); + Return ret = effect->getParameter( + hidl_vec(), 0, [&](Result, const hidl_vec&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, GetSupportedConfigsForFeature) { + description("Verify that GetSupportedConfigsForFeature does not crash"); + Return ret = effect->getSupportedConfigsForFeature( + 0, 0, 0, [&](Result, uint32_t, const hidl_vec&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, GetCurrentConfigForFeature) { + description("Verify that GetCurrentConfigForFeature does not crash"); + Return ret = effect->getCurrentConfigForFeature( + 0, 0, [&](Result, const hidl_vec&) {}); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(AudioEffectHidlTest, SetCurrentConfigForFeature) { + description("Verify that SetCurrentConfigForFeature does not crash"); + Return ret = + effect->setCurrentConfigForFeature(0, hidl_vec()); + EXPECT_TRUE(ret.isOk()); +} + + +// The main test class for Equalizer Audio Effect HIDL HAL. +class EqualizerAudioEffectHidlTest : public AudioEffectHidlTest { + public: + void SetUp() override { + AudioEffectHidlTest::SetUp(); + equalizer = IEqualizerEffect::castFrom(effect); + ASSERT_NE(nullptr, equalizer.get()); + } + + protected: + Uuid getEffectType() override { return EQUALIZER_EFFECT_TYPE; } + void getNumBands(uint16_t* numBands); + void getLevelRange(int16_t* minLevel, int16_t* maxLevel); + void getBandFrequencyRange(uint16_t band, uint32_t* minFreq, + uint32_t* centerFreq, uint32_t* maxFreq); + void getPresetCount(size_t* count); + + sp equalizer; +}; + +void EqualizerAudioEffectHidlTest::getNumBands(uint16_t* numBands) { + Result retval = Result::NOT_INITIALIZED; + Return ret = equalizer->getNumBands([&](Result r, uint16_t b) { + retval = r; + if (retval == Result::OK) { + *numBands = b; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); +} + +void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel, + int16_t* maxLevel) { + Result retval = Result::NOT_INITIALIZED; + Return ret = + equalizer->getLevelRange([&](Result r, int16_t min, int16_t max) { + retval = r; + if (retval == Result::OK) { + *minLevel = min; + *maxLevel = max; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); +} + +void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, + uint32_t* minFreq, + uint32_t* centerFreq, + uint32_t* maxFreq) { + Result retval = Result::NOT_INITIALIZED; + Return ret = equalizer->getBandFrequencyRange( + band, [&](Result r, uint32_t min, uint32_t max) { + retval = r; + if (retval == Result::OK) { + *minFreq = min; + *maxFreq = max; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); + ret = equalizer->getBandCenterFrequency(band, [&](Result r, uint32_t center) { + retval = r; + if (retval == Result::OK) { + *centerFreq = center; + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); +} + +void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) { + Result retval = Result::NOT_INITIALIZED; + Return ret = equalizer->getPresetNames( + [&](Result r, const hidl_vec& names) { + retval = r; + if (retval == Result::OK) { + *count = names.size(); + } + }); + ASSERT_TRUE(ret.isOk()); + ASSERT_EQ(Result::OK, retval); +} + +TEST_F(EqualizerAudioEffectHidlTest, GetNumBands) { + description("Verify that Equalizer effect reports at least one band"); + uint16_t numBands = 0; + getNumBands(&numBands); + EXPECT_GT(numBands, 0); +} + +TEST_F(EqualizerAudioEffectHidlTest, GetLevelRange) { + description("Verify that Equalizer effect reports adequate band level range"); + int16_t minLevel = 0x7fff, maxLevel = 0; + getLevelRange(&minLevel, &maxLevel); + EXPECT_GT(maxLevel, minLevel); +} + +TEST_F(EqualizerAudioEffectHidlTest, GetSetBandLevel) { + description( + "Verify that manipulating band levels works for Equalizer effect"); + uint16_t numBands = 0; + getNumBands(&numBands); + ASSERT_GT(numBands, 0); + int16_t levels[3]{0x7fff, 0, 0}; + getLevelRange(&levels[0], &levels[2]); + ASSERT_GT(levels[2], levels[0]); + levels[1] = (levels[2] + levels[0]) / 2; + for (uint16_t i = 0; i < numBands; ++i) { + for (size_t j = 0; j < ARRAY_SIZE(levels); ++j) { + Return ret = equalizer->setBandLevel(i, levels[j]); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + Result retval = Result::NOT_INITIALIZED; + int16_t actualLevel; + Return ret2 = equalizer->getBandLevel(i, [&](Result r, int16_t l) { + retval = r; + if (retval == Result::OK) { + actualLevel = l; + } + }); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(levels[j], actualLevel); + } + } +} + +TEST_F(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) { + description( + "Verify that Equalizer effect reports adequate band frequency range"); + uint16_t numBands = 0; + getNumBands(&numBands); + ASSERT_GT(numBands, 0); + for (uint16_t i = 0; i < numBands; ++i) { + uint32_t minFreq = 0xffffffff, centerFreq = 0xffffffff, + maxFreq = 0xffffffff; + getBandFrequencyRange(i, &minFreq, ¢erFreq, &maxFreq); + // Note: NXP legacy implementation reports "1" as upper bound for last band, + // so this check fails. + EXPECT_GE(maxFreq, centerFreq); + EXPECT_GE(centerFreq, minFreq); + } +} + +TEST_F(EqualizerAudioEffectHidlTest, GetBandForFrequency) { + description( + "Verify that Equalizer effect supports GetBandForFrequency correctly"); + uint16_t numBands = 0; + getNumBands(&numBands); + ASSERT_GT(numBands, 0); + for (uint16_t i = 0; i < numBands; ++i) { + uint32_t freqs[3]{0, 0, 0}; + getBandFrequencyRange(i, &freqs[0], &freqs[1], &freqs[2]); + // NXP legacy implementation reports "1" as upper bound for last band, some + // of the checks fail. + for (size_t j = 0; j < ARRAY_SIZE(freqs); ++j) { + if (j == 0) { + freqs[j]++; + } // Min frequency is an open interval. + Result retval = Result::NOT_INITIALIZED; + uint16_t actualBand = numBands + 1; + Return ret = + equalizer->getBandForFrequency(freqs[j], [&](Result r, uint16_t b) { + retval = r; + if (retval == Result::OK) { + actualBand = b; + } + }); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j]; + } + } +} + +TEST_F(EqualizerAudioEffectHidlTest, GetPresetNames) { + description("Verify that Equalizer effect reports at least one preset"); + size_t presetCount; + getPresetCount(&presetCount); + EXPECT_GT(presetCount, 0u); +} + +TEST_F(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) { + description( + "Verify that manipulating the current preset for Equalizer effect"); + size_t presetCount; + getPresetCount(&presetCount); + ASSERT_GT(presetCount, 0u); + for (uint16_t i = 0; i < presetCount; ++i) { + Return ret = equalizer->setCurrentPreset(i); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + Result retval = Result::NOT_INITIALIZED; + uint16_t actualPreset = 0xffff; + Return ret2 = equalizer->getCurrentPreset([&](Result r, uint16_t p) { + retval = r; + if (retval == Result::OK) { + actualPreset = p; + } + }); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(i, actualPreset); + } +} + +TEST_F(EqualizerAudioEffectHidlTest, GetSetAllProperties) { + description( + "Verify that setting band levels and presets works via Get / " + "SetAllProperties for Equalizer effect"); + using AllProperties = + android::hardware::audio::effect::V2_0::IEqualizerEffect::AllProperties; + uint16_t numBands = 0; + getNumBands(&numBands); + ASSERT_GT(numBands, 0); + AllProperties props; + props.bandLevels.resize(numBands); + for (size_t i = 0; i < numBands; ++i) { + props.bandLevels[i] = 0; + } + + AllProperties actualProps; + Result retval = Result::NOT_INITIALIZED; + + // Verify setting of the band levels via properties. + props.curPreset = -1; + Return ret = equalizer->setAllProperties(props); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + Return ret2 = + equalizer->getAllProperties([&](Result r, AllProperties p) { + retval = r; + if (retval == Result::OK) { + actualProps = p; + } + }); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(props.bandLevels, actualProps.bandLevels); + + // Verify setting of the current preset via properties. + props.curPreset = 0; // Assuming there is at least one preset. + ret = equalizer->setAllProperties(props); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) { + retval = r; + if (retval == Result::OK) { + actualProps = p; + } + }); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(props.curPreset, actualProps.curPreset); +} + +// The main test class for Equalizer Audio Effect HIDL HAL. +class LoudnessEnhancerAudioEffectHidlTest : public AudioEffectHidlTest { + public: + void SetUp() override { + AudioEffectHidlTest::SetUp(); + enhancer = ILoudnessEnhancerEffect::castFrom(effect); + ASSERT_NE(nullptr, enhancer.get()); + } + + protected: + Uuid getEffectType() override { return LOUDNESS_ENHANCER_EFFECT_TYPE; } + + sp enhancer; +}; + +TEST_F(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) { + description( + "Verify that manipulating the target gain works for Loudness Enhancer " + "effect"); + const int32_t gain = 100; + Return ret = enhancer->setTargetGain(gain); + EXPECT_TRUE(ret.isOk()); + EXPECT_EQ(Result::OK, ret); + int32_t actualGain = 0; + Result retval; + Return ret2 = enhancer->getTargetGain([&](Result r, int32_t g) { + retval = r; + if (retval == Result::OK) { + actualGain = g; + } + }); + EXPECT_TRUE(ret2.isOk()); + EXPECT_EQ(Result::OK, retval); + EXPECT_EQ(gain, actualGain); +} + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment(AudioEffectsFactoryHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + AudioEffectsFactoryHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} From 8532f6b2cc093e4fac933287569e175b0bcf4493 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 12 Mar 2018 14:20:09 -0700 Subject: [PATCH 2/3] Fix issues in ReturnIn Bug: 74037175 Test: compile Change-Id: Icc3a0d1d489474de7c6255a014dccecc4dda4aa2 Signed-off-by: Kevin Rocard --- .../all-versions/test/utility/include/utility/ReturnIn.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/common/all-versions/test/utility/include/utility/ReturnIn.h b/audio/common/all-versions/test/utility/include/utility/ReturnIn.h index 2b92a216fe..7fd0d4a437 100644 --- a/audio/common/all-versions/test/utility/include/utility/ReturnIn.h +++ b/audio/common/all-versions/test/utility/include/utility/ReturnIn.h @@ -45,7 +45,7 @@ class ReturnIn { template void set(Head&& head, Tail&&... tail) { std::get(results) = std::forward(head); - set(tail...); + set(std::forward(tail)...); } // Trivial case void set() {} @@ -56,7 +56,7 @@ class ReturnIn { } // namespace detail // Generate the HIDL synchronous callback with a copy policy -// Input: the variables (lvalue reference) where to save the return values +// Input: the variables (lvalue references) where to copy the return values // Output: the callback to provide to a HIDL call with a synchronous callback // The output parameters *will be copied* do not use this function if you have // a zero copy policy From e3afd40e91eafa020429dbbc3ebbb1690d0e679d Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 12 Mar 2018 15:56:51 -0700 Subject: [PATCH 3/3] Audio V4: Update V2 tests to V4 Bug: 74037175 Bug: 38184704 Test: vts-tradefed run commandAndExit vts --module VtsHalAudioV4_0Target Change-Id: Ic8da941fc042b7ef838edf8fdac36b2f0fa1fd32 Signed-off-by: Kevin Rocard --- .../common/all-versions/VersionUtils.h | 6 + audio/core/4.0/vts/functional/Android.bp | 7 +- .../functional/AudioPrimaryHidlHalTest.cpp | 250 ++++++++++-------- audio/effect/4.0/vts/functional/Android.bp | 7 +- .../ValidateAudioEffectsConfiguration.cpp | 2 +- .../VtsHalAudioEffectV4_0TargetTest.cpp | 57 ++-- 6 files changed, 194 insertions(+), 135 deletions(-) diff --git a/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h b/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h index a998b0675c..70c3d56a42 100644 --- a/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h +++ b/audio/common/all-versions/util/include/common/all-versions/VersionUtils.h @@ -52,6 +52,12 @@ auto mkEnumConverter(Source source) { return EnumConverter{source}; } +/** Allows converting an enum to its bitfield or itself. */ +template +EnumConverter mkBitfield(Enum value) { + return EnumConverter{value}; +} + } // namespace utils } // namespace common } // namespace audio diff --git a/audio/core/4.0/vts/functional/Android.bp b/audio/core/4.0/vts/functional/Android.bp index f0327272e4..dde3e7b036 100644 --- a/audio/core/4.0/vts/functional/Android.bp +++ b/audio/core/4.0/vts/functional/Android.bp @@ -23,11 +23,14 @@ cc_test { ], static_libs: [ "android.hardware.audio.common.test.utility", - "android.hardware.audio@2.0", - "android.hardware.audio.common@2.0", + "android.hardware.audio@4.0", + "android.hardware.audio.common@4.0", "libxml2", ], shared_libs: [ "libicuuc", ], + header_libs: [ + "android.hardware.audio.common.util@all-versions", + ], } diff --git a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp index bb1d26f928..a568a3cebe 100644 --- a/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp +++ b/audio/core/4.0/vts/functional/AudioPrimaryHidlHalTest.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "VtsHalAudioV2_0TargetTest" +#define LOG_TAG "VtsHalAudioV4_0TargetTest" #include #include @@ -32,16 +32,18 @@ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include + +#include #include "utility/AssertOk.h" #include "utility/Documentation.h" #include "utility/EnvironmentTearDown.h" -#define AUDIO_HAL_VERSION V2_0 +#define AUDIO_HAL_VERSION V4_0 #include "utility/PrettyPrintAudioTypes.h" #include "utility/ReturnIn.h" @@ -52,39 +54,44 @@ using std::vector; using ::android::sp; using ::android::hardware::Return; +using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::MQDescriptorSync; -using ::android::hardware::audio::V2_0::AudioDrain; -using ::android::hardware::audio::V2_0::DeviceAddress; -using ::android::hardware::audio::V2_0::IDevice; -using ::android::hardware::audio::V2_0::IPrimaryDevice; -using TtyMode = ::android::hardware::audio::V2_0::IPrimaryDevice::TtyMode; -using ::android::hardware::audio::V2_0::IDevicesFactory; -using ::android::hardware::audio::V2_0::IStream; -using ::android::hardware::audio::V2_0::IStreamIn; -using ::android::hardware::audio::V2_0::TimeSpec; -using ReadParameters = ::android::hardware::audio::V2_0::IStreamIn::ReadParameters; -using ReadStatus = ::android::hardware::audio::V2_0::IStreamIn::ReadStatus; -using ::android::hardware::audio::V2_0::IStreamOut; -using ::android::hardware::audio::V2_0::IStreamOutCallback; -using ::android::hardware::audio::V2_0::MmapBufferInfo; -using ::android::hardware::audio::V2_0::MmapPosition; -using ::android::hardware::audio::V2_0::ParameterValue; -using ::android::hardware::audio::V2_0::Result; -using ::android::hardware::audio::common::V2_0::AudioChannelMask; -using ::android::hardware::audio::common::V2_0::AudioConfig; -using ::android::hardware::audio::common::V2_0::AudioDevice; -using ::android::hardware::audio::common::V2_0::AudioFormat; -using ::android::hardware::audio::common::V2_0::AudioHandleConsts; -using ::android::hardware::audio::common::V2_0::AudioInputFlag; -using ::android::hardware::audio::common::V2_0::AudioIoHandle; -using ::android::hardware::audio::common::V2_0::AudioMode; -using ::android::hardware::audio::common::V2_0::AudioOffloadInfo; -using ::android::hardware::audio::common::V2_0::AudioOutputFlag; -using ::android::hardware::audio::common::V2_0::AudioSource; -using ::android::hardware::audio::common::V2_0::ThreadInfo; +using ::android::hardware::audio::V4_0::AudioDrain; +using ::android::hardware::audio::V4_0::DeviceAddress; +using ::android::hardware::audio::V4_0::IDevice; +using ::android::hardware::audio::V4_0::IPrimaryDevice; +using TtyMode = ::android::hardware::audio::V4_0::IPrimaryDevice::TtyMode; +using ::android::hardware::audio::V4_0::IDevicesFactory; +using ::android::hardware::audio::V4_0::IStream; +using ::android::hardware::audio::V4_0::IStreamIn; +using ::android::hardware::audio::V4_0::TimeSpec; +using ReadParameters = ::android::hardware::audio::V4_0::IStreamIn::ReadParameters; +using ReadStatus = ::android::hardware::audio::V4_0::IStreamIn::ReadStatus; +using ::android::hardware::audio::V4_0::IStreamOut; +using ::android::hardware::audio::V4_0::IStreamOutCallback; +using ::android::hardware::audio::V4_0::MmapBufferInfo; +using ::android::hardware::audio::V4_0::MmapPosition; +using ::android::hardware::audio::V4_0::ParameterValue; +using ::android::hardware::audio::V4_0::Result; +using ::android::hardware::audio::V4_0::SourceMetadata; +using ::android::hardware::audio::V4_0::SinkMetadata; +using ::android::hardware::audio::common::V4_0::AudioChannelMask; +using ::android::hardware::audio::common::V4_0::AudioConfig; +using ::android::hardware::audio::common::V4_0::AudioDevice; +using ::android::hardware::audio::common::V4_0::AudioFormat; +using ::android::hardware::audio::common::V4_0::AudioHandleConsts; +using ::android::hardware::audio::common::V4_0::AudioHwSync; +using ::android::hardware::audio::common::V4_0::AudioInputFlag; +using ::android::hardware::audio::common::V4_0::AudioIoHandle; +using ::android::hardware::audio::common::V4_0::AudioMode; +using ::android::hardware::audio::common::V4_0::AudioOffloadInfo; +using ::android::hardware::audio::common::V4_0::AudioOutputFlag; +using ::android::hardware::audio::common::V4_0::AudioSource; +using ::android::hardware::audio::common::V4_0::ThreadInfo; +using ::android::hardware::audio::common::utils::mkBitfield; using namespace ::android::hardware::audio::common::test::utility; @@ -132,10 +139,10 @@ TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) { TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) { doc::test("test passing an invalid parameter to openDevice"); - IDevicesFactory::Result result; + Result result; sp device; - ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device(-1), returnIn(result, device))); - ASSERT_EQ(IDevicesFactory::Result::INVALID_ARGUMENTS, result); + ASSERT_OK(devicesFactory->openDevice("Non existing device", returnIn(result, device))); + ASSERT_EQ(Result::INVALID_ARGUMENTS, result); ASSERT_TRUE(device == nullptr); } @@ -151,10 +158,9 @@ class AudioPrimaryHidlTest : public AudioHidlTest { ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base if (device == nullptr) { - IDevicesFactory::Result result; + Result result; sp baseDevice; - ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY, - returnIn(result, baseDevice))); + ASSERT_OK(devicesFactory->openDevice("primary", returnIn(result, baseDevice))); ASSERT_OK(result); ASSERT_TRUE(baseDevice != nullptr); @@ -337,7 +343,7 @@ class AudioConfigPrimaryTest : public AudioPatchPrimaryHidlTest { for (auto format : formats) { AudioConfig config{}; // leave offloadInfo to 0 - config.channelMask = channelMask; + config.channelMask = mkBitfield(channelMask); config.sampleRateHz = sampleRate; config.format = format; // FIXME: leave frameCount to 0 ? @@ -358,10 +364,10 @@ static string generateTestName(const testing::TestParamInfo& info) const AudioConfig& config = info.param; return to_string(info.index) + "__" + to_string(config.sampleRateHz) + "_" + // "MONO" is more clear than "FRONT_LEFT" - ((config.channelMask == AudioChannelMask::OUT_MONO || - config.channelMask == AudioChannelMask::IN_MONO) + ((config.channelMask == mkBitfield(AudioChannelMask::OUT_MONO) || + config.channelMask == mkBitfield(AudioChannelMask::IN_MONO)) ? "MONO" - : toString(config.channelMask)); + : ::testing::PrintToString(config.channelMask)); } ////////////////////////////////////////////////////////////////////////////// @@ -447,12 +453,13 @@ TEST_F(AudioPrimaryHidlTest, setScreenState) { TEST_F(AudioPrimaryHidlTest, getParameters) { doc::test("Check that the hal can set and get parameters"); + hidl_vec context; hidl_vec keys; hidl_vec values; - ASSERT_OK(device->getParameters(keys, returnIn(res, values))); - ASSERT_OK(device->setParameters(values)); + ASSERT_OK(device->getParameters(context, keys, returnIn(res, values))); + ASSERT_OK(device->setParameters(context, values)); values.resize(0); - ASSERT_OK(device->setParameters(values)); + ASSERT_OK(device->setParameters(context, values)); } ////////////////////////////////////////////////////////////////////////////// @@ -495,12 +502,32 @@ static void testDebugDump(DebugDump debugDump) { TEST_F(AudioPrimaryHidlTest, DebugDump) { doc::test("Check that the hal can dump its state without error"); - testDebugDump([](const auto& handle) { return device->debugDump(handle); }); + testDebugDump([](const auto& handle) { return device->debug(handle, {/* options */}); }); } TEST_F(AudioPrimaryHidlTest, DebugDumpInvalidArguments) { doc::test("Check that the hal dump doesn't crash on invalid arguments"); - ASSERT_OK(device->debugDump(hidl_handle())); + ASSERT_OK(device->debug(hidl_handle(), {/* options */})); +} + +TEST_F(AudioPrimaryHidlTest, SetConnectedState) { + doc::test("Check that the HAL can be notified of device connection and deconnection"); + using AD = AudioDevice; + for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) { + SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType)); + for (bool state : {true, false}) { + SCOPED_TRACE("state=" + ::testing::PrintToString(state)); + DeviceAddress address = {}; + address.device = deviceType; + auto ret = device->setConnectedState(address, state); + ASSERT_TRUE(ret.isOk()); + if (res == Result::NOT_SUPPORTED) { + doc::partialTest("setConnectedState is not supported"); + return; + } + ASSERT_OK(res); + } + } } ////////////////////////////////////////////////////////////////////////////// @@ -569,10 +596,12 @@ class OutputStreamTest : public OpenStreamTest { ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base address.device = AudioDevice::OUT_DEFAULT; const AudioConfig& config = GetParam(); - AudioOutputFlag flags = AudioOutputFlag::NONE; // TODO: test all flag combination + // TODO: test all flag combination + auto flags = hidl_bitfield(AudioOutputFlag::NONE); + SourceMetadata metadata = {{{}}}; // create on track metadata testOpen( [&](AudioIoHandle handle, AudioConfig config, auto cb) { - return device->openOutputStream(handle, address, config, flags, cb); + return device->openOutputStream(handle, address, config, flags, metadata, cb); }, config); } @@ -604,11 +633,12 @@ class InputStreamTest : public OpenStreamTest { ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base address.device = AudioDevice::IN_DEFAULT; const AudioConfig& config = GetParam(); - AudioInputFlag flags = AudioInputFlag::NONE; // TODO: test all flag combination - AudioSource source = AudioSource::DEFAULT; // TODO: test all flag combination + // TODO: test all supported flags and source + auto flags = hidl_bitfield(AudioInputFlag::NONE); + SinkMetadata metadata = {{{AudioSource::DEFAULT, 1}}}; testOpen( [&](AudioIoHandle handle, AudioConfig config, auto cb) { - return device->openInputStream(handle, address, config, flags, source, cb); + return device->openInputStream(handle, address, config, flags, metadata, cb); }, config); } @@ -680,22 +710,19 @@ TEST_IO_STREAM(GetFrameSize, "Check that the stream frame size == the one it was TEST_IO_STREAM(GetBufferSize, "Check that the stream buffer size== the one it was opened with", ASSERT_GE(extract(stream->getBufferSize()), extract(stream->getFrameSize()))); -template +template static void testCapabilityGetter(const string& name, IStream* stream, - CapabilityGetter capablityGetter, + CapablityGetter capablityGetter, Return (IStream::*getter)(), Return (IStream::*setter)(Property), bool currentMustBeSupported = true) { hidl_vec capabilities; - ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities))); - if (capabilities.size() == 0) { - // The default hal should probably return a NOT_SUPPORTED if the hal - // does not expose - // capability retrieval. For now it returns an empty list if not - // implemented + auto ret = capablityGetter(stream, capabilities); + if (ret == Result::NOT_SUPPORTED) { doc::partialTest(name + " is not supported"); return; }; + ASSERT_OK(ret); if (currentMustBeSupported) { Property currentValue = extract((stream->*getter)()); @@ -718,9 +745,29 @@ static void testCapabilityGetter(const string& name, IStream* stream, } } +Result getSupportedSampleRates(IStream* stream, hidl_vec& rates) { + Result res; + EXPECT_OK(stream->getSupportedSampleRates(extract(stream->getFormat()), returnIn(res, rates))); + return res; +} + +Result getSupportedChannelMasks(IStream* stream, + hidl_vec>& channels) { + Result res; + EXPECT_OK( + stream->getSupportedSampleRates(extract(stream->getFormat()), returnIn(res, channels))); + return res; +} + +Result getSupportedFormats(IStream* stream, hidl_vec& capabilities) { + EXPECT_OK(stream->getSupportedFormats(returnIn(capabilities))); + // TODO: this should be an optional function + return Result::OK; +} + TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declared as supported", testCapabilityGetter("getSupportedSampleRate", stream.get(), - &IStream::getSupportedSampleRates, &IStream::getSampleRate, + &getSupportedSampleRates, &IStream::getSampleRate, &IStream::setSampleRate, // getSupportedSampleRate returns the native sampling rates, // (the sampling rates that can be played without resampling) @@ -729,22 +776,24 @@ TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declar TEST_IO_STREAM(SupportedChannelMask, "Check that the stream channel mask is declared as supported", testCapabilityGetter("getSupportedChannelMask", stream.get(), - &IStream::getSupportedChannelMasks, &IStream::getChannelMask, + &getSupportedChannelMasks, &IStream::getChannelMask, &IStream::setChannelMask)) TEST_IO_STREAM(SupportedFormat, "Check that the stream format is declared as supported", - testCapabilityGetter("getSupportedFormat", stream.get(), - &IStream::getSupportedFormats, &IStream::getFormat, - &IStream::setFormat)) + testCapabilityGetter("getSupportedFormat", stream.get(), &getSupportedFormats, + &IStream::getFormat, &IStream::setFormat)) static void testGetDevice(IStream* stream, AudioDevice expectedDevice) { - // Unfortunately the interface does not allow the implementation to return - // NOT_SUPPORTED - // Thus allow NONE as signaling that the call is not supported. - auto ret = stream->getDevice(); - ASSERT_IS_OK(ret); - AudioDevice device = ret; - ASSERT_TRUE(device == expectedDevice || device == AudioDevice::NONE) + hidl_vec devices; + Result res; + ASSERT_OK(stream->getDevices(returnIn(res, devices))); + if (res == Result::NOT_SUPPORTED) { + return doc::partialTest("GetDevices is not supported"); + } + // The stream was constructed with one device, thus getDevices must only return one + ASSERT_EQ(1U, devices.size()); + AudioDevice device = devices[0].device; + ASSERT_TRUE(device == expectedDevice) << "Expected: " << ::testing::PrintToString(expectedDevice) << "\n Actual: " << ::testing::PrintToString(device); } @@ -757,9 +806,9 @@ static void testSetDevice(IStream* stream, const DeviceAddress& address) { DeviceAddress otherAddress = address; otherAddress.device = (address.device & AudioDevice::BIT_IN) == 0 ? AudioDevice::OUT_SPEAKER : AudioDevice::IN_BUILTIN_MIC; - EXPECT_OK(stream->setDevice(otherAddress)); + EXPECT_OK(stream->setDevices({otherAddress})); - ASSERT_OK(stream->setDevice(address)); // Go back to the original value + ASSERT_OK(stream->setDevices({address})); // Go back to the original value } TEST_IO_STREAM(SetDevice, "Check that the stream can be rerouted to SPEAKER or BUILTIN_MIC", @@ -768,7 +817,7 @@ TEST_IO_STREAM(SetDevice, "Check that the stream can be rerouted to SPEAKER or B static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) { uint32_t sampleRateHz; - AudioChannelMask mask; + hidl_bitfield mask; AudioFormat format; stream->getAudioProperties(returnIn(sampleRateHz, mask, format)); @@ -784,33 +833,28 @@ TEST_IO_STREAM(GetAudioProperties, "Check that the stream audio properties == the ones it was opened with", testGetAudioProperties(stream.get(), audioConfig)) -static void testConnectedState(IStream* stream) { - DeviceAddress address = {}; - using AD = AudioDevice; - for (auto device : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) { - address.device = device; - - ASSERT_OK(stream->setConnectedState(address, true)); - ASSERT_OK(stream->setConnectedState(address, false)); - } -} -TEST_IO_STREAM(SetConnectedState, - "Check that the stream can be notified of device connection and " - "deconnection", - testConnectedState(stream.get())) - static auto invalidArgsOrNotSupportedOrOK = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED, Result::OK}; TEST_IO_STREAM(SetHwAvSync, "Try to set hardware sync to an invalid value", ASSERT_RESULT(invalidArgsOrNotSupportedOrOK, stream->setHwAvSync(666))) -TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", ASSERT_IS_OK(device->getHwAvSync())); +static void checkGetHwAVSync(IDevice* device) { + Result res; + AudioHwSync sync; + ASSERT_OK(device->getHwAvSync(returnIn(res, sync))); + if (res == Result::NOT_SUPPORTED) { + return doc::partialTest("getHwAvSync is not supported"); + } + ASSERT_OK(res); +} +TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(device.get())); static void checkGetNoParameter(IStream* stream, hidl_vec keys, initializer_list expectedResults) { + hidl_vec context; hidl_vec parameters; Result res; - ASSERT_OK(stream->getParameters(keys, returnIn(res, parameters))); + ASSERT_OK(stream->getParameters(context, keys, returnIn(res, parameters))); ASSERT_RESULT(expectedResults, res); if (res == Result::OK) { for (auto& parameter : parameters) { @@ -831,7 +875,7 @@ TEST_IO_STREAM(getNonExistingParameter, "Retrieve the values of an non existing {Result::NOT_SUPPORTED})) TEST_IO_STREAM(setEmptySetParameter, "Set the values of an empty set of parameters", - ASSERT_RESULT(Result::OK, stream->setParameters({}))) + ASSERT_RESULT(Result::OK, stream->setParameters({}, {}))) TEST_IO_STREAM(setNonExistingParameter, "Set the values of an non existing parameter", // Unfortunately, the set_parameter legacy interface did not return any @@ -839,14 +883,14 @@ TEST_IO_STREAM(setNonExistingParameter, "Set the values of an non existing param // To allow implementation to just wrapped the legacy one, consider OK as a // valid result for setting a non existing parameter. ASSERT_RESULT(invalidArgsOrNotSupportedOrOK, - stream->setParameters({{"non existing key", "0"}}))) + stream->setParameters({}, {{"non existing key", "0"}}))) TEST_IO_STREAM(DebugDump, "Check that a stream can dump its state without error", - testDebugDump([this](const auto& handle) { return stream->debugDump(handle); })) + testDebugDump([this](const auto& handle) { return stream->debug(handle, {}); })) TEST_IO_STREAM(DebugDumpInvalidArguments, "Check that the stream dump doesn't crash on invalid arguments", - ASSERT_OK(stream->debugDump(hidl_handle()))) + ASSERT_OK(stream->debug(hidl_handle(), {}))) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// addRemoveEffect /////////////////////////////// @@ -1227,9 +1271,9 @@ TEST_F(AudioPrimaryHidlTest, setMode) { "Make sure setMode always succeeds if mode is valid " "and fails otherwise"); // Test Invalid values - for (AudioMode mode : {AudioMode::INVALID, AudioMode::CURRENT, AudioMode::CNT}) { - SCOPED_TRACE("mode=" + toString(mode)); - ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(mode)); + for (int mode : {-1, 0, int(AudioMode::IN_COMMUNICATION) + 1}) { + SCOPED_TRACE("mode=" + to_string(mode)); + ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(AudioMode(mode))); } // Test valid values for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE, diff --git a/audio/effect/4.0/vts/functional/Android.bp b/audio/effect/4.0/vts/functional/Android.bp index 86c0afc8b7..92b5db748d 100644 --- a/audio/effect/4.0/vts/functional/Android.bp +++ b/audio/effect/4.0/vts/functional/Android.bp @@ -23,8 +23,8 @@ cc_test { ], static_libs: [ "android.hardware.audio.common.test.utility", - "android.hardware.audio.common@2.0", - "android.hardware.audio.effect@2.0", + "android.hardware.audio.common@4.0", + "android.hardware.audio.effect@4.0", "android.hidl.allocator@1.0", "android.hidl.memory@1.0", "libxml2", @@ -33,4 +33,7 @@ cc_test { "libeffectsconfig", "libicuuc", ], + header_libs: [ + "android.hardware.audio.common.util@all-versions", + ], } diff --git a/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp index d0bc6908d6..6338563c2e 100644 --- a/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp +++ b/audio/effect/4.0/vts/functional/ValidateAudioEffectsConfiguration.cpp @@ -28,5 +28,5 @@ TEST(CheckConfig, audioEffectsConfigurationValidation) { std::vector locations(std::begin(DEFAULT_LOCATIONS), std::end(DEFAULT_LOCATIONS)); EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(DEFAULT_NAME, locations, - "/data/local/tmp/audio_effects_conf_V2_0.xsd"); + "/data/local/tmp/audio_effects_conf_V4_0.xsd"); } diff --git a/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp index c90c4fab2e..ec783c4bfa 100644 --- a/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp +++ b/audio/effect/4.0/vts/functional/VtsHalAudioEffectV4_0TargetTest.cpp @@ -18,33 +18,36 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include +#include + #include #include -using android::hardware::audio::common::V2_0::AudioDevice; -using android::hardware::audio::common::V2_0::AudioHandleConsts; -using android::hardware::audio::common::V2_0::AudioMode; -using android::hardware::audio::common::V2_0::AudioSource; -using android::hardware::audio::common::V2_0::Uuid; -using android::hardware::audio::effect::V2_0::AudioBuffer; -using android::hardware::audio::effect::V2_0::EffectAuxChannelsConfig; -using android::hardware::audio::effect::V2_0::EffectBufferConfig; -using android::hardware::audio::effect::V2_0::EffectConfig; -using android::hardware::audio::effect::V2_0::EffectDescriptor; -using android::hardware::audio::effect::V2_0::EffectOffloadParameter; -using android::hardware::audio::effect::V2_0::IEffect; -using android::hardware::audio::effect::V2_0::IEffectsFactory; -using android::hardware::audio::effect::V2_0::IEqualizerEffect; -using android::hardware::audio::effect::V2_0::ILoudnessEnhancerEffect; -using android::hardware::audio::effect::V2_0::Result; +using android::hardware::audio::common::V4_0::AudioDevice; +using android::hardware::audio::common::V4_0::AudioHandleConsts; +using android::hardware::audio::common::V4_0::AudioMode; +using android::hardware::audio::common::V4_0::AudioSource; +using android::hardware::audio::common::V4_0::Uuid; +using android::hardware::audio::common::utils::mkBitfield; +using android::hardware::audio::effect::V4_0::AudioBuffer; +using android::hardware::audio::effect::V4_0::EffectAuxChannelsConfig; +using android::hardware::audio::effect::V4_0::EffectBufferConfig; +using android::hardware::audio::effect::V4_0::EffectConfig; +using android::hardware::audio::effect::V4_0::EffectDescriptor; +using android::hardware::audio::effect::V4_0::EffectOffloadParameter; +using android::hardware::audio::effect::V4_0::IEffect; +using android::hardware::audio::effect::V4_0::IEffectsFactory; +using android::hardware::audio::effect::V4_0::IEqualizerEffect; +using android::hardware::audio::effect::V4_0::ILoudnessEnhancerEffect; +using android::hardware::audio::effect::V4_0::Result; using android::hardware::MQDescriptorSync; using android::hardware::Return; using android::hardware::Void; @@ -159,7 +162,7 @@ TEST_F(AudioEffectsFactoryHidlTest, GetDescriptor) { TEST_F(AudioEffectsFactoryHidlTest, DebugDumpInvalidArgument) { description("Verify that debugDump doesn't crash on invalid arguments"); - Return ret = effectsFactory->debugDump(hidl_handle()); + Return ret = effectsFactory->debug(hidl_handle(), {}); ASSERT_TRUE(ret.isOk()); } @@ -340,7 +343,7 @@ namespace android { namespace hardware { namespace audio { namespace effect { -namespace V2_0 { +namespace V4_0 { inline bool operator==(const AudioBuffer& lhs, const AudioBuffer& rhs) { return lhs.id == rhs.id && lhs.frameCount == rhs.frameCount && lhs.data.handle() == nullptr && rhs.data.handle() == nullptr; @@ -356,7 +359,7 @@ inline bool operator==(const EffectBufferConfig& lhs, inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) { return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg; } -} // namespace V2_0 +} // namespace V4_0 } // namespace effect } // namespace audio } // namespace hardware @@ -402,7 +405,7 @@ TEST_F(AudioEffectHidlTest, DisableEnableDisable) { TEST_F(AudioEffectHidlTest, SetDevice) { description("Verify that SetDevice works for an output chain effect"); - Return ret = effect->setDevice(AudioDevice::OUT_SPEAKER); + Return ret = effect->setDevice(mkBitfield(AudioDevice::OUT_SPEAKER)); EXPECT_TRUE(ret.isOk()); EXPECT_EQ(Result::OK, ret); } @@ -453,7 +456,7 @@ TEST_F(AudioEffectHidlTest, SetConfigReverse) { TEST_F(AudioEffectHidlTest, SetInputDevice) { description("Verify that SetInputDevice does not crash"); - Return ret = effect->setInputDevice(AudioDevice::IN_BUILTIN_MIC); + Return ret = effect->setInputDevice(mkBitfield(AudioDevice::IN_BUILTIN_MIC)); EXPECT_TRUE(ret.isOk()); } @@ -758,7 +761,7 @@ TEST_F(EqualizerAudioEffectHidlTest, GetSetAllProperties) { "Verify that setting band levels and presets works via Get / " "SetAllProperties for Equalizer effect"); using AllProperties = - android::hardware::audio::effect::V2_0::IEqualizerEffect::AllProperties; + android::hardware::audio::effect::V4_0::IEqualizerEffect::AllProperties; uint16_t numBands = 0; getNumBands(&numBands); ASSERT_GT(numBands, 0);