VTS for IConfig getEngineConfig
Bug: 242678729
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I051d0778bbad447e49dd4b99a2797333d0d67862
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 03e9fca..e207e22 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -49,7 +49,8 @@
],
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 0000000..bf73648
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioCoreConfigTargetTest.cpp
@@ -0,0 +1,333 @@
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#define LOG_TAG "VtsHalAudioCore.Config"
+
+#include <Utils.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/audio/core/IConfig.h>
+
+#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<std::string> {
+ 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<AudioHalEngineConfig>();
+ 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<int>& GetSupportedAudioProductStrategyTypes() {
+ static const std::unordered_set<int> supportedAudioProductStrategyTypes = []() {
+ std::unordered_set<int> supportedStrategyTypes;
+ for (const auto& audioProductStrategyType :
+ ndk::enum_range<AudioProductStrategyType>()) {
+ if (!IsProductStrategyTypeReservedForSystemUse(audioProductStrategyType)) {
+ supportedStrategyTypes.insert(static_cast<int>(audioProductStrategyType));
+ }
+ }
+ return supportedStrategyTypes;
+ }();
+ return supportedAudioProductStrategyTypes;
+ }
+
+ static int GetSupportedAudioFlagsMask() {
+ static const int supportedAudioFlagsMask = []() {
+ int mask = 0;
+ for (const auto& audioFlag : ndk::enum_range<AudioFlag>()) {
+ mask |= static_cast<int>(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<std::string, const AudioHalVolumeGroup&>& volumeGroupMap,
+ std::unordered_set<std::string>& 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<std::string, const AudioHalVolumeGroup&>& volumeGroupMap,
+ std::unordered_set<std::string>& 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<AudioHalVolumeCurve::DeviceCategory> 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<std::string, AudioHalCapCriterionType> criterionTypeMap;
+ for (const AudioHalCapCriterionType& criterionType : capCfg.criterionTypes) {
+ EXPECT_NO_FATAL_FAILURE(ValidateAudioHalCapCriterionType(criterionType));
+ EXPECT_TRUE(criterionTypeMap.insert({criterionType.name, criterionType}).second);
+ }
+ std::unordered_set<std::string> 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<std::string, const AudioHalVolumeGroup&> 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<int> productStrategyIdSet;
+ std::unordered_set<std::string> 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<int>(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<IConfig> mConfig;
+ std::unique_ptr<AudioHalEngineConfig> 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 c0c04f4..eb7a3e4 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -27,7 +27,7 @@
#include <variant>
#include <vector>
-#define LOG_TAG "VtsHalAudioCore"
+#define LOG_TAG "VtsHalAudioCore.Module"
#include <android-base/logging.h>
#include <StreamWorker.h>
@@ -490,16 +490,16 @@
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<StateTransition> mSteps;
size_t mCurrentStep = 0;
@@ -1902,9 +1902,13 @@
using AudioStreamIn = AudioStream<IStreamIn>;
using AudioStreamOut = AudioStream<IStreamOut>;
-#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 @@
using AudioStreamIoIn = AudioStreamIo<IStreamIn>;
using AudioStreamIoOut = AudioStreamIo<IStreamOut>;
-#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 @@
// 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);