1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <set>
18 #include <string>
19 #include <unordered_map>
20 #include <unordered_set>
21 #include <vector>
22
23 #define LOG_TAG "VtsHalAudioCore.Config"
24
25 #include <Utils.h>
26 #include <aidl/Gtest.h>
27 #include <aidl/Vintf.h>
28 #include <aidl/android/hardware/audio/core/IConfig.h>
29 #include <aidl/android/media/audio/common/AudioFlag.h>
30 #include <aidl/android/media/audio/common/AudioProductStrategyType.h>
31 #include <android-base/logging.h>
32
33 #include "AudioHalBinderServiceUtil.h"
34 #include "TestUtils.h"
35
36 using namespace android;
37 using aidl::android::hardware::audio::common::isDefaultAudioFormat;
38 using aidl::android::hardware::audio::core::IConfig;
39 using aidl::android::hardware::audio::core::SurroundSoundConfig;
40 using aidl::android::media::audio::common::AudioAttributes;
41 using aidl::android::media::audio::common::AudioFlag;
42 using aidl::android::media::audio::common::AudioFormatDescription;
43 using aidl::android::media::audio::common::AudioFormatType;
44 using aidl::android::media::audio::common::AudioHalAttributesGroup;
45 using aidl::android::media::audio::common::AudioHalCapCriterion;
46 using aidl::android::media::audio::common::AudioHalCapCriterionType;
47 using aidl::android::media::audio::common::AudioHalEngineConfig;
48 using aidl::android::media::audio::common::AudioHalProductStrategy;
49 using aidl::android::media::audio::common::AudioHalVolumeCurve;
50 using aidl::android::media::audio::common::AudioHalVolumeGroup;
51 using aidl::android::media::audio::common::AudioProductStrategyType;
52 using aidl::android::media::audio::common::AudioSource;
53 using aidl::android::media::audio::common::AudioStreamType;
54 using aidl::android::media::audio::common::AudioUsage;
55 using aidl::android::media::audio::common::PcmType;
56
57 class AudioCoreConfig : public testing::TestWithParam<std::string> {
58 public:
SetUp()59 void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); }
ConnectToService()60 void ConnectToService() {
61 mConfig = IConfig::fromBinder(mBinderUtil.connectToService(GetParam()));
62 ASSERT_NE(mConfig, nullptr);
63 }
64
RestartService()65 void RestartService() {
66 ASSERT_NE(mConfig, nullptr);
67 mEngineConfig.reset();
68 mSurroundSoundConfig.reset();
69 mConfig = IConfig::fromBinder(mBinderUtil.restartService());
70 ASSERT_NE(mConfig, nullptr);
71 }
72
SetUpEngineConfig()73 void SetUpEngineConfig() {
74 if (mEngineConfig == nullptr) {
75 auto tempConfig = std::make_unique<AudioHalEngineConfig>();
76 ASSERT_IS_OK(mConfig->getEngineConfig(tempConfig.get()));
77 mEngineConfig = std::move(tempConfig);
78 }
79 }
80
SetUpSurroundSoundConfig()81 void SetUpSurroundSoundConfig() {
82 if (mSurroundSoundConfig == nullptr) {
83 auto tempConfig = std::make_unique<SurroundSoundConfig>();
84 ASSERT_IS_OK(mConfig->getSurroundSoundConfig(tempConfig.get()));
85 mSurroundSoundConfig = std::move(tempConfig);
86 }
87 }
88
IsProductStrategyTypeReservedForSystemUse(const AudioProductStrategyType & pst)89 static bool IsProductStrategyTypeReservedForSystemUse(const AudioProductStrategyType& pst) {
90 switch (pst) {
91 case AudioProductStrategyType::SYS_RESERVED_NONE:
92 case AudioProductStrategyType::SYS_RESERVED_REROUTING:
93 case AudioProductStrategyType::SYS_RESERVED_CALL_ASSISTANT:
94 return true;
95 default:
96 return false;
97 }
98 }
99
IsStreamTypeReservedForSystemUse(const AudioStreamType & streamType)100 static bool IsStreamTypeReservedForSystemUse(const AudioStreamType& streamType) {
101 switch (streamType) {
102 case AudioStreamType::SYS_RESERVED_DEFAULT:
103 case AudioStreamType::SYS_RESERVED_REROUTING:
104 case AudioStreamType::SYS_RESERVED_PATCH:
105 case AudioStreamType::CALL_ASSISTANT:
106 return true;
107 default:
108 return false;
109 }
110 }
111
IsAudioUsageValid(const AudioUsage & usage)112 static bool IsAudioUsageValid(const AudioUsage& usage) {
113 switch (usage) {
114 case AudioUsage::INVALID:
115 case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST:
116 case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT:
117 case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED:
118 return false;
119 default:
120 return true;
121 }
122 }
123
IsAudioSourceValid(const AudioSource & source)124 static bool IsAudioSourceValid(const AudioSource& source) {
125 return (source != AudioSource::SYS_RESERVED_INVALID);
126 }
127
GetSupportedAudioProductStrategyTypes()128 static const std::unordered_set<int>& GetSupportedAudioProductStrategyTypes() {
129 static const std::unordered_set<int> supportedAudioProductStrategyTypes = []() {
130 std::unordered_set<int> supportedStrategyTypes;
131 for (const auto& audioProductStrategyType :
132 ndk::enum_range<AudioProductStrategyType>()) {
133 if (!IsProductStrategyTypeReservedForSystemUse(audioProductStrategyType)) {
134 supportedStrategyTypes.insert(static_cast<int>(audioProductStrategyType));
135 }
136 }
137 return supportedStrategyTypes;
138 }();
139 return supportedAudioProductStrategyTypes;
140 }
141
GetSupportedAudioFlagsMask()142 static int GetSupportedAudioFlagsMask() {
143 static const int supportedAudioFlagsMask = []() {
144 int mask = 0;
145 for (const auto& audioFlag : ndk::enum_range<AudioFlag>()) {
146 mask |= static_cast<int>(audioFlag);
147 }
148 return mask;
149 }();
150 return supportedAudioFlagsMask;
151 }
152
153 /**
154 * Verify streamType is not INVALID if using default engine.
155 * Verify that streamType is a valid AudioStreamType if the associated
156 * volumeGroup minIndex/maxIndex is INDEX_DEFERRED_TO_AUDIO_SERVICE.
157 */
ValidateAudioStreamType(const AudioStreamType & streamType,const AudioHalVolumeGroup & associatedVolumeGroup)158 void ValidateAudioStreamType(const AudioStreamType& streamType,
159 const AudioHalVolumeGroup& associatedVolumeGroup) {
160 EXPECT_FALSE(IsStreamTypeReservedForSystemUse(streamType));
161 if (!mEngineConfig->capSpecificConfig ||
162 associatedVolumeGroup.minIndex ==
163 AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE) {
164 EXPECT_NE(streamType, AudioStreamType::INVALID);
165 }
166 }
167
168 /**
169 * Verify contained enum types are valid.
170 */
ValidateAudioAttributes(const AudioAttributes & attributes)171 void ValidateAudioAttributes(const AudioAttributes& attributes) {
172 // No need to check contentType; there are no INVALID or SYS_RESERVED values
173 EXPECT_TRUE(IsAudioUsageValid(attributes.usage));
174 EXPECT_TRUE(IsAudioSourceValid(attributes.source));
175 EXPECT_EQ(attributes.flags & ~GetSupportedAudioFlagsMask(), 0);
176 }
177
178 /**
179 * Verify volumeGroupName corresponds to an AudioHalVolumeGroup.
180 * Validate contained types.
181 */
ValidateAudioHalAttributesGroup(const AudioHalAttributesGroup & attributesGroup,std::unordered_map<std::string,const AudioHalVolumeGroup &> & volumeGroupMap,std::unordered_set<std::string> & volumeGroupsUsedInStrategies)182 void ValidateAudioHalAttributesGroup(
183 const AudioHalAttributesGroup& attributesGroup,
184 std::unordered_map<std::string, const AudioHalVolumeGroup&>& volumeGroupMap,
185 std::unordered_set<std::string>& volumeGroupsUsedInStrategies) {
186 bool isVolumeGroupNameValid = volumeGroupMap.count(attributesGroup.volumeGroupName);
187 EXPECT_TRUE(isVolumeGroupNameValid);
188 EXPECT_NO_FATAL_FAILURE(ValidateAudioStreamType(
189 attributesGroup.streamType, volumeGroupMap.at(attributesGroup.volumeGroupName)));
190 if (isVolumeGroupNameValid) {
191 volumeGroupsUsedInStrategies.insert(attributesGroup.volumeGroupName);
192 }
193 for (const AudioAttributes& attr : attributesGroup.attributes) {
194 EXPECT_NO_FATAL_FAILURE(ValidateAudioAttributes(attr));
195 }
196 }
197
198 /**
199 * Default engine: verify productStrategy.id is valid AudioProductStrategyType.
200 * CAP engine: verify productStrategy.id is either valid AudioProductStrategyType
201 * or is >= VENDOR_STRATEGY_ID_START.
202 * Validate contained types.
203 */
ValidateAudioHalProductStrategy(const AudioHalProductStrategy & strategy,std::unordered_map<std::string,const AudioHalVolumeGroup &> & volumeGroupMap,std::unordered_set<std::string> & volumeGroupsUsedInStrategies)204 void ValidateAudioHalProductStrategy(
205 const AudioHalProductStrategy& strategy,
206 std::unordered_map<std::string, const AudioHalVolumeGroup&>& volumeGroupMap,
207 std::unordered_set<std::string>& volumeGroupsUsedInStrategies) {
208 if (!mEngineConfig->capSpecificConfig ||
209 (strategy.id < AudioHalProductStrategy::VENDOR_STRATEGY_ID_START)) {
210 EXPECT_NE(GetSupportedAudioProductStrategyTypes().find(strategy.id),
211 GetSupportedAudioProductStrategyTypes().end());
212 }
213 for (const AudioHalAttributesGroup& attributesGroup : strategy.attributesGroups) {
214 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalAttributesGroup(attributesGroup, volumeGroupMap,
215 volumeGroupsUsedInStrategies));
216 }
217 }
218
219 /**
220 * Verify curve point index is in [CurvePoint::MIN_INDEX, CurvePoint::MAX_INDEX].
221 */
ValidateAudioHalVolumeCurve(const AudioHalVolumeCurve & volumeCurve)222 void ValidateAudioHalVolumeCurve(const AudioHalVolumeCurve& volumeCurve) {
223 for (const AudioHalVolumeCurve::CurvePoint& curvePoint : volumeCurve.curvePoints) {
224 EXPECT_TRUE(curvePoint.index >= AudioHalVolumeCurve::CurvePoint::MIN_INDEX);
225 EXPECT_TRUE(curvePoint.index <= AudioHalVolumeCurve::CurvePoint::MAX_INDEX);
226 }
227 }
228
229 /**
230 * Verify minIndex, maxIndex are non-negative.
231 * Verify minIndex <= maxIndex.
232 * Verify no two volume curves use the same device category.
233 * Validate contained types.
234 */
ValidateAudioHalVolumeGroup(const AudioHalVolumeGroup & volumeGroup)235 void ValidateAudioHalVolumeGroup(const AudioHalVolumeGroup& volumeGroup) {
236 /**
237 * Legacy volume curves in audio_policy_configuration.xsd don't use
238 * minIndex or maxIndex. Use of audio_policy_configuration.xml still
239 * allows, and in some cases, relies on, AudioService to provide the min
240 * and max indices for a volumeGroup. From the VTS perspective, there is
241 * no way to differentiate between use of audio_policy_configuration.xml
242 * or audio_policy_engine_configuration.xml, as either one can be used
243 * for the default audio policy engine.
244 */
245 if (volumeGroup.minIndex != AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE ||
246 volumeGroup.maxIndex != AudioHalVolumeGroup::INDEX_DEFERRED_TO_AUDIO_SERVICE) {
247 EXPECT_TRUE(volumeGroup.minIndex >= 0);
248 EXPECT_TRUE(volumeGroup.maxIndex >= 0);
249 }
250 EXPECT_TRUE(volumeGroup.minIndex <= volumeGroup.maxIndex);
251 std::unordered_set<AudioHalVolumeCurve::DeviceCategory> deviceCategorySet;
252 for (const AudioHalVolumeCurve& volumeCurve : volumeGroup.volumeCurves) {
253 EXPECT_TRUE(deviceCategorySet.insert(volumeCurve.deviceCategory).second);
254 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalVolumeCurve(volumeCurve));
255 }
256 }
257
258 /**
259 * Verify defaultLiteralValue is empty for inclusive criterion.
260 */
ValidateAudioHalCapCriterion(const AudioHalCapCriterion & criterion,const AudioHalCapCriterionType & criterionType)261 void ValidateAudioHalCapCriterion(const AudioHalCapCriterion& criterion,
262 const AudioHalCapCriterionType& criterionType) {
263 if (criterionType.isInclusive) {
264 EXPECT_TRUE(criterion.defaultLiteralValue.empty());
265 }
266 }
267
268 /**
269 * Verify values only contain alphanumeric characters.
270 */
ValidateAudioHalCapCriterionType(const AudioHalCapCriterionType & criterionType)271 void ValidateAudioHalCapCriterionType(const AudioHalCapCriterionType& criterionType) {
272 auto isNotAlnum = [](const char& c) { return !isalnum(c); };
273 for (const std::string& value : criterionType.values) {
274 EXPECT_EQ(find_if(value.begin(), value.end(), isNotAlnum), value.end());
275 }
276 }
277
278 /**
279 * Verify each criterionType has a unique name.
280 * Verify each criterion has a unique name.
281 * Verify each criterion maps to a criterionType.
282 * Verify each criterionType is used in a criterion.
283 * Validate contained types.
284 */
ValidateCapSpecificConfig(const AudioHalEngineConfig::CapSpecificConfig & capCfg)285 void ValidateCapSpecificConfig(const AudioHalEngineConfig::CapSpecificConfig& capCfg) {
286 EXPECT_FALSE(capCfg.criteria.empty());
287 EXPECT_FALSE(capCfg.criterionTypes.empty());
288 std::unordered_map<std::string, AudioHalCapCriterionType> criterionTypeMap;
289 for (const AudioHalCapCriterionType& criterionType : capCfg.criterionTypes) {
290 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalCapCriterionType(criterionType));
291 EXPECT_TRUE(criterionTypeMap.insert({criterionType.name, criterionType}).second);
292 }
293 std::unordered_set<std::string> criterionNameSet;
294 for (const AudioHalCapCriterion& criterion : capCfg.criteria) {
295 EXPECT_TRUE(criterionNameSet.insert(criterion.name).second);
296 EXPECT_EQ(criterionTypeMap.count(criterion.criterionTypeName), 1UL);
297 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalCapCriterion(
298 criterion, criterionTypeMap.at(criterion.criterionTypeName)));
299 }
300 EXPECT_EQ(criterionTypeMap.size(), criterionNameSet.size());
301 }
302
303 /**
304 * Verify VolumeGroups are non-empty.
305 * Verify defaultProductStrategyId matches one of the provided productStrategies.
306 * Otherwise, must be left uninitialized.
307 * Verify each volumeGroup has a unique name.
308 * Verify each productStrategy has a unique id.
309 * Verify each volumeGroup is used in a product strategy.
310 * CAP engine: verify productStrategies are non-empty.
311 * Validate contained types.
312 */
ValidateAudioHalEngineConfig()313 void ValidateAudioHalEngineConfig() {
314 EXPECT_NE(mEngineConfig->volumeGroups.size(), 0UL);
315 std::unordered_map<std::string, const AudioHalVolumeGroup&> volumeGroupMap;
316 for (const AudioHalVolumeGroup& volumeGroup : mEngineConfig->volumeGroups) {
317 EXPECT_TRUE(volumeGroupMap.insert({volumeGroup.name, volumeGroup}).second);
318 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalVolumeGroup(volumeGroup));
319 }
320 if (!mEngineConfig->productStrategies.empty()) {
321 std::unordered_set<int> productStrategyIdSet;
322 std::unordered_set<std::string> volumeGroupsUsedInStrategies;
323 for (const AudioHalProductStrategy& strategy : mEngineConfig->productStrategies) {
324 EXPECT_TRUE(productStrategyIdSet.insert(strategy.id).second);
325 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalProductStrategy(
326 strategy, volumeGroupMap, volumeGroupsUsedInStrategies));
327 }
328 EXPECT_TRUE(productStrategyIdSet.count(mEngineConfig->defaultProductStrategyId))
329 << "defaultProductStrategyId doesn't match any of the provided "
330 "productStrategies";
331 EXPECT_EQ(volumeGroupMap.size(), volumeGroupsUsedInStrategies.size());
332 } else {
333 EXPECT_EQ(mEngineConfig->defaultProductStrategyId,
334 static_cast<int>(AudioProductStrategyType::SYS_RESERVED_NONE))
335 << "defaultProductStrategyId defined, but no productStrategies were provided";
336 }
337 if (mEngineConfig->capSpecificConfig) {
338 EXPECT_NO_FATAL_FAILURE(
339 ValidateCapSpecificConfig(mEngineConfig->capSpecificConfig.value()));
340 EXPECT_FALSE(mEngineConfig->productStrategies.empty());
341 }
342 }
343
ValidateAudioFormatDescription(const AudioFormatDescription & format)344 void ValidateAudioFormatDescription(const AudioFormatDescription& format) {
345 EXPECT_NE(AudioFormatType::SYS_RESERVED_INVALID, format.type);
346 if (format.type == AudioFormatType::PCM) {
347 EXPECT_NE(PcmType::DEFAULT, format.pcm);
348 EXPECT_TRUE(format.encoding.empty()) << format.encoding;
349 } else {
350 EXPECT_FALSE(format.encoding.empty());
351 }
352 }
353
354 /**
355 * Verify that the surround sound configuration is not empty.
356 * Verify each of the formatFamilies has a non-empty primaryFormat.
357 * Verify that each format only appears once.
358 */
ValidateSurroundSoundConfig()359 void ValidateSurroundSoundConfig() {
360 EXPECT_FALSE(mSurroundSoundConfig->formatFamilies.empty());
361 std::set<AudioFormatDescription> formatSet;
362 for (const SurroundSoundConfig::SurroundFormatFamily& family :
363 mSurroundSoundConfig->formatFamilies) {
364 EXPECT_NO_FATAL_FAILURE(ValidateAudioFormatDescription(family.primaryFormat));
365 EXPECT_FALSE(isDefaultAudioFormat(family.primaryFormat));
366 EXPECT_TRUE(formatSet.insert(family.primaryFormat).second);
367 for (const AudioFormatDescription& subformat : family.subFormats) {
368 EXPECT_NO_FATAL_FAILURE(ValidateAudioFormatDescription(subformat));
369 EXPECT_FALSE(isDefaultAudioFormat(subformat));
370 EXPECT_TRUE(formatSet.insert(subformat).second);
371 }
372 }
373 }
374
375 private:
376 std::shared_ptr<IConfig> mConfig;
377 std::unique_ptr<AudioHalEngineConfig> mEngineConfig;
378 std::unique_ptr<SurroundSoundConfig> mSurroundSoundConfig;
379 AudioHalBinderServiceUtil mBinderUtil;
380 };
381
TEST_P(AudioCoreConfig,Published)382 TEST_P(AudioCoreConfig, Published) {
383 // SetUp must complete with no failures.
384 }
385
TEST_P(AudioCoreConfig,CanBeRestarted)386 TEST_P(AudioCoreConfig, CanBeRestarted) {
387 ASSERT_NO_FATAL_FAILURE(RestartService());
388 }
389
TEST_P(AudioCoreConfig,GetEngineConfigIsValid)390 TEST_P(AudioCoreConfig, GetEngineConfigIsValid) {
391 ASSERT_NO_FATAL_FAILURE(SetUpEngineConfig());
392 EXPECT_NO_FATAL_FAILURE(ValidateAudioHalEngineConfig());
393 }
394
TEST_P(AudioCoreConfig,GetSurroundSoundConfigIsValid)395 TEST_P(AudioCoreConfig, GetSurroundSoundConfigIsValid) {
396 ASSERT_NO_FATAL_FAILURE(SetUpSurroundSoundConfig());
397 EXPECT_NO_FATAL_FAILURE(ValidateSurroundSoundConfig());
398 }
399
400 INSTANTIATE_TEST_SUITE_P(AudioCoreConfigTest, AudioCoreConfig,
401 testing::ValuesIn(android::getAidlHalInstanceNames(IConfig::descriptor)),
402 android::PrintInstanceNameToString);
403 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioCoreConfig);
404