/* * Copyright 2022 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_NDEBUG 0 #include #include #include #include #include #include #include #define LOG_TAG "EffectsFactoryHalInterfaceTest" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { using ::aidl::android::media::audio::common::AudioUuid; using ::android::audio::utils::toString; using effect::utils::EffectParamReader; using effect::utils::EffectParamWriter; // EffectsFactoryHalInterface TEST(libAudioHalTest, createEffectsFactoryHalInterface) { ASSERT_NE(nullptr, EffectsFactoryHalInterface::create()); } TEST(libAudioHalTest, queryNumberEffects) { auto factory = EffectsFactoryHalInterface::create(); ASSERT_NE(nullptr, factory); uint32_t numEffects = 0; EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects)); EXPECT_NE(0ul, numEffects); } TEST(libAudioHalTest, getDescriptorByNumber) { auto factory = EffectsFactoryHalInterface::create(); ASSERT_NE(nullptr, factory); uint32_t numEffects = 0; EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects)); EXPECT_NE(0ul, numEffects); effect_descriptor_t desc; for (uint32_t i = 0; i < numEffects; i++) { EXPECT_EQ(OK, factory->getDescriptor(i, &desc)); } } TEST(libAudioHalTest, createEffect) { auto factory = EffectsFactoryHalInterface::create(); ASSERT_NE(nullptr, factory); uint32_t numEffects = 0; EXPECT_EQ(OK, factory->queryNumberEffects(&numEffects)); EXPECT_NE(0ul, numEffects); effect_descriptor_t desc; for (uint32_t i = 0; i < numEffects; i++) { sp interface; EXPECT_EQ(OK, factory->getDescriptor(i, &desc)); EXPECT_EQ(OK, factory->createEffect(&desc.uuid, 1 /* sessionId */, 1 /* ioId */, 1 /* deviceId */, &interface)); } } TEST(libAudioHalTest, getProcessings) { auto factory = EffectsFactoryHalInterface::create(); ASSERT_NE(nullptr, factory); const auto &processings = factory->getProcessings(); if (processings) { EXPECT_NE(0UL, processings->preprocess.size() + processings->postprocess.size() + processings->deviceprocess.size()); auto processingChecker = [](const auto& processings) { if (processings.size() != 0) { // any process need at least 1 effect inside std::for_each(processings.begin(), processings.end(), [](const auto& process) { EXPECT_NE(0ul, process.effects.size()); // any effect should have a valid name string, and not proxy for (const auto& effect : process.effects) { SCOPED_TRACE("Effect: {" + (effect == nullptr ? "NULL}" : ("{name: " + effect->name + ", isproxy: " + (effect->isProxy ? "true" : "false") + ", sw: " + (effect->libSw ? "non-null" : "null") + ", hw: " + (effect->libHw ? "non-null" : "null") + "}"))); EXPECT_NE(nullptr, effect); EXPECT_NE("", effect->name); EXPECT_EQ(false, effect->isProxy); EXPECT_EQ(nullptr, effect->libSw); EXPECT_EQ(nullptr, effect->libHw); } }); } }; processingChecker(processings->preprocess); processingChecker(processings->postprocess); processingChecker(processings->deviceprocess); } else { GTEST_SKIP() << "no processing found, skipping the test"; } } TEST(libAudioHalTest, getHalVersion) { auto factory = EffectsFactoryHalInterface::create(); ASSERT_NE(nullptr, factory); auto version = factory->getHalVersion(); EXPECT_NE(0, version.getMajorVersion()); } class EffectParamCombination { public: template void init(const P& p, const V& v, size_t len) { setBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4); getBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); expectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4); parameterSet = std::make_shared(createEffectParam(setBuffer.data(), p, v)); parameterGet = std::make_shared(createEffectParam(getBuffer.data(), p, v)); parameterExpect = std::make_shared(createEffectParam(expectBuffer.data(), p, v)); valueSize = len; } std::shared_ptr parameterSet; /* setParameter */ std::shared_ptr parameterGet; /* getParameter */ std::shared_ptr parameterExpect; /* expected from getParameter */ size_t valueSize; /* ValueSize expect to write in reply data buffer */ private: std::vector setBuffer; std::vector getBuffer; std::vector expectBuffer; template EffectParamReader createEffectParam(void* buf, const P& p, const V& v) { effect_param_t* paramRet = (effect_param_t*)buf; paramRet->psize = sizeof(P); paramRet->vsize = sizeof(V); EffectParamWriter writer(*paramRet); EXPECT_EQ(OK, writer.writeToParameter(&p)); EXPECT_EQ(OK, writer.writeToValue(&v)); writer.finishValueWrite(); return writer; } }; template std::shared_ptr createEffectParamCombination(const P& p, const V& v, size_t len) { auto comb = std::make_shared(); comb->init(p, v, len); return comb; } enum ParamName { TUPLE_UUID, TUPLE_PARAM_COMBINATION }; using EffectParamTestTuple = std::tuple>; static const effect_uuid_t EXTEND_EFFECT_TYPE_UUID = { 0xfa81dbde, 0x588b, 0x11ed, 0x9b6a, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}}; constexpr std::array kVendorExtensionData({0xff, 0x5, 0x50, 0xab, 0xcd, 0x00, 0xbd, 0xdb, 0xee, 0xff}); std::vector testPairs = { std::make_tuple(FX_IID_AEC, createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */, sizeof(int32_t) /* returnValueSize */)), std::make_tuple(FX_IID_AGC, createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */, sizeof(int16_t) /* returnValueSize */)), std::make_tuple(SL_IID_BASSBOOST, createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */, sizeof(int16_t) /* returnValueSize */)), std::make_tuple(EFFECT_UIID_DOWNMIX, createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD, sizeof(int16_t) /* returnValueSize */)), std::make_tuple(SL_IID_DYNAMICSPROCESSING, createEffectParamCombination( std::array({DP_PARAM_INPUT_GAIN, 0 /* channel */}), 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)), std::make_tuple( FX_IID_LOUDNESS_ENHANCER, createEffectParamCombination(LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */, sizeof(int32_t) /* returnValueSize */)), std::make_tuple(FX_IID_NS, createEffectParamCombination(NS_PARAM_LEVEL, 1 /* level */, sizeof(int32_t) /* returnValueSize */)), std::make_tuple(&EXTEND_EFFECT_TYPE_UUID, createEffectParamCombination(8, kVendorExtensionData, sizeof(kVendorExtensionData)))}; class libAudioHalEffectParamTest : public ::testing::TestWithParam { public: libAudioHalEffectParamTest() : mParamTuple(GetParam()), mFactory(EffectsFactoryHalInterface::create()), mTypeUuid(std::get(mParamTuple)), mCombination(std::get(mParamTuple)), mExpectedValue([&]() { std::vector expectData(mCombination->valueSize); mCombination->parameterExpect->readFromValue(expectData.data(), mCombination->valueSize); return expectData; }()), mDescs([&]() { std::vector descs; if (mFactory && mTypeUuid && OK == mFactory->getDescriptors(mTypeUuid, &descs)) { return descs; } return descs; }()) {} void SetUp() override { if (0ul == mDescs.size()) { GTEST_SKIP() << "Effect type not available on device, skipping"; } for (const auto& desc : mDescs) { sp interface = createEffectHal(desc); ASSERT_NE(nullptr, interface); mHalInterfaces.push_back(interface); } } void initEffect(const sp& interface) { uint32_t reply = 0; uint32_t replySize = sizeof(reply); ASSERT_EQ(OK, interface->command(EFFECT_CMD_INIT, 0, nullptr, &replySize, &reply)); ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(mEffectConfig), &mEffectConfig, &replySize, &reply)); } void TearDown() override { for (auto& interface : mHalInterfaces) { interface->close(); } } sp createEffectHal(const effect_descriptor_t& desc) { sp interface = nullptr; if (0 == std::memcmp(&desc.type, mTypeUuid, sizeof(effect_uuid_t)) && OK == mFactory->createEffect(&desc.uuid, 1 /* sessionId */, 1 /* ioId */, 1 /* deviceId */, &interface)) { return interface; } return nullptr; } void setAndGetParameter(const sp& interface) { uint32_t replySize = sizeof(uint32_t); uint8_t reply[replySize]; auto parameterSet = mCombination->parameterSet; ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)parameterSet->getTotalSize(), const_cast(¶meterSet->getEffectParam()), &replySize, &reply)) << parameterSet->toString(); ASSERT_EQ(replySize, sizeof(uint32_t)); effect_param_t* getParam = const_cast(&mCombination->parameterGet->getEffectParam()); size_t maxReplySize = mCombination->valueSize + sizeof(effect_param_t) + sizeof(parameterSet->getPaddedParameterSize()); replySize = maxReplySize; EXPECT_EQ(OK, interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)parameterSet->getTotalSize(), const_cast(¶meterSet->getEffectParam()), &replySize, getParam)); EffectParamReader parameterGet(*getParam); EXPECT_EQ(replySize, parameterGet.getTotalSize()) << parameterGet.toString(); if (mCombination->valueSize) { std::vector response(mCombination->valueSize); EXPECT_EQ(OK, parameterGet.readFromValue(response.data(), mCombination->valueSize)) << " try get valueSize " << mCombination->valueSize << " from " << parameterGet.toString() << " set " << parameterSet->toString(); EXPECT_EQ(response, mExpectedValue); } } const EffectParamTestTuple mParamTuple; const sp mFactory; const effect_uuid_t* mTypeUuid; std::shared_ptr mCombination; const std::vector mExpectedValue; const std::vector mDescs; std::vector> mHalInterfaces; effect_config_t mEffectConfig = {.inputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_READ, .format = AUDIO_FORMAT_PCM_FLOAT, .bufferProvider.getBuffer = nullptr, .bufferProvider.releaseBuffer = nullptr, .bufferProvider.cookie = nullptr, .mask = EFFECT_CONFIG_ALL, .samplingRate = 48000, .channels = AUDIO_CHANNEL_IN_STEREO}, .outputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_WRITE, .format = AUDIO_FORMAT_PCM_FLOAT, .bufferProvider.getBuffer = nullptr, .bufferProvider.releaseBuffer = nullptr, .bufferProvider.cookie = nullptr, .mask = EFFECT_CONFIG_ALL, .samplingRate = 48000, .channels = AUDIO_CHANNEL_OUT_STEREO}}; }; TEST_P(libAudioHalEffectParamTest, setAndGetParam) { for (auto& interface : mHalInterfaces) { EXPECT_NO_FATAL_FAILURE(initEffect(interface)); EXPECT_NO_FATAL_FAILURE(setAndGetParameter(interface)); } } TEST_P(libAudioHalEffectParamTest, deviceIndicationUpdate) { for (auto& interface : mHalInterfaces) { EXPECT_NO_FATAL_FAILURE(initEffect(interface)); // output device uint32_t deviceTypes = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_BLE_SPEAKER; status_t cmdStatus; uint32_t replySize = sizeof(cmdStatus); EXPECT_EQ(OK, interface->command(EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &deviceTypes, &replySize, &cmdStatus)); // input device deviceTypes = AUDIO_DEVICE_IN_WIRED_HEADSET | AUDIO_DEVICE_IN_BLUETOOTH_BLE; EXPECT_EQ(OK, interface->command(EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &deviceTypes, &replySize, &cmdStatus)); } } TEST_P(libAudioHalEffectParamTest, audioModeIndicationUpdate) { for (auto& interface : mHalInterfaces) { EXPECT_NO_FATAL_FAILURE(initEffect(interface)); uint32_t mode = AUDIO_MODE_IN_CALL; status_t cmdStatus; uint32_t replySize = sizeof(cmdStatus); EXPECT_EQ(OK, interface->command(EFFECT_CMD_SET_AUDIO_MODE, sizeof(uint32_t), &mode, &replySize, &cmdStatus)); } } TEST_P(libAudioHalEffectParamTest, audioSourceIndicationUpdate) { for (auto& interface : mHalInterfaces) { EXPECT_NO_FATAL_FAILURE(initEffect(interface)); uint32_t source = AUDIO_SOURCE_MIC; status_t cmdStatus; uint32_t replySize = sizeof(cmdStatus); EXPECT_EQ(OK, interface->command(EFFECT_CMD_SET_AUDIO_SOURCE, sizeof(uint32_t), &source, &replySize, &cmdStatus)); } } INSTANTIATE_TEST_SUITE_P( libAudioHalEffectParamTest, libAudioHalEffectParamTest, ::testing::ValuesIn(testPairs), [](const testing::TestParamInfo& info) { AudioUuid uuid = ::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid( *std::get(info.param)) .value(); std::string name = "UUID_" + toString(uuid); std::replace_if( name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); return name; }); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(libAudioHalEffectParamTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } // TODO: b/263986405 Add multi-thread testing } // namespace android