/* * Copyright 2023 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 #define LOG_TAG "EffectProxyTest" #include #include #include #include #include #include "EffectProxy.h" /** * This test suite is depending on audio effect AIDL service. */ namespace android { using ::aidl::android::hardware::audio::effect::CommandId; using ::aidl::android::hardware::audio::effect::Descriptor; using ::aidl::android::hardware::audio::effect::Flags; using ::aidl::android::hardware::audio::effect::IEffect; using ::aidl::android::hardware::audio::effect::IFactory; using ::aidl::android::hardware::audio::effect::Parameter; using ::aidl::android::hardware::audio::effect::State; using ::aidl::android::media::audio::common::AudioChannelLayout; using ::aidl::android::media::audio::common::AudioFormatDescription; using ::aidl::android::media::audio::common::AudioFormatType; using ::aidl::android::media::audio::common::AudioUuid; using ::aidl::android::media::audio::common::PcmType; using ::android::effect::EffectProxy; class EffectProxyTest : public testing::Test { public: void SetUp() override { auto serviceName = android::getAidlHalInstanceNames(IFactory::descriptor); // only unit test with the first one in case more than one EffectFactory service exist if (0ul == serviceName.size()) { GTEST_SKIP() << "EffectFactory not available on device, skipping"; } mFactory = IFactory::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(serviceName[0].c_str()))); ASSERT_NE(nullptr, mFactory); mFactory->queryEffects(std::nullopt, std::nullopt, std::nullopt, &mDescs); for (const auto& desc : mDescs) { if (desc.common.id.proxy.has_value()) { mProxyDescs[desc.common.id.proxy.value()].emplace_back(desc); } } } void TearDown() override {} const AudioFormatDescription kDefaultFormatDescription = { .type = AudioFormatType::PCM, .pcm = PcmType::FLOAT_32_BIT, .encoding = ""}; Parameter::Common createParamCommon( int session = 0, int ioHandle = -1, int iSampleRate = 48000, int oSampleRate = 48000, long iFrameCount = 0x100, long oFrameCount = 0x100, AudioChannelLayout inputChannelLayout = AudioChannelLayout::make( AudioChannelLayout::LAYOUT_STEREO), AudioChannelLayout outputChannelLayout = AudioChannelLayout::make( AudioChannelLayout::LAYOUT_STEREO)) { Parameter::Common common; common.session = session; common.ioHandle = ioHandle; auto& input = common.input; auto& output = common.output; input.base.sampleRate = iSampleRate; input.base.channelMask = inputChannelLayout; input.base.format = kDefaultFormatDescription; input.frameCount = iFrameCount; output.base.sampleRate = oSampleRate; output.base.channelMask = outputChannelLayout; output.base.format = kDefaultFormatDescription; output.frameCount = oFrameCount; return common; } enum TupleIndex { HANDLE, DESCRIPTOR }; using EffectProxyTuple = std::tuple, std::vector>; std::map createAllProxies() { std::map proxyMap; for (const auto& itor : mProxyDescs) { const auto& uuid = itor.first; if (proxyMap.end() == proxyMap.find(uuid)) { std::get(proxyMap[uuid]) = ndk::SharedRefBase::make(itor.first, itor.second, mFactory); } } return proxyMap; } std::shared_ptr mFactory; std::vector mDescs; std::map> mProxyDescs; }; TEST_F(EffectProxyTest, createProxy) { auto proxyMap = createAllProxies(); // if there are some descriptor defined with proxy, then proxyMap can not be empty EXPECT_EQ(mProxyDescs.size() == 0, proxyMap.size() == 0); } TEST_F(EffectProxyTest, addSubEffectsCreateAndDestroy) { auto proxyMap = createAllProxies(); for (const auto& itor : proxyMap) { auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->destroy().isOk()); } } TEST_F(EffectProxyTest, addSubEffectsCreateOpenCloseDestroy) { auto proxyMap = createAllProxies(); Parameter::Common common = createParamCommon(); IEffect::OpenEffectReturn ret; for (const auto& itor : proxyMap) { auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk()); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->destroy().isOk()); } } // Add sub-effects, set active sub-effect with different checkers TEST_F(EffectProxyTest, setOffloadParam) { auto proxyMap = createAllProxies(); // Any flag exist should be able to set successfully Parameter::Common common = createParamCommon(); IEffect::OpenEffectReturn ret; for (const auto& itor : proxyMap) { auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk()); effect_offload_param_t offloadParam{false, 0}; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); offloadParam.isOffload = true; offloadParam.ioHandle++; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->destroy().isOk()); } } TEST_F(EffectProxyTest, destroyWithoutCreate) { auto proxyMap = createAllProxies(); for (const auto& itor : proxyMap) { auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->destroy().isOk()); } } TEST_F(EffectProxyTest, closeWithoutOpen) { auto proxyMap = createAllProxies(); for (const auto& itor : proxyMap) { auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->destroy().isOk()); } } // Add sub-effects, set active sub-effect, create, open, and send command, expect success handling TEST_F(EffectProxyTest, normalSequency) { auto proxyMap = createAllProxies(); Parameter::Common common = createParamCommon(); IEffect::OpenEffectReturn ret; Parameter::VolumeStereo volumeStereo({.left = .1f, .right = -0.8f}); Parameter expect = Parameter::make(volumeStereo); const Parameter::Id id = Parameter::Id::make(Parameter::volumeStereo); State state; for (const auto& itor : proxyMap) { Parameter getParam = Parameter::make(true); auto& proxy = std::get(itor.second); effect_offload_param_t offloadParam{true, 0}; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk()); EXPECT_TRUE(proxy->setParameter(expect).isOk()); EXPECT_TRUE(proxy->getParameter(id, &getParam).isOk()); EXPECT_EQ(expect, getParam) << " EXPECTED: " << expect.toString() << "\nACTUAL: " << getParam.toString(); EXPECT_TRUE(proxy->command(CommandId::START).isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::PROCESSING, state); EXPECT_TRUE(proxy->command(CommandId::STOP).isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::IDLE, state); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->destroy().isOk()); } } // setParameter, change active sub-effect, verify with getParameter TEST_F(EffectProxyTest, changeActiveSubAndVerifyParameter) { auto proxyMap = createAllProxies(); Parameter::Common common = createParamCommon(); IEffect::OpenEffectReturn ret; Parameter::VolumeStereo volumeStereo({.left = .5f, .right = .8f}); Parameter expect = Parameter::make(volumeStereo); const Parameter::Id id = Parameter::Id::make(Parameter::volumeStereo); for (const auto& itor : proxyMap) { Parameter getParam = Parameter::make(true); auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk()); EXPECT_TRUE(proxy->setParameter(expect).isOk()); EXPECT_TRUE(proxy->getParameter(id, &getParam).isOk()); EXPECT_EQ(expect, getParam); effect_offload_param_t offloadParam{false, 0}; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); EXPECT_TRUE(proxy->getParameter(id, &getParam).isOk()); EXPECT_EQ(expect, getParam); offloadParam.isOffload = true; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); EXPECT_TRUE(proxy->getParameter(id, &getParam).isOk()); EXPECT_EQ(expect, getParam); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->destroy().isOk()); } } // send command, change active sub-effect, then verify the state with getState TEST_F(EffectProxyTest, changeActiveSubAndVerifyState) { auto proxyMap = createAllProxies(); Parameter::Common common = createParamCommon(); IEffect::OpenEffectReturn ret; State state; for (const auto& itor : proxyMap) { Parameter expect; auto& proxy = std::get(itor.second); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::INIT, state); EXPECT_TRUE(proxy->open(common, std::nullopt, &ret).isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::IDLE, state); EXPECT_TRUE(proxy->command(CommandId::START).isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::PROCESSING, state); effect_offload_param_t offloadParam{false, 0}; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); offloadParam.isOffload = true; EXPECT_TRUE(proxy->setOffloadParam(&offloadParam).isOk()); EXPECT_TRUE(proxy->command(CommandId::STOP).isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::IDLE, state); EXPECT_TRUE(proxy->close().isOk()); EXPECT_TRUE(proxy->getState(&state).isOk()); EXPECT_EQ(State::INIT, state); EXPECT_TRUE(proxy->destroy().isOk()); } } } // namespace android