From aff8a1f8fdafad50e0dee8952c4c10d080587acf Mon Sep 17 00:00:00 2001 From: Lorena Torres-Huerta Date: Sat, 5 Nov 2022 01:28:15 +0000 Subject: [PATCH] VTS for IConfig getEngineConfig Bug: 242678729 Test: atest VtsHalAudioCoreTargetTest Change-Id: I051d0778bbad447e49dd4b99a2797333d0d67862 --- audio/aidl/vts/Android.bp | 3 +- .../vts/VtsHalAudioCoreConfigTargetTest.cpp | 333 ++++++++++++++++++ ...pp => VtsHalAudioCoreModuleTargetTest.cpp} | 44 ++- 3 files changed, 363 insertions(+), 17 deletions(-) create mode 100644 audio/aidl/vts/VtsHalAudioCoreConfigTargetTest.cpp rename audio/aidl/vts/{VtsHalAudioCoreTargetTest.cpp => VtsHalAudioCoreModuleTargetTest.cpp} (98%) diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index 03e9fcaeee..e207e22e33 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -49,7 +49,8 @@ cc_test { ], srcs: [ "ModuleConfig.cpp", - "VtsHalAudioCoreTargetTest.cpp", + "VtsHalAudioCoreConfigTargetTest.cpp", + "VtsHalAudioCoreModuleTargetTest.cpp", ], } diff --git a/audio/aidl/vts/VtsHalAudioCoreConfigTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreConfigTargetTest.cpp new file mode 100644 index 0000000000..bf73648454 --- /dev/null +++ b/audio/aidl/vts/VtsHalAudioCoreConfigTargetTest.cpp @@ -0,0 +1,333 @@ +#include +#include +#include +#include +#include + +#define LOG_TAG "VtsHalAudioCore.Config" + +#include +#include +#include +#include + +#include "AudioHalBinderServiceUtil.h" +#include "TestUtils.h" + +using namespace android; +using aidl::android::hardware::audio::core::IConfig; +using aidl::android::media::audio::common::AudioAttributes; +using aidl::android::media::audio::common::AudioFlag; +using aidl::android::media::audio::common::AudioHalAttributesGroup; +using aidl::android::media::audio::common::AudioHalCapCriterion; +using aidl::android::media::audio::common::AudioHalCapCriterionType; +using aidl::android::media::audio::common::AudioHalEngineConfig; +using aidl::android::media::audio::common::AudioHalProductStrategy; +using aidl::android::media::audio::common::AudioHalVolumeCurve; +using aidl::android::media::audio::common::AudioHalVolumeGroup; +using aidl::android::media::audio::common::AudioProductStrategyType; +using aidl::android::media::audio::common::AudioSource; +using aidl::android::media::audio::common::AudioStreamType; +using aidl::android::media::audio::common::AudioUsage; + +class AudioCoreConfig : public testing::TestWithParam { + public: + void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); } + void ConnectToService() { + mConfig = IConfig::fromBinder(mBinderUtil.connectToService(GetParam())); + ASSERT_NE(mConfig, nullptr); + } + + void RestartService() { + ASSERT_NE(mConfig, nullptr); + mEngineConfig.reset(); + mConfig = IConfig::fromBinder(mBinderUtil.restartService()); + ASSERT_NE(mConfig, nullptr); + } + + void SetUpEngineConfig() { + if (mEngineConfig == nullptr) { + auto tempConfig = std::make_unique(); + ASSERT_IS_OK(mConfig->getEngineConfig(tempConfig.get())); + mEngineConfig = std::move(tempConfig); + } + } + + static bool IsProductStrategyTypeReservedForSystemUse(const AudioProductStrategyType& pst) { + switch (pst) { + case AudioProductStrategyType::SYS_RESERVED_NONE: + case AudioProductStrategyType::SYS_RESERVED_REROUTING: + case AudioProductStrategyType::SYS_RESERVED_CALL_ASSISTANT: + return true; + default: + return false; + } + } + + static bool IsStreamTypeReservedForSystemUse(const AudioStreamType& streamType) { + switch (streamType) { + case AudioStreamType::SYS_RESERVED_DEFAULT: + case AudioStreamType::SYS_RESERVED_REROUTING: + case AudioStreamType::SYS_RESERVED_PATCH: + case AudioStreamType::CALL_ASSISTANT: + return true; + default: + return false; + } + } + + static bool IsAudioUsageValid(const AudioUsage& usage) { + switch (usage) { + case AudioUsage::INVALID: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED: + return false; + default: + return true; + } + } + + static bool IsAudioSourceValid(const AudioSource& source) { + return (source != AudioSource::SYS_RESERVED_INVALID); + } + + static const std::unordered_set& GetSupportedAudioProductStrategyTypes() { + static const std::unordered_set supportedAudioProductStrategyTypes = []() { + std::unordered_set supportedStrategyTypes; + for (const auto& audioProductStrategyType : + ndk::enum_range()) { + if (!IsProductStrategyTypeReservedForSystemUse(audioProductStrategyType)) { + supportedStrategyTypes.insert(static_cast(audioProductStrategyType)); + } + } + return supportedStrategyTypes; + }(); + return supportedAudioProductStrategyTypes; + } + + static int GetSupportedAudioFlagsMask() { + static const int supportedAudioFlagsMask = []() { + int mask = 0; + for (const auto& audioFlag : ndk::enum_range()) { + mask |= static_cast(audioFlag); + } + return mask; + }(); + return supportedAudioFlagsMask; + } + + /** + * Verify streamType is not INVALID if using default engine. + * Verify that streamType is a valid AudioStreamType if the associated + * volumeGroup minIndex/maxIndex is INDEX_DEFERRED_TO_AUDIO_SERVICE. + */ + void ValidateAudioStreamType(const AudioStreamType& streamType, + const AudioHalVolumeGroup& associatedVolumeGroup) { + EXPECT_FALSE(IsStreamTypeReservedForSystemUse(streamType)); + if (!mEngineConfig->capSpecificConfig || + associatedVolumeGroup.minIndex == + AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE) { + EXPECT_NE(streamType, AudioStreamType::INVALID); + } + } + + /** + * Verify contained enum types are valid. + */ + void ValidateAudioAttributes(const AudioAttributes& attributes) { + // No need to check contentType; there are no INVALID or SYS_RESERVED values + EXPECT_TRUE(IsAudioUsageValid(attributes.usage)); + EXPECT_TRUE(IsAudioSourceValid(attributes.source)); + EXPECT_EQ(attributes.flags & ~GetSupportedAudioFlagsMask(), 0); + } + + /** + * Verify volumeGroupName corresponds to an AudioHalVolumeGroup. + * Validate contained types. + */ + void ValidateAudioHalAttributesGroup( + const AudioHalAttributesGroup& attributesGroup, + std::unordered_map& volumeGroupMap, + std::unordered_set& volumeGroupsUsedInStrategies) { + bool isVolumeGroupNameValid = volumeGroupMap.count(attributesGroup.volumeGroupName); + EXPECT_TRUE(isVolumeGroupNameValid); + EXPECT_NO_FATAL_FAILURE(ValidateAudioStreamType( + attributesGroup.streamType, volumeGroupMap.at(attributesGroup.volumeGroupName))); + if (isVolumeGroupNameValid) { + volumeGroupsUsedInStrategies.insert(attributesGroup.volumeGroupName); + } + for (const AudioAttributes& attr : attributesGroup.attributes) { + EXPECT_NO_FATAL_FAILURE(ValidateAudioAttributes(attr)); + } + } + + /** + * Default engine: verify productStrategy.id is valid AudioProductStrategyType. + * CAP engine: verify productStrategy.id is either valid AudioProductStrategyType + * or is >= VENDOR_STRATEGY_ID_START. + * Validate contained types. + */ + void ValidateAudioHalProductStrategy( + const AudioHalProductStrategy& strategy, + std::unordered_map& volumeGroupMap, + std::unordered_set& volumeGroupsUsedInStrategies) { + if (!mEngineConfig->capSpecificConfig || + (strategy.id < AudioHalProductStrategy::VENDOR_STRATEGY_ID_START)) { + EXPECT_NE(GetSupportedAudioProductStrategyTypes().find(strategy.id), + GetSupportedAudioProductStrategyTypes().end()); + } + for (const AudioHalAttributesGroup& attributesGroup : strategy.attributesGroups) { + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalAttributesGroup(attributesGroup, volumeGroupMap, + volumeGroupsUsedInStrategies)); + } + } + + /** + * Verify curve point index is in [CurvePoint::MIN_INDEX, CurvePoint::MAX_INDEX]. + */ + void ValidateAudioHalVolumeCurve(const AudioHalVolumeCurve& volumeCurve) { + for (const AudioHalVolumeCurve::CurvePoint& curvePoint : volumeCurve.curvePoints) { + EXPECT_TRUE(curvePoint.index >= AudioHalVolumeCurve::CurvePoint::MIN_INDEX); + EXPECT_TRUE(curvePoint.index <= AudioHalVolumeCurve::CurvePoint::MAX_INDEX); + } + } + + /** + * Verify minIndex, maxIndex are non-negative. + * Verify minIndex <= maxIndex. + * Verify no two volume curves use the same device category. + * Validate contained types. + */ + void ValidateAudioHalVolumeGroup(const AudioHalVolumeGroup& volumeGroup) { + /** + * Legacy volume curves in audio_policy_configuration.xsd don't use + * minIndex or maxIndex. Use of audio_policy_configuration.xml still + * allows, and in some cases, relies on, AudioService to provide the min + * and max indices for a volumeGroup. From the VTS perspective, there is + * no way to differentiate between use of audio_policy_configuration.xml + * or audio_policy_engine_configuration.xml, as either one can be used + * for the default audio policy engine. + */ + if (volumeGroup.minIndex != AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE || + volumeGroup.maxIndex != AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE) { + EXPECT_TRUE(volumeGroup.minIndex >= 0); + EXPECT_TRUE(volumeGroup.maxIndex >= 0); + } + EXPECT_TRUE(volumeGroup.minIndex <= volumeGroup.maxIndex); + std::unordered_set deviceCategorySet; + for (const AudioHalVolumeCurve& volumeCurve : volumeGroup.volumeCurves) { + EXPECT_TRUE(deviceCategorySet.insert(volumeCurve.deviceCategory).second); + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalVolumeCurve(volumeCurve)); + } + } + + /** + * Verify defaultLiteralValue is empty for inclusive criterion. + */ + void ValidateAudioHalCapCriterion(const AudioHalCapCriterion& criterion, + const AudioHalCapCriterionType& criterionType) { + if (criterionType.isInclusive) { + EXPECT_TRUE(criterion.defaultLiteralValue.empty()); + } + } + + /** + * Verify values only contain alphanumeric characters. + */ + void ValidateAudioHalCapCriterionType(const AudioHalCapCriterionType& criterionType) { + auto isNotAlnum = [](const char& c) { return !isalnum(c); }; + for (const std::string& value : criterionType.values) { + EXPECT_EQ(find_if(value.begin(), value.end(), isNotAlnum), value.end()); + } + } + + /** + * Verify each criterionType has a unique name. + * Verify each criterion has a unique name. + * Verify each criterion maps to a criterionType. + * Verify each criterionType is used in a criterion. + * Validate contained types. + */ + void ValidateCapSpecificConfig(const AudioHalEngineConfig::CapSpecificConfig& capCfg) { + EXPECT_FALSE(capCfg.criteria.empty()); + EXPECT_FALSE(capCfg.criterionTypes.empty()); + std::unordered_map criterionTypeMap; + for (const AudioHalCapCriterionType& criterionType : capCfg.criterionTypes) { + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalCapCriterionType(criterionType)); + EXPECT_TRUE(criterionTypeMap.insert({criterionType.name, criterionType}).second); + } + std::unordered_set criterionNameSet; + for (const AudioHalCapCriterion& criterion : capCfg.criteria) { + EXPECT_TRUE(criterionNameSet.insert(criterion.name).second); + EXPECT_EQ(criterionTypeMap.count(criterion.criterionTypeName), 1UL); + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalCapCriterion( + criterion, criterionTypeMap.at(criterion.criterionTypeName))); + } + EXPECT_EQ(criterionTypeMap.size(), criterionNameSet.size()); + } + + /** + * Verify VolumeGroups are non-empty. + * Verify defaultProductStrategyId matches one of the provided productStrategies. + * Otherwise, must be left uninitialized. + * Verify each volumeGroup has a unique name. + * Verify each productStrategy has a unique id. + * Verify each volumeGroup is used in a product strategy. + * CAP engine: verify productStrategies are non-empty. + * Validate contained types. + */ + void ValidateAudioHalEngineConfig() { + EXPECT_NE(mEngineConfig->volumeGroups.size(), 0UL); + std::unordered_map volumeGroupMap; + for (const AudioHalVolumeGroup& volumeGroup : mEngineConfig->volumeGroups) { + EXPECT_TRUE(volumeGroupMap.insert({volumeGroup.name, volumeGroup}).second); + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalVolumeGroup(volumeGroup)); + } + if (!mEngineConfig->productStrategies.empty()) { + std::unordered_set productStrategyIdSet; + std::unordered_set volumeGroupsUsedInStrategies; + for (const AudioHalProductStrategy& strategy : mEngineConfig->productStrategies) { + EXPECT_TRUE(productStrategyIdSet.insert(strategy.id).second); + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalProductStrategy( + strategy, volumeGroupMap, volumeGroupsUsedInStrategies)); + } + EXPECT_TRUE(productStrategyIdSet.count(mEngineConfig->defaultProductStrategyId)) + << "defaultProductStrategyId doesn't match any of the provided " + "productStrategies"; + EXPECT_EQ(volumeGroupMap.size(), volumeGroupsUsedInStrategies.size()); + } else { + EXPECT_EQ(mEngineConfig->defaultProductStrategyId, + static_cast(AudioProductStrategyType::SYS_RESERVED_NONE)) + << "defaultProductStrategyId defined, but no productStrategies were provided"; + } + if (mEngineConfig->capSpecificConfig) { + EXPECT_NO_FATAL_FAILURE( + ValidateCapSpecificConfig(mEngineConfig->capSpecificConfig.value())); + EXPECT_FALSE(mEngineConfig->productStrategies.empty()); + } + } + + private: + std::shared_ptr mConfig; + std::unique_ptr mEngineConfig; + AudioHalBinderServiceUtil mBinderUtil; +}; + +TEST_P(AudioCoreConfig, Published) { + // SetUp must complete with no failures. +} + +TEST_P(AudioCoreConfig, CanBeRestarted) { + ASSERT_NO_FATAL_FAILURE(RestartService()); +} + +TEST_P(AudioCoreConfig, GetEngineConfigIsValid) { + ASSERT_NO_FATAL_FAILURE(SetUpEngineConfig()); + EXPECT_NO_FATAL_FAILURE(ValidateAudioHalEngineConfig()); +} + +INSTANTIATE_TEST_SUITE_P(AudioCoreConfigTest, AudioCoreConfig, + testing::ValuesIn(android::getAidlHalInstanceNames(IConfig::descriptor)), + android::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreConfig); diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp similarity index 98% rename from audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp rename to audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp index c0c04f42a5..eb7a3e43cd 100644 --- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp @@ -27,7 +27,7 @@ #include #include -#define LOG_TAG "VtsHalAudioCore" +#define LOG_TAG "VtsHalAudioCore.Module" #include #include @@ -490,16 +490,16 @@ class SmartStateSequence : public StateSequence { private: StreamDescriptor::State getState() const { return mSteps[mCurrentStep].second; } bool isBurstBifurcation() { - return getTrigger() == TransitionTrigger{kBurstCommand}&& getState() == - StreamDescriptor::State::TRANSFERRING; + return getTrigger() == TransitionTrigger{kBurstCommand} && + getState() == StreamDescriptor::State::TRANSFERRING; } bool isPauseBifurcation() { - return getTrigger() == TransitionTrigger{kPauseCommand}&& getState() == - StreamDescriptor::State::TRANSFER_PAUSED; + return getTrigger() == TransitionTrigger{kPauseCommand} && + getState() == StreamDescriptor::State::TRANSFER_PAUSED; } bool isStartBifurcation() { - return getTrigger() == TransitionTrigger{kStartCommand}&& getState() == - StreamDescriptor::State::TRANSFERRING; + return getTrigger() == TransitionTrigger{kStartCommand} && + getState() == StreamDescriptor::State::TRANSFERRING; } const std::vector mSteps; size_t mCurrentStep = 0; @@ -1902,9 +1902,13 @@ class AudioStream : public AudioCoreModule { using AudioStreamIn = AudioStream; using AudioStreamOut = AudioStream; -#define TEST_IN_AND_OUT_STREAM(method_name) \ - TEST_P(AudioStreamIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \ - TEST_P(AudioStreamOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } +#define TEST_IN_AND_OUT_STREAM(method_name) \ + TEST_P(AudioStreamIn, method_name) { \ + ASSERT_NO_FATAL_FAILURE(method_name()); \ + } \ + TEST_P(AudioStreamOut, method_name) { \ + ASSERT_NO_FATAL_FAILURE(method_name()); \ + } TEST_IN_AND_OUT_STREAM(CloseTwice); TEST_IN_AND_OUT_STREAM(OpenAllConfigs); @@ -2280,9 +2284,13 @@ class AudioStreamIo : public AudioCoreModuleBase, using AudioStreamIoIn = AudioStreamIo; using AudioStreamIoOut = AudioStreamIo; -#define TEST_IN_AND_OUT_STREAM_IO(method_name) \ - TEST_P(AudioStreamIoIn, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } \ - TEST_P(AudioStreamIoOut, method_name) { ASSERT_NO_FATAL_FAILURE(method_name()); } +#define TEST_IN_AND_OUT_STREAM_IO(method_name) \ + TEST_P(AudioStreamIoIn, method_name) { \ + ASSERT_NO_FATAL_FAILURE(method_name()); \ + } \ + TEST_P(AudioStreamIoOut, method_name) { \ + ASSERT_NO_FATAL_FAILURE(method_name()); \ + } TEST_IN_AND_OUT_STREAM_IO(Run); @@ -2435,9 +2443,13 @@ class AudioModulePatch : public AudioCoreModule { // Not all tests require both directions, so parametrization would require // more abstractions. -#define TEST_PATCH_BOTH_DIRECTIONS(method_name) \ - TEST_P(AudioModulePatch, method_name##Input) { ASSERT_NO_FATAL_FAILURE(method_name(true)); } \ - TEST_P(AudioModulePatch, method_name##Output) { ASSERT_NO_FATAL_FAILURE(method_name(false)); } +#define TEST_PATCH_BOTH_DIRECTIONS(method_name) \ + TEST_P(AudioModulePatch, method_name##Input) { \ + ASSERT_NO_FATAL_FAILURE(method_name(true)); \ + } \ + TEST_P(AudioModulePatch, method_name##Output) { \ + ASSERT_NO_FATAL_FAILURE(method_name(false)); \ + } TEST_PATCH_BOTH_DIRECTIONS(ResetPortConfigUsedByPatch); TEST_PATCH_BOTH_DIRECTIONS(SetInvalidPatch);