/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "VtsHalAudioVTargetTest" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include PATH(android/hardware/audio/FILE_VERSION/IDevice.h) #include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h) #include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h) #include PATH(android/hardware/audio/FILE_VERSION/types.h) #include PATH(android/hardware/audio/common/FILE_VERSION/types.h) #if MAJOR_VERSION >= 7 #include #include #endif #include #include #include #include #include #include "utility/AssertOk.h" #include "utility/Documentation.h" #include "utility/ReturnIn.h" #include "utility/ValidateXml.h" #include "AudioTestDefinitions.h" /** Provide version specific functions that are used in the generic tests */ #if MAJOR_VERSION == 2 #include "2.0/AudioPrimaryHidlHalUtils.h" #elif MAJOR_VERSION >= 4 #include "4.0/AudioPrimaryHidlHalUtils.h" #endif using ::android::NO_INIT; using ::android::OK; using ::android::sp; using ::android::status_t; using ::android::hardware::EventFlag; using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_enum_range; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::IPCThreadState; using ::android::hardware::kSynchronizedReadWrite; using ::android::hardware::MessageQueue; using ::android::hardware::MQDescriptorSync; using ::android::hardware::Return; using ::android::hardware::audio::common::utils::EnumBitfield; using ::android::hardware::audio::common::utils::mkEnumBitfield; using ::android::hardware::details::toHexString; using namespace ::android::hardware::audio::common::CPP_VERSION; using namespace ::android::hardware::audio::common::test::utility; using namespace ::android::hardware::audio::CPP_VERSION; using ReadParameters = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadParameters; using ReadStatus = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadStatus; using WriteCommand = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteCommand; using WriteStatus = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteStatus; #if MAJOR_VERSION >= 7 // Make an alias for enumerations generated from the APM config XSD. namespace xsd { using namespace ::android::audio::policy::configuration::CPP_VERSION; } #endif // Typical accepted results from interface methods static auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED}; static auto okOrNotSupportedOrInvalidArgs = {Result::OK, Result::NOT_SUPPORTED, Result::INVALID_ARGUMENTS}; static auto okOrInvalidState = {Result::OK, Result::INVALID_STATE}; static auto okOrInvalidStateOrNotSupported = {Result::OK, Result::INVALID_STATE, Result::NOT_SUPPORTED}; static auto invalidArgsOrNotSupported = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED}; static auto invalidStateOrNotSupported = {Result::INVALID_STATE, Result::NOT_SUPPORTED}; #include "DeviceManager.h" #if MAJOR_VERSION <= 6 #include "PolicyConfig.h" #if MAJOR_VERSION == 6 #include "6.0/Generators.h" #endif #elif MAJOR_VERSION >= 7 #include "7.0/Generators.h" #include "7.0/PolicyConfig.h" #endif #include "StreamWorker.h" class HidlTest : public ::testing::Test { public: virtual ~HidlTest() = default; // public access to avoid annoyances when using this method in template classes // derived from test classes sp getDevice() const { return DeviceManager::getInstance().get(getFactoryName(), getDeviceName()); } protected: // Factory and device name getters to be overridden in subclasses. virtual const std::string& getFactoryName() const = 0; virtual const std::string& getDeviceName() const = 0; sp getDevicesFactory() const { return DevicesFactoryManager::getInstance().get(getFactoryName()); } bool resetDevice() const { return DeviceManager::getInstance().reset(getFactoryName(), getDeviceName()); } bool areAudioPatchesSupported() { return extract(getDevice()->supportsAudioPatches()); } // Convenient member to store results Result res; }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////// Audio policy configuration //////////////////////// ////////////////////////////////////////////////////////////////////////////// // Stringify the argument. #define QUOTE(x) #x #define STRINGIFY(x) QUOTE(x) static constexpr char kConfigFileName[] = "audio_policy_configuration.xml"; // Cached policy config after parsing for faster test startup const PolicyConfig& getCachedPolicyConfig() { static std::unique_ptr policyConfig = [] { auto config = std::make_unique(kConfigFileName); return config; }(); return *policyConfig; } TEST(CheckConfig, audioPolicyConfigurationValidation) { const auto factories = ::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor); if (factories.size() == 0) { GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no factory instances " "are found."; } RecordProperty("description", "Verify that the audio policy configuration file " "is valid according to the schema"); const char* xsd = "/data/local/tmp/audio_policy_configuration_" STRINGIFY(CPP_VERSION) ".xsd"; EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(kConfigFileName, android::audio_get_configuration_paths(), xsd); } ////////////////////////////////////////////////////////////////////////////// //////////////////// Test parameter types and definitions //////////////////// ////////////////////////////////////////////////////////////////////////////// static inline std::string DeviceParameterToString( const ::testing::TestParamInfo& info) { const auto& deviceName = std::get(info.param); const auto factoryName = ::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo{ std::get(info.param), info.index}); return !deviceName.empty() ? factoryName + "_" + deviceName : factoryName; } const std::vector& getDeviceParameters() { static std::vector parameters = [] { std::vector result; const auto factories = ::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor); const auto devices = getCachedPolicyConfig().getModulesWithDevicesNames(); result.reserve(devices.size()); for (const auto& factoryName : factories) { for (const auto& deviceName : devices) { if (DeviceManager::getInstance().get(factoryName, deviceName) != nullptr) { result.emplace_back(factoryName, deviceName); } } } return result; }(); return parameters; } const std::vector& getDeviceParametersForFactoryTests() { static std::vector parameters = [] { std::vector result; const auto factories = ::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor); for (const auto& factoryName : factories) { result.emplace_back(factoryName, DeviceManager::getInstance().getPrimary(factoryName) != nullptr ? DeviceManager::kPrimaryDevice : ""); } return result; }(); return parameters; } const std::vector& getDeviceParametersForPrimaryDeviceTests() { static std::vector parameters = [] { std::vector result; const auto primary = std::find_if( getDeviceParameters().begin(), getDeviceParameters().end(), [](const auto& elem) { return std::get(elem) == DeviceManager::kPrimaryDevice; }); if (primary != getDeviceParameters().end()) result.push_back(*primary); return result; }(); return parameters; } class AudioHidlTestWithDeviceParameter : public HidlTest, public ::testing::WithParamInterface { protected: const std::string& getFactoryName() const override { return std::get(GetParam()); } const std::string& getDeviceName() const override { return std::get(GetParam()); } }; class AudioPolicyConfigTest : public AudioHidlTestWithDeviceParameter { public: void SetUp() override { ASSERT_NO_FATAL_FAILURE(AudioHidlTestWithDeviceParameter::SetUp()); // setup base auto& policyConfig = getCachedPolicyConfig(); ASSERT_EQ(0, policyConfig.getStatus()) << policyConfig.getError(); } }; TEST_P(AudioPolicyConfigTest, LoadAudioPolicyXMLConfiguration) { doc::test("Test parsing audio_policy_configuration.xml (called in SetUp)"); } TEST_P(AudioPolicyConfigTest, HasPrimaryModule) { auto& policyConfig = getCachedPolicyConfig(); ASSERT_TRUE(policyConfig.getPrimaryModule() != nullptr) << "Could not find primary module in configuration file: " << policyConfig.getFilePath(); } INSTANTIATE_TEST_CASE_P(AudioHidl, AudioPolicyConfigTest, ::testing::ValuesIn(getDeviceParametersForFactoryTests()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPolicyConfigTest); ////////////////////////////////////////////////////////////////////////////// ////////////////////// getService audio_devices_factory ////////////////////// ////////////////////////////////////////////////////////////////////////////// // Test audio devices factory class AudioHidlTest : public AudioHidlTestWithDeviceParameter { public: void SetUp() override { ASSERT_NO_FATAL_FAILURE(AudioHidlTestWithDeviceParameter::SetUp()); // setup base ASSERT_TRUE(getDevicesFactory() != nullptr); } }; TEST_P(AudioHidlTest, GetAudioDevicesFactoryService) { doc::test("Test the getService"); } TEST_P(AudioHidlTest, OpenDeviceInvalidParameter) { doc::test("Test passing an invalid parameter to openDevice"); Result result; sp device; #if MAJOR_VERSION == 2 auto invalidDevice = IDevicesFactory::Device(-1); #elif MAJOR_VERSION >= 4 auto invalidDevice = "Non existing device"; #endif ASSERT_OK(getDevicesFactory()->openDevice(invalidDevice, returnIn(result, device))); ASSERT_EQ(Result::INVALID_ARGUMENTS, result); ASSERT_TRUE(device == nullptr); } INSTANTIATE_TEST_CASE_P(AudioHidl, AudioHidlTest, ::testing::ValuesIn(getDeviceParametersForFactoryTests()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioHidlTest); ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// openDevice /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Test all audio devices class AudioHidlDeviceTest : public AudioHidlTest { public: void SetUp() override { ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base ASSERT_TRUE(getDevice() != nullptr); } }; TEST_P(AudioHidlDeviceTest, OpenDevice) { doc::test("Test openDevice (called during setup)"); } TEST_P(AudioHidlDeviceTest, Init) { doc::test("Test that the audio hal initialized correctly"); ASSERT_OK(getDevice()->initCheck()); } INSTANTIATE_TEST_CASE_P(AudioHidlDevice, AudioHidlDeviceTest, ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioHidlDeviceTest); ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// openDevice primary /////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Test the primary device class AudioPrimaryHidlTest : public AudioHidlDeviceTest { public: void SetUp() override { ASSERT_NO_FATAL_FAILURE(AudioHidlDeviceTest::SetUp()); // setup base ASSERT_TRUE(getDevice() != nullptr); } // public access to avoid annoyances when using this method in template classes // derived from test classes sp getDevice() const { return DeviceManager::getInstance().getPrimary(getFactoryName()); } }; TEST_P(AudioPrimaryHidlTest, OpenPrimaryDevice) { doc::test("Test openPrimaryDevice (called during setup)"); } INSTANTIATE_TEST_CASE_P(AudioPrimaryHidl, AudioPrimaryHidlTest, ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPrimaryHidlTest); ////////////////////////////////////////////////////////////////////////////// ///////////////////// {set,get}{Master,Mic}{Mute,Volume} ///////////////////// ////////////////////////////////////////////////////////////////////////////// template class AccessorHidlTest : public BaseTestClass { protected: enum Optionality { REQUIRED, OPTIONAL }; struct Initial { // Initial property value Initial(Property value, Optionality check = REQUIRED) : value(value), check(check) {} Property value; Optionality check; // If this initial value should be checked }; using BaseTestClass::res; /** Test a property getter and setter. * The getter and/or the setter may return NOT_SUPPORTED if optionality == OPTIONAL. */ template void testAccessors(IUTGetter iutGetter, const std::string& propertyName, const Initial expectedInitial, std::list valuesToTest, Setter setter, Getter getter, const std::vector& invalidValues = {}) { const auto expectedResults = {Result::OK, optionality == OPTIONAL ? Result::NOT_SUPPORTED : Result::OK}; Property initialValue = expectedInitial.value; ASSERT_OK(((this->*iutGetter)().get()->*getter)(returnIn(res, initialValue))); ASSERT_RESULT(expectedResults, res); if (res == Result::OK && expectedInitial.check == REQUIRED) { EXPECT_EQ(expectedInitial.value, initialValue); } valuesToTest.push_front(expectedInitial.value); valuesToTest.push_back(initialValue); for (Property setValue : valuesToTest) { SCOPED_TRACE("Test " + propertyName + " getter and setter for " + testing::PrintToString(setValue)); auto ret = ((this->*iutGetter)().get()->*setter)(setValue); ASSERT_RESULT(expectedResults, ret); if (ret == Result::NOT_SUPPORTED) { doc::partialTest(propertyName + " setter is not supported"); break; } Property getValue; // Make sure the getter returns the same value just set ASSERT_OK(((this->*iutGetter)().get()->*getter)(returnIn(res, getValue))); ASSERT_RESULT(expectedResults, res); if (res == Result::NOT_SUPPORTED) { doc::partialTest(propertyName + " getter is not supported"); continue; } EXPECT_EQ(setValue, getValue); } for (Property invalidValue : invalidValues) { SCOPED_TRACE("Try to set " + propertyName + " with the invalid value " + testing::PrintToString(invalidValue)); EXPECT_RESULT(invalidArgsOrNotSupported, ((this->*iutGetter)().get()->*setter)(invalidValue)); } // Restore initial value EXPECT_RESULT(expectedResults, ((this->*iutGetter)().get()->*setter)(initialValue)); } template void testAccessors(const std::string& propertyName, const Initial expectedInitial, std::list valuesToTest, Setter setter, Getter getter, const std::vector& invalidValues = {}) { testAccessors(&BaseTestClass::getDevice, propertyName, expectedInitial, valuesToTest, setter, getter, invalidValues); } }; using BoolAccessorHidlTest = AccessorHidlTest; using BoolAccessorPrimaryHidlTest = AccessorHidlTest; TEST_P(BoolAccessorHidlTest, MicMuteTest) { doc::test("Check that the mic can be muted and unmuted"); testAccessors("mic mute", Initial{false}, {true}, &IDevice::setMicMute, &IDevice::getMicMute); // TODO: check that the mic is really muted (all sample are 0) } TEST_P(BoolAccessorHidlTest, MasterMuteTest) { doc::test("If master mute is supported, try to mute and unmute the master output"); testAccessors("master mute", Initial{false}, {true}, &IDevice::setMasterMute, &IDevice::getMasterMute); // TODO: check that the master volume is really muted } INSTANTIATE_TEST_CASE_P(BoolAccessorHidl, BoolAccessorHidlTest, ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString); INSTANTIATE_TEST_CASE_P(BoolAccessorPrimaryHidl, BoolAccessorPrimaryHidlTest, ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BoolAccessorHidlTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BoolAccessorPrimaryHidlTest); using FloatAccessorHidlTest = AccessorHidlTest; TEST_P(FloatAccessorHidlTest, MasterVolumeTest) { doc::test("Test the master volume if supported"); testAccessors( "master volume", Initial{1}, {0, 0.5}, &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 } INSTANTIATE_TEST_CASE_P(FloatAccessorHidl, FloatAccessorHidlTest, ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FloatAccessorHidlTest); ////////////////////////////////////////////////////////////////////////////// //////////////////////////////// AudioPatches //////////////////////////////// ////////////////////////////////////////////////////////////////////////////// class AudioPatchHidlTest : public AudioHidlDeviceTest { public: void SetUp() override { ASSERT_NO_FATAL_FAILURE(AudioHidlDeviceTest::SetUp()); // setup base if (!areAudioPatchesSupported()) { GTEST_SKIP() << "Audio patches are not supported"; } } }; TEST_P(AudioPatchHidlTest, AudioPatches) { doc::test("Test if audio patches are supported"); // TODO: test audio patches } INSTANTIATE_TEST_CASE_P(AudioPatchHidl, AudioPatchHidlTest, ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPatchHidlTest); #if MAJOR_VERSION >= 4 static std::string SanitizeStringForGTestName(const std::string& s) { std::string result = s; for (size_t i = 0; i < result.size(); i++) { // gtest test names must only contain alphanumeric characters if (!std::isalnum(result[i])) result[i] = '_'; } return result; } #endif /** 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 std::string DeviceConfigParameterToString( const testing::TestParamInfo& info) { const AudioConfig& config = std::get(info.param); const auto deviceName = DeviceParameterToString(::testing::TestParamInfo{ std::get(info.param), info.index}); const auto devicePart = (deviceName.empty() ? "" : deviceName + "_") + std::to_string(info.index); // The types had changed a lot between versions 2, 4..6 and 7. Use separate // code sections for easier understanding. #if MAJOR_VERSION == 2 const auto configPart = std::to_string(config.sampleRateHz) + "_" + // "MONO" is more clear than "FRONT_LEFT" (config.channelMask == AudioChannelMask::OUT_MONO || config.channelMask == AudioChannelMask::IN_MONO ? "MONO" : ::testing::PrintToString(config.channelMask)) + "_" + std::visit([](auto&& arg) -> std::string { return ::testing::PrintToString(arg); }, std::get(info.param)); #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6 const auto configPart = std::to_string(config.sampleRateHz) + "_" + // "MONO" is more clear than "FRONT_LEFT" (config.channelMask == mkEnumBitfield(AudioChannelMask::OUT_MONO) || config.channelMask == mkEnumBitfield(AudioChannelMask::IN_MONO) ? "MONO" // In V4 and above the channel mask is a bitfield. // Printing its value using HIDL's toString for a bitfield emits a lot of extra // text due to overlapping constant values. Instead, we print the bitfield // value as if it was a single value + its hex representation : SanitizeStringForGTestName( ::testing::PrintToString(AudioChannelMask(config.channelMask)) + "_" + toHexString(config.channelMask))) + "_" + SanitizeStringForGTestName(std::visit( [](auto&& arg) -> std::string { using T = std::decay_t; // Need to use FQN of toString to avoid confusing the compiler return ::android::hardware::audio::common::CPP_VERSION::toString( hidl_bitfield(arg)); }, std::get(info.param))); #elif MAJOR_VERSION >= 7 const auto configPart = std::to_string(config.base.sampleRateHz) + "_" + // The channel masks and flags are vectors of strings, just need to sanitize them. SanitizeStringForGTestName(::testing::PrintToString(config.base.channelMask)) + "_" + SanitizeStringForGTestName(::testing::PrintToString(std::get(info.param))); #endif return devicePart + "__" + configPart; } class AudioHidlTestWithDeviceConfigParameter : public HidlTest, public ::testing::WithParamInterface { protected: void SetUp() override { ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base ASSERT_TRUE(getDevicesFactory() != nullptr); ASSERT_TRUE(getDevice() != nullptr); } const std::string& getFactoryName() const override { return std::get(std::get(GetParam())); } const std::string& getDeviceName() const override { return std::get(std::get(GetParam())); } const AudioConfig& getConfig() const { return std::get(GetParam()); } #if MAJOR_VERSION == 2 AudioInputFlag getInputFlags() const { return std::get(std::get(GetParam())); } AudioOutputFlag getOutputFlags() const { return std::get(std::get(GetParam())); } #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6 hidl_bitfield getInputFlags() const { return hidl_bitfield( std::get(std::get(GetParam()))); } hidl_bitfield getOutputFlags() const { return hidl_bitfield( std::get(std::get(GetParam()))); } #elif MAJOR_VERSION >= 7 hidl_vec getInputFlags() const { return std::get(GetParam()); } hidl_vec getOutputFlags() const { return std::get(GetParam()); } #endif }; #if MAJOR_VERSION <= 6 #define AUDIO_PRIMARY_HIDL_HAL_TEST #include "ConfigHelper.h" #undef AUDIO_PRIMARY_HIDL_HAL_TEST #endif ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// getInputBufferSize ///////////////////////////// ////////////////////////////////////////////////////////////////////////////// // FIXME: execute input test only if platform declares // android.hardware.microphone // how to get this value ? is it a property ??? class AudioCaptureConfigTest : public AudioHidlTestWithDeviceConfigParameter { protected: void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) { uint64_t bufferSize; ASSERT_OK(getDevice()->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 AudioCaptureConfigTest {}; TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) { doc::test( "Input buffer size must be retrievable for a format with required " "support."); inputBufferSizeTest(getConfig(), true); } // Test that the recommended capture config are supported or lead to a // INVALID_ARGUMENTS return class OptionalInputBufferSizeTest : public AudioCaptureConfigTest {}; TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) { doc::test( "Input buffer size should be retrievable for a format with recommended " "support."); inputBufferSizeTest(getConfig(), false); } #if MAJOR_VERSION <= 5 // For V2..5 test the primary device according to CDD requirements. INSTANTIATE_TEST_CASE_P( RequiredInputBufferSize, RequiredInputBufferSizeTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRequiredSupportCaptureAudioConfig()), ::testing::Values(AudioInputFlag::NONE)), &DeviceConfigParameterToString); INSTANTIATE_TEST_CASE_P( RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRecommendedSupportCaptureAudioConfig()), ::testing::Values(AudioInputFlag::NONE)), &DeviceConfigParameterToString); #elif MAJOR_VERSION >= 6 INSTANTIATE_TEST_CASE_P(SupportedInputBufferSize, RequiredInputBufferSizeTest, ::testing::ValuesIn(getInputDeviceConfigParameters()), &DeviceConfigParameterToString); #endif // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OptionalInputBufferSizeTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RequiredInputBufferSizeTest); ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// setScreenState /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// TEST_P(AudioHidlDeviceTest, setScreenState) { doc::test("Check that the hal can receive the screen state"); for (bool turnedOn : {false, true, true, false, false}) { ASSERT_RESULT(okOrNotSupported, getDevice()->setScreenState(turnedOn)); } } ////////////////////////////////////////////////////////////////////////////// //////////////////////////// {get,set}Parameters ///////////////////////////// ////////////////////////////////////////////////////////////////////////////// TEST_P(AudioHidlDeviceTest, getParameters) { doc::test("Check that the hal can set and get parameters"); hidl_vec context; hidl_vec keys; hidl_vec values; ASSERT_OK(Parameters::get(getDevice(), keys, returnIn(res, values))); ASSERT_RESULT(okOrNotSupported, res); ASSERT_RESULT(okOrNotSupported, Parameters::set(getDevice(), values)); values.resize(0); ASSERT_RESULT(okOrNotSupported, Parameters::set(getDevice(), 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_P(AudioHidlDeviceTest, DebugDump) { doc::test("Check that the hal can dump its state without error"); testDebugDump([this](const auto& handle) { return dump(getDevice(), handle); }); } TEST_P(AudioHidlDeviceTest, DebugDumpInvalidArguments) { doc::test("Check that the hal dump doesn't crash on invalid arguments"); ASSERT_OK(dump(getDevice(), hidl_handle())); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////// open{Output,Input}Stream ////////////////////////// ////////////////////////////////////////////////////////////////////////////// static inline AudioIoHandle getNextIoHandle() { static AudioIoHandle lastHandle{}; return ++lastHandle; } // This class is also used by some device tests. template class StreamHelper { public: // StreamHelper doesn't own the stream, this is for simpler stream lifetime management. explicit StreamHelper(sp& stream) : mStream(stream) {} template void open(Open openStream, const AudioConfig& config, Result* res, AudioConfig* suggestedConfigPtr) { AudioConfig suggestedConfig{}; bool retryWithSuggestedConfig = true; if (suggestedConfigPtr == nullptr) { suggestedConfigPtr = &suggestedConfig; retryWithSuggestedConfig = false; } ASSERT_OK(openStream(mIoHandle, config, returnIn(*res, mStream, *suggestedConfigPtr))); switch (*res) { case Result::OK: ASSERT_TRUE(mStream != nullptr); *suggestedConfigPtr = config; break; case Result::INVALID_ARGUMENTS: ASSERT_TRUE(mStream == nullptr); if (retryWithSuggestedConfig) { AudioConfig suggestedConfigRetry; ASSERT_OK(openStream(mIoHandle, *suggestedConfigPtr, returnIn(*res, mStream, suggestedConfigRetry))); ASSERT_OK(*res); ASSERT_TRUE(mStream != nullptr); } break; default: FAIL() << "Invalid return status: " << ::testing::PrintToString(*res); } } void close(bool clear, Result* res) { auto ret = mStream->close(); EXPECT_TRUE(ret.isOk()); *res = ret; if (clear) { mStream.clear(); #if MAJOR_VERSION <= 5 // FIXME: there is no way to know when the remote IStream is being destroyed // Binder does not support testing if an object is alive, thus // wait for 100ms to let the binder destruction propagates and // the remote device has the time to be destroyed. // flushCommand makes sure all local command are sent, thus should reduce // the latency between local and remote destruction. IPCThreadState::self()->flushCommands(); usleep(100 * 1000); #endif } } AudioIoHandle getIoHandle() const { return mIoHandle; } private: const AudioIoHandle mIoHandle = getNextIoHandle(); sp& mStream; }; template class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter { public: // public access to avoid annoyances when using this method in template classes // derived from test classes sp getStream() const { return stream; } protected: OpenStreamTest() : AudioHidlTestWithDeviceConfigParameter(), helper(stream) {} template void testOpen(Open openStream, const AudioConfig& config) { // TODO: only allow failure for RecommendedPlaybackAudioConfig ASSERT_NO_FATAL_FAILURE(helper.open(openStream, config, &res, &audioConfig)); open = true; } Result closeStream(bool clear = true) { open = false; helper.close(clear, &res); return res; } void TearDown() override { if (open) { ASSERT_OK(closeStream()); } AudioHidlTestWithDeviceConfigParameter::TearDown(); } protected: AudioConfig audioConfig; DeviceAddress address = {}; sp stream; StreamHelper helper; bool open = false; }; ////////////////////////////// openOutputStream ////////////////////////////// class StreamWriter : public StreamWorker { public: StreamWriter(IStreamOut* stream, size_t bufferSize) : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {} ~StreamWriter() { stop(); if (mEfGroup) { EventFlag::deleteEventFlag(&mEfGroup); } } typedef MessageQueue CommandMQ; typedef MessageQueue DataMQ; typedef MessageQueue StatusMQ; bool workerInit() { std::unique_ptr tempCommandMQ; std::unique_ptr tempDataMQ; std::unique_ptr tempStatusMQ; Result retval; Return ret = mStream->prepareForWriting( 1, mBufferSize, [&](Result r, const CommandMQ::Descriptor& commandMQ, const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ, const auto& /*halThreadInfo*/) { retval = r; if (retval == Result::OK) { tempCommandMQ.reset(new CommandMQ(commandMQ)); tempDataMQ.reset(new DataMQ(dataMQ)); tempStatusMQ.reset(new StatusMQ(statusMQ)); if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); } } }); if (!ret.isOk()) { ALOGE("Transport error while calling prepareForWriting: %s", ret.description().c_str()); return false; } if (retval != Result::OK) { ALOGE("Error from prepareForWriting: %d", retval); return false; } if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() || !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) { ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing"); ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), "Command message queue for writing is invalid"); ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing"); ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid"); ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing"); ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), "Status message queue for writing is invalid"); ALOGE_IF(!mEfGroup, "Event flag creation for writing failed"); return false; } mCommandMQ = std::move(tempCommandMQ); mDataMQ = std::move(tempDataMQ); mStatusMQ = std::move(tempStatusMQ); return true; } bool workerCycle() { WriteCommand cmd = WriteCommand::WRITE; if (!mCommandMQ->write(&cmd)) { ALOGE("command message queue write failed"); return false; } const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite()); bool success = mDataMQ->write(mData.data(), dataSize); ALOGE_IF(!success, "data message queue write failed"); mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_EMPTY)); uint32_t efState = 0; retry: status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_FULL), &efState); if (efState & static_cast(MessageQueueFlagBits::NOT_FULL)) { WriteStatus writeStatus; writeStatus.retval = Result::NOT_INITIALIZED; if (!mStatusMQ->read(&writeStatus)) { ALOGE("status message read failed"); success = false; } if (writeStatus.retval != Result::OK) { ALOGE("bad write status: %d", writeStatus.retval); success = false; } } if (ret == -EAGAIN || ret == -EINTR) { // Spurious wakeup. This normally retries no more than once. goto retry; } else if (ret) { ALOGE("bad wait status: %d", ret); success = false; } return success; } private: IStreamOut* const mStream; const size_t mBufferSize; std::vector mData; std::unique_ptr mCommandMQ; std::unique_ptr mDataMQ; std::unique_ptr mStatusMQ; EventFlag* mEfGroup = nullptr; }; class OutputStreamTest : public OpenStreamTest { void SetUp() override { ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base #if MAJOR_VERSION <= 6 address.device = AudioDevice::OUT_DEFAULT; #elif MAJOR_VERSION >= 7 address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT); #endif const AudioConfig& config = getConfig(); auto flags = getOutputFlags(); testOpen( [&](AudioIoHandle handle, AudioConfig config, auto cb) { #if MAJOR_VERSION == 2 return getDevice()->openOutputStream(handle, address, config, flags, cb); #elif MAJOR_VERSION >= 4 return getDevice()->openOutputStream(handle, address, config, flags, initMetadata, cb); #endif }, config); } #if MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6 protected: const SourceMetadata initMetadata = { { { AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */ } }}; #elif MAJOR_VERSION >= 7 protected: const SourceMetadata initMetadata = { { { toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA), toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC), 1 /* gain */, toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), {} } }}; #endif }; TEST_P(OutputStreamTest, OpenOutputStreamTest) { doc::test( "Check that output streams can be open with the required and " "recommended config"); // Open done in SetUp } #if MAJOR_VERSION <= 5 // For V2..5 test the primary device according to CDD requirements. INSTANTIATE_TEST_CASE_P( RequiredOutputStreamConfigSupport, OutputStreamTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRequiredSupportPlaybackAudioConfig()), ::testing::Values(AudioOutputFlag::NONE)), &DeviceConfigParameterToString); INSTANTIATE_TEST_CASE_P( RecommendedOutputStreamConfigSupport, OutputStreamTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRecommendedSupportPlaybackAudioConfig()), ::testing::Values(AudioOutputFlag::NONE)), &DeviceConfigParameterToString); #elif MAJOR_VERSION >= 6 // For V6 and above test according to the audio policy manager configuration. // This is more correct as CDD is written from the apps perspective. // Audio system provides necessary format conversions for missing configurations. INSTANTIATE_TEST_CASE_P(DeclaredOutputStreamConfigSupport, OutputStreamTest, ::testing::ValuesIn(getOutputDeviceConfigParameters()), &DeviceConfigParameterToString); #endif // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamTest); ////////////////////////////// openInputStream ////////////////////////////// class StreamReader : public StreamWorker { public: StreamReader(IStreamIn* stream, size_t bufferSize) : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {} ~StreamReader() { stop(); if (mEfGroup) { EventFlag::deleteEventFlag(&mEfGroup); } } typedef MessageQueue CommandMQ; typedef MessageQueue DataMQ; typedef MessageQueue StatusMQ; bool workerInit() { std::unique_ptr tempCommandMQ; std::unique_ptr tempDataMQ; std::unique_ptr tempStatusMQ; Result retval; Return ret = mStream->prepareForReading( 1, mBufferSize, [&](Result r, const CommandMQ::Descriptor& commandMQ, const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ, const auto& /*halThreadInfo*/) { retval = r; if (retval == Result::OK) { tempCommandMQ.reset(new CommandMQ(commandMQ)); tempDataMQ.reset(new DataMQ(dataMQ)); tempStatusMQ.reset(new StatusMQ(statusMQ)); if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); } } }); if (!ret.isOk()) { ALOGE("Transport error while calling prepareForReading: %s", ret.description().c_str()); return false; } if (retval != Result::OK) { ALOGE("Error from prepareForReading: %d", retval); return false; } if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() || !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) { ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for reading"); ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(), "Command message queue for reading is invalid"); ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading"); ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid"); ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading"); ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), "Status message queue for reading is invalid"); ALOGE_IF(!mEfGroup, "Event flag creation for reading failed"); return false; } mCommandMQ = std::move(tempCommandMQ); mDataMQ = std::move(tempDataMQ); mStatusMQ = std::move(tempStatusMQ); return true; } bool workerCycle() { ReadParameters params; params.command = IStreamIn::ReadCommand::READ; params.params.read = mBufferSize; if (!mCommandMQ->write(¶ms)) { ALOGE("command message queue write failed"); return false; } mEfGroup->wake(static_cast(MessageQueueFlagBits::NOT_FULL)); uint32_t efState = 0; bool success = true; retry: status_t ret = mEfGroup->wait(static_cast(MessageQueueFlagBits::NOT_EMPTY), &efState); if (efState & static_cast(MessageQueueFlagBits::NOT_EMPTY)) { ReadStatus readStatus; readStatus.retval = Result::NOT_INITIALIZED; if (!mStatusMQ->read(&readStatus)) { ALOGE("status message read failed"); success = false; } if (readStatus.retval != Result::OK) { ALOGE("bad read status: %d", readStatus.retval); success = false; } const size_t dataSize = std::min(mData.size(), mDataMQ->availableToRead()); if (!mDataMQ->read(mData.data(), dataSize)) { ALOGE("data message queue read failed"); success = false; } } if (ret == -EAGAIN || ret == -EINTR) { // Spurious wakeup. This normally retries no more than once. goto retry; } else if (ret) { ALOGE("bad wait status: %d", ret); success = false; } return success; } private: IStreamIn* const mStream; const size_t mBufferSize; std::vector mData; std::unique_ptr mCommandMQ; std::unique_ptr mDataMQ; std::unique_ptr mStatusMQ; EventFlag* mEfGroup = nullptr; }; class InputStreamTest : public OpenStreamTest { void SetUp() override { ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base #if MAJOR_VERSION <= 6 address.device = AudioDevice::IN_DEFAULT; #elif MAJOR_VERSION >= 7 auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort( getDeviceName(), getMixPortName()); if (maybeSourceAddress.has_value() && !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) { address = maybeSourceAddress.value(); auto& metadata = initMetadata.tracks[0]; metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED); metadata.channelMask = getConfig().base.channelMask; } else { address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT); } #endif const AudioConfig& config = getConfig(); auto flags = getInputFlags(); testOpen( [&](AudioIoHandle handle, AudioConfig config, auto cb) { return getDevice()->openInputStream(handle, address, config, flags, initMetadata, cb); }, config); } protected: #if MAJOR_VERSION == 2 const AudioSource initMetadata = AudioSource::DEFAULT; #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6 const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }}; #elif MAJOR_VERSION >= 7 const std::string& getMixPortName() const { return std::get(GetParam()); } SinkMetadata initMetadata = { {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1, .tags = {}, .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}}; #endif }; TEST_P(InputStreamTest, OpenInputStreamTest) { doc::test( "Check that input streams can be open with the required and " "recommended config"); // Open done in setup } #if MAJOR_VERSION <= 5 // For V2..5 test the primary device according to CDD requirements. INSTANTIATE_TEST_CASE_P( RequiredInputStreamConfigSupport, InputStreamTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRequiredSupportCaptureAudioConfig()), ::testing::Values(AudioInputFlag::NONE)), &DeviceConfigParameterToString); INSTANTIATE_TEST_CASE_P( RecommendedInputStreamConfigSupport, InputStreamTest, ::testing::Combine( ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), ::testing::ValuesIn(ConfigHelper::getRecommendedSupportCaptureAudioConfig()), ::testing::Values(AudioInputFlag::NONE)), &DeviceConfigParameterToString); #elif MAJOR_VERSION >= 6 // For V6 and above test according to the audio policy manager configuration. // This is more correct as CDD is written from the apps perspective. // Audio system provides necessary format conversions for missing configurations. INSTANTIATE_TEST_CASE_P(DeclaredInputStreamConfigSupport, InputStreamTest, ::testing::ValuesIn(getInputDeviceConfigParameters()), &DeviceConfigParameterToString); #endif // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamTest); ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// IStream getters /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /* 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 getting stream frame count does not crash the HAL.", ASSERT_TRUE(stream->getFrameCount().isOk())) #if MAJOR_VERSION <= 6 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()))) #endif // 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 std::string& name, IStream* stream, CapabilityGetter capabilityGetter, Return (IStream::*getter)(), Return (IStream::*setter)(Property), bool currentMustBeSupported = true) { hidl_vec capabilities; auto ret = capabilityGetter(stream, capabilities); ASSERT_RESULT(okOrNotSupported, ret); bool notSupported = ret == Result::NOT_SUPPORTED; if (notSupported) { doc::partialTest(name + " is not supported"); return; }; if (currentMustBeSupported) { ASSERT_NE(0U, capabilities.size()) << name << " must not return an empty list"; Property currentValue = extract((stream->*getter)()); EXPECT_TRUE(std::find(capabilities.begin(), capabilities.end(), currentValue) != capabilities.end()) << "value returned by " << name << "() = " << testing::PrintToString(currentValue) << " 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)())); } } #if MAJOR_VERSION <= 6 TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declared as supported", testCapabilityGetter("getSupportedSampleRate", stream.get(), &GetSupported::sampleRates, &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(), &GetSupported::channelMasks, &IStream::getChannelMask, &IStream::setChannelMask)) TEST_IO_STREAM(SupportedFormat, "Check that the stream format is declared as supported", testCapabilityGetter("getSupportedFormat", stream.get(), &GetSupported::formats, &IStream::getFormat, &IStream::setFormat)) #else static void testGetSupportedProfiles(IStream* stream) { Result res; hidl_vec profiles; auto ret = stream->getSupportedProfiles(returnIn(res, profiles)); EXPECT_TRUE(ret.isOk()); if (res == Result::OK) { EXPECT_GT(profiles.size(), 0); } else { EXPECT_EQ(Result::NOT_SUPPORTED, res); } } TEST_IO_STREAM(GetSupportedProfiles, "Try to call optional method GetSupportedProfiles", testGetSupportedProfiles(stream.get())) static void testSetAudioProperties(IStream* stream) { Result res; hidl_vec profiles; auto ret = stream->getSupportedProfiles(returnIn(res, profiles)); EXPECT_TRUE(ret.isOk()); if (res == Result::NOT_SUPPORTED) { GTEST_SKIP() << "Retrieving supported profiles is not implemented"; } for (const auto& profile : profiles) { for (const auto& sampleRate : profile.sampleRates) { for (const auto& channelMask : profile.channelMasks) { AudioConfigBaseOptional config; config.format.value(profile.format); config.sampleRateHz.value(sampleRate); config.channelMask.value(channelMask); auto ret = stream->setAudioProperties(config); EXPECT_TRUE(ret.isOk()); if (ret == Result::NOT_SUPPORTED) { GTEST_SKIP() << "setAudioProperties is not supported"; } EXPECT_EQ(Result::OK, ret) << profile.format << "; " << sampleRate << "; " << channelMask; } } } } TEST_IO_STREAM(SetAudioProperties, "Call setAudioProperties for all supported profiles", testSetAudioProperties(stream.get())) #endif // MAJOR_VERSION <= 6 static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) { #if MAJOR_VERSION <= 6 uint32_t sampleRateHz; auto mask = mkEnumBitfield({}); AudioFormat format; auto ret = stream->getAudioProperties(returnIn(sampleRateHz, mask, format)); EXPECT_TRUE(ret.isOk()); // 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); #elif MAJOR_VERSION >= 7 Result res; AudioConfigBase actualConfig{}; auto ret = stream->getAudioProperties(returnIn(res, actualConfig)); EXPECT_TRUE(ret.isOk()); EXPECT_EQ(Result::OK, res); EXPECT_EQ(expectedConfig.base.sampleRateHz, actualConfig.sampleRateHz); EXPECT_EQ(expectedConfig.base.channelMask, actualConfig.channelMask); EXPECT_EQ(expectedConfig.base.format, actualConfig.format); #endif } TEST_IO_STREAM(GetAudioProperties, "Check that the stream audio properties == the ones it was opened with", testGetAudioProperties(stream.get(), audioConfig)) TEST_IO_STREAM(SetHwAvSync, "Try to set hardware sync to an invalid value", ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, stream->setHwAvSync(666))) static void checkGetNoParameter(IStream* stream, hidl_vec keys, std::initializer_list expectedResults) { hidl_vec parameters; Result res; ASSERT_OK(Parameters::get(stream, 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, Parameters::set(stream, {}))) 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(okOrNotSupportedOrInvalidArgs, Parameters::set(stream, {{"non existing key", "0"}}))) TEST_IO_STREAM(DebugDump, "Check that a stream can dump its state without error", testDebugDump([this](const auto& handle) { return dump(stream, handle); })) TEST_IO_STREAM(DebugDumpInvalidArguments, "Check that the stream dump doesn't crash on invalid arguments", ASSERT_OK(dump(stream, 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 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())) // clang-format off TEST_IO_STREAM(closeTwice, "Make sure a stream can not be closed twice", ASSERT_OK(closeStream(false /*clear*/)); ASSERT_EQ(Result::INVALID_STATE, closeStream())) // clang-format on static void testMmapBufferOfInvalidSize(IStream* stream) { for (int32_t value : {-1, 0, std::numeric_limits::max()}) { MmapBufferInfo info; Result res; EXPECT_OK(stream->createMmapBuffer(value, returnIn(res, info))); EXPECT_RESULT(invalidArgsOrNotSupported, res) << "value=" << value; } } TEST_IO_STREAM(CreateTooBigMmapBuffer, "Create mmap buffer of invalid size must fail", testMmapBufferOfInvalidSize(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); #if MAJOR_VERSION <= 6 ASSERT_EQ(AudioSource::DEFAULT, source); #elif MAJOR_VERSION >= 7 ASSERT_EQ(xsd::AudioSource::AUDIO_SOURCE_DEFAULT, xsd::stringToAudioSource(source)); #endif } 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, std::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 or 0"); uint64_t frames; uint64_t time; ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, time))); // Although 'getCapturePosition' is mandatory in V7, legacy implementations // may return -ENOSYS (which is translated to NOT_SUPPORTED) in cases when // the capture position can't be retrieved, e.g. when the stream isn't // running. Because of this, we don't fail when getting NOT_SUPPORTED // in this test. Behavior of 'getCapturePosition' for running streams is // tested in 'PcmOnlyConfigInputStreamTest' for V7. ASSERT_RESULT(okOrInvalidStateOrNotSupported, res); if (res == Result::OK) { ASSERT_EQ(0U, frames); ASSERT_LE(0U, time); } } ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// StreamOut ////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// 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))); drain = extract(stream->supportsDrain()); } 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()); } 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; } expectValueOrFailure(res, 0U, dspFrames, Result::INVALID_STATE); } 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; } expectValueOrFailure(res, uint64_t{0}, timestampUs, Result::INVALID_STATE); } /** 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 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->pause()); } 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 measureTS; ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, measureTS))); #if MAJOR_VERSION <= 6 if (res == Result::NOT_SUPPORTED) { doc::partialTest("getPresentationPosition is not supported"); return; } #else ASSERT_NE(Result::NOT_SUPPORTED, res) << "getPresentationPosition is mandatory in V7"; #endif ASSERT_EQ(0U, frames); if (measureTS.tvNSec == 0 && measureTS.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 measureTime = toMicroSec(measureTS.tvSec, measureTS.tvNSec); ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, measureTime); } ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// PrimaryDevice //////////////////////////////// ////////////////////////////////////////////////////////////////////////////// TEST_P(AudioPrimaryHidlTest, setVoiceVolume) { doc::test("Make sure setVoiceVolume only succeed if volume is in [0,1]"); testUnitaryGain([this](float volume) { return getDevice()->setVoiceVolume(volume); }); } TEST_P(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) { doc::test("Query and set the BT SCO NR&EC state"); testAccessors("BtScoNrecEnabled", Initial{false, OPTIONAL}, {true}, &IPrimaryDevice::setBtScoNrecEnabled, &IPrimaryDevice::getBtScoNrecEnabled); } TEST_P(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) { doc::test("Query and set the SCO whideband state"); testAccessors("BtScoWideband", Initial{false, OPTIONAL}, {true}, &IPrimaryDevice::setBtScoWidebandEnabled, &IPrimaryDevice::getBtScoWidebandEnabled); } using TtyModeAccessorPrimaryHidlTest = AccessorHidlTest; TEST_P(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) { doc::test("Query and set the TTY mode state"); testAccessors( "TTY mode", Initial{IPrimaryDevice::TtyMode::OFF}, {IPrimaryDevice::TtyMode::HCO, IPrimaryDevice::TtyMode::VCO, IPrimaryDevice::TtyMode::FULL}, &IPrimaryDevice::setTtyMode, &IPrimaryDevice::getTtyMode); } INSTANTIATE_TEST_CASE_P(TtyModeAccessorPrimaryHidl, TtyModeAccessorPrimaryHidlTest, ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()), &DeviceParameterToString); // When the VTS test runs on a device lacking the corresponding HAL version the parameter // list is empty, this isn't a problem. GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TtyModeAccessorPrimaryHidlTest); TEST_P(BoolAccessorPrimaryHidlTest, setGetHac) { doc::test("Query and set the HAC state"); testAccessors("HAC", Initial{false}, {true}, &IPrimaryDevice::setHacEnabled, &IPrimaryDevice::getHacEnabled); }