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