AIDL effect: Add effect AIDL implementationi and vts test

Bug: 238913361
Test: atest VtsHalAudioEffectTargetTest; atest VtsHalAudioEffectFactoryTargetTest
Merged-In: If8000b7396360996bdfb8eb269bc3de543871673
Change-Id: If8000b7396360996bdfb8eb269bc3de543871673
This commit is contained in:
Shunkai Yao
2022-09-28 17:39:23 +00:00
parent 0632ae6cd1
commit ea24c1a128
15 changed files with 1435 additions and 291 deletions

View File

@@ -81,18 +81,23 @@ cc_defaults {
cc_library_static {
name: "libaudioeffectserviceexampleimpl",
defaults: ["aidlaudioeffectservice_defaults"],
export_include_dirs: [
"include",
"include/equalizer-impl/",
],
export_include_dirs: ["include"],
srcs: [
"EffectFactory.cpp",
],
header_libs: [
"libsystem_headers",
],
visibility: [
":__subpackages__",
],
}
filegroup {
name: "effectCommonFile",
srcs: ["EffectThread.cpp"],
}
cc_binary {
name: "android.hardware.audio.effect.service-aidl.example",
relative_install_path: "hw",

View File

@@ -16,57 +16,156 @@
#define LOG_TAG "AHAL_EffectFactory"
#include <android-base/logging.h>
#include <dlfcn.h>
#include "effect-impl/EffectUUID.h"
#include "effectFactory-impl/EffectFactory.h"
#include "equalizer-impl/Equalizer.h"
#include "visualizer-impl/Visualizer.h"
using aidl::android::media::audio::common::AudioUuid;
namespace aidl::android::hardware::audio::effect {
Factory::Factory() {
// TODO: implement this with xml parser on audio_effect.xml, and filter with optional
// parameters.
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
}
};
// TODO: implement this with audio_effect.xml.
auto libHandle =
std::unique_ptr<void, decltype(dlClose)>{dlopen("libequalizer.so", RTLD_LAZY), dlClose};
if (!libHandle) {
LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
return;
}
LOG(DEBUG) << __func__ << " dlopen uuid: " << EqualizerSwImplUUID.toString() << " handle "
<< libHandle;
mEffectLibMap.insert({EqualizerSwImplUUID, std::make_pair(std::move(libHandle), nullptr)});
Descriptor::Identity id;
id.type = EqualizerTypeUUID;
id.uuid = EqualizerSwImplUUID;
mIdentityList.push_back(id);
}
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type,
const std::optional<AudioUuid>& in_instance,
Factory::~Factory() {
if (auto count = mEffectUuidMap.size()) {
LOG(ERROR) << __func__ << " remaining " << count
<< " effect instances not destroyed indicating resource leak!";
for (const auto& it : mEffectUuidMap) {
if (auto spEffect = it.first.lock()) {
LOG(ERROR) << __func__ << " erase remaining instance UUID " << it.second.toString();
destroyEffectImpl(spEffect);
}
}
}
}
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type_uuid,
const std::optional<AudioUuid>& in_impl_uuid,
std::vector<Descriptor::Identity>* _aidl_return) {
std::copy_if(mIdentityList.begin(), mIdentityList.end(), std::back_inserter(*_aidl_return),
[&](auto& desc) {
return (!in_type.has_value() || in_type.value() == desc.type) &&
(!in_instance.has_value() || in_instance.value() == desc.uuid);
return (!in_type_uuid.has_value() || in_type_uuid.value() == desc.type) &&
(!in_impl_uuid.has_value() || in_impl_uuid.value() == desc.uuid);
});
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::createEffect(
const AudioUuid& in_impl_uuid,
std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>* _aidl_return) {
#define RETURN_IF_BINDER_EXCEPTION(functor) \
{ \
binder_exception_t exception = functor; \
if (EX_NONE != exception) { \
LOG(ERROR) << #functor << ": failed with error " << exception; \
return ndk::ScopedAStatus::fromExceptionCode(exception); \
} \
}
ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
std::shared_ptr<IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
if (in_impl_uuid == EqualizerSwImplUUID) {
*_aidl_return = ndk::SharedRefBase::make<Equalizer>();
if (mEffectLibMap.count(in_impl_uuid)) {
auto& lib = mEffectLibMap[in_impl_uuid];
// didn't do dlsym yet
if (nullptr == lib.second) {
void* libHandle = lib.first.get();
struct effect_interface_s intf = {
.createEffectFunc = (EffectCreateFunctor)dlsym(libHandle, "createEffect"),
.destroyEffectFunc =
(EffectDestroyFunctor)dlsym(libHandle, "destroyEffect")};
auto dlInterface = std::make_unique<struct effect_interface_s>(intf);
if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc) {
LOG(ERROR) << __func__
<< ": create or destroy symbol not exist in library: " << libHandle
<< "!";
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
}
lib.second = std::move(dlInterface);
}
auto& libInterface = lib.second;
std::shared_ptr<IEffect> effectSp;
RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&effectSp));
if (!effectSp) {
LOG(ERROR) << __func__ << ": library created null instance without return error!";
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
}
*_aidl_return = effectSp;
mEffectUuidMap[std::weak_ptr<IEffect>(effectSp)] = in_impl_uuid;
LOG(DEBUG) << __func__ << ": instance " << effectSp.get() << " created successfully";
return ndk::ScopedAStatus::ok();
} else {
LOG(ERROR) << __func__ << ": library doesn't exist";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
} else {
LOG(ERROR) << __func__ << ": UUID "
<< " not supported";
LOG(ERROR) << __func__ << ": UUID not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::destroyEffect(
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_handle) {
if (in_handle) {
// TODO: b/245393900 need check the instance state with IEffect.getState before destroy.
ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
std::weak_ptr<IEffect> wpHandle(in_handle);
// find UUID with key (std::weak_ptr<IEffect>)
if (auto uuidIt = mEffectUuidMap.find(wpHandle); uuidIt != mEffectUuidMap.end()) {
auto& uuid = uuidIt->second;
// find implementation library with UUID
if (auto libIt = mEffectLibMap.find(uuid); libIt != mEffectLibMap.end()) {
if (libIt->second.second->destroyEffectFunc) {
RETURN_IF_BINDER_EXCEPTION(libIt->second.second->destroyEffectFunc(in_handle));
}
} else {
LOG(ERROR) << __func__ << ": UUID " << uuid.toString() << " does not exist in libMap!";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
mEffectUuidMap.erase(uuidIt);
return ndk::ScopedAStatus::ok();
} else {
LOG(ERROR) << __func__ << ": instance " << in_handle << " does not exist!";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
// go over the map and cleanup all expired weak_ptrs.
void Factory::cleanupEffectMap() {
for (auto it = mEffectUuidMap.begin(); it != mEffectUuidMap.end();) {
if (nullptr == it->first.lock()) {
it = mEffectUuidMap.erase(it);
} else {
++it;
}
}
}
ndk::ScopedAStatus Factory::destroyEffect(const std::shared_ptr<IEffect>& in_handle) {
LOG(DEBUG) << __func__ << ": instance " << in_handle.get();
ndk::ScopedAStatus status = destroyEffectImpl(in_handle);
// always do the cleanup
cleanupEffectMap();
return status;
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 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_TAG "AHAL_EffectThread"
#include <android-base/logging.h>
#include <pthread.h>
#include <sys/resource.h>
#include "effect-impl/EffectThread.h"
namespace aidl::android::hardware::audio::effect {
EffectThread::EffectThread() {
LOG(DEBUG) << __func__;
}
EffectThread::~EffectThread() {
destroy();
LOG(DEBUG) << __func__ << " done";
};
RetCode EffectThread::create(const std::string& name, const int priority) {
if (mThread.joinable()) {
LOG(WARNING) << __func__ << " thread already created, no-op";
return RetCode::SUCCESS;
}
mName = name;
mPriority = priority;
mThread = std::thread(&EffectThread::threadLoop, this);
LOG(DEBUG) << __func__ << " " << name << " priority " << mPriority << " done";
return RetCode::SUCCESS;
}
RetCode EffectThread::destroy() {
{
std::lock_guard lg(mMutex);
mStop = mExit = true;
}
mCv.notify_one();
if (mThread.joinable()) {
mThread.join();
}
LOG(DEBUG) << __func__ << " done";
return RetCode::SUCCESS;
}
RetCode EffectThread::start() {
if (!mThread.joinable()) {
LOG(ERROR) << __func__ << " thread already destroyed";
return RetCode::ERROR;
}
{
std::lock_guard lg(mMutex);
if (!mStop) {
LOG(WARNING) << __func__ << " already start";
return RetCode::SUCCESS;
}
mStop = false;
}
mCv.notify_one();
LOG(DEBUG) << __func__ << " done";
return RetCode::SUCCESS;
}
RetCode EffectThread::stop() {
if (!mThread.joinable()) {
LOG(ERROR) << __func__ << " thread already destroyed";
return RetCode::ERROR;
}
{
std::lock_guard lg(mMutex);
if (mStop) {
LOG(WARNING) << __func__ << " already stop";
return RetCode::SUCCESS;
}
mStop = true;
}
LOG(DEBUG) << __func__ << " done";
return RetCode::SUCCESS;
}
void EffectThread::threadLoop() {
pthread_setname_np(pthread_self(), mName.substr(0, MAX_TASK_COMM_LEN - 1).c_str());
setpriority(PRIO_PROCESS, 0, mPriority);
while (true) {
bool needExit = false;
{
std::unique_lock l(mMutex);
mCv.wait(l, [&]() REQUIRES(mMutex) {
needExit = mExit;
return mExit || !mStop;
});
}
if (needExit) {
LOG(WARNING) << __func__ << " EXIT!";
return;
}
// process without lock
process();
}
}
std::string toString(RetCode& code) {
switch (code) {
case RetCode::SUCCESS:
return "SUCCESS";
case RetCode::ERROR:
return "ERROR";
default:
return "EnumError";
}
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -27,17 +27,28 @@ cc_library_shared {
name: "libequalizer",
vendor: true,
shared_libs: [
"libaudioaidlcommon",
"libbase",
"libbinder_ndk",
"libstagefright_foundation",
"android.hardware.common-V2-ndk",
],
defaults: [
"aidlaudioservice_defaults",
"latest_android_media_audio_common_types_ndk_shared",
"latest_android_hardware_audio_effect_ndk_shared",
],
include_dirs: ["hardware/interfaces/audio/aidl/default/include/equalizer-impl"],
include_dirs: [
"hardware/interfaces/audio/aidl/default/include",
"system/media/audio/include",
],
srcs: [
"Equalizer.cpp",
":effectCommonFile",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wthread-safety",
],
visibility: [
"//hardware/interfaces/audio/aidl/default",

View File

@@ -14,27 +14,243 @@
* limitations under the License.
*/
#define LOG_TAG "AHAL_Equalizer"
#define LOG_TAG "AHAL_EqualizerSw"
#include <Utils.h>
#include <android-base/logging.h>
#include <unordered_set>
#include "Equalizer.h"
#include "effect-impl/EffectUUID.h"
#include "equalizer-impl/EqualizerSw.h"
using android::hardware::audio::common::getFrameSizeInBytes;
namespace aidl::android::hardware::audio::effect {
ndk::ScopedAStatus Equalizer::open() {
extern "C" binder_exception_t createEffect(std::shared_ptr<IEffect>* instanceSpp) {
if (instanceSpp) {
*instanceSpp = ndk::SharedRefBase::make<EqualizerSw>();
LOG(DEBUG) << __func__ << " instance " << instanceSpp->get() << " created";
return EX_NONE;
} else {
LOG(ERROR) << __func__ << " invalid input parameter!";
return EX_ILLEGAL_ARGUMENT;
}
}
extern "C" binder_exception_t destroyEffect(const std::shared_ptr<IEffect>& instanceSp) {
State state;
ndk::ScopedAStatus status = instanceSp->getState(&state);
if (!status.isOk() || State::INIT != state) {
LOG(ERROR) << __func__ << " instance " << instanceSp.get()
<< " in state: " << toString(state) << ", status: " << status.getDescription();
return EX_ILLEGAL_STATE;
}
LOG(DEBUG) << __func__ << " instance " << instanceSp.get() << " destroyed";
return EX_NONE;
}
ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common,
const Parameter::Specific& specific,
OpenEffectReturn* _aidl_return) {
LOG(DEBUG) << __func__;
if (mState != State::INIT) {
LOG(WARNING) << __func__ << " eq already open";
return ndk::ScopedAStatus::ok();
}
// Set essential parameters before create worker thread.
setCommonParameter(common);
setSpecificParameter(specific);
LOG(DEBUG) << " common: " << common.toString() << " specific " << specific.toString();
auto& input = common.input;
auto& output = common.output;
size_t inputFrameSize = getFrameSizeInBytes(input.base.format, input.base.channelMask);
size_t outputFrameSize = getFrameSizeInBytes(output.base.format, output.base.channelMask);
if (!createFmq(1, input.frameCount * inputFrameSize, output.frameCount * outputFrameSize,
_aidl_return)) {
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
"FailedToCreateFmq");
}
// create the worker thread
if (RetCode::SUCCESS != mWorker->create(LOG_TAG)) {
LOG(ERROR) << __func__ << " created worker thread failed";
destroyFmq();
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION,
"FailedToCreateFmq");
}
mState = State::IDLE;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EqualizerSw::close() {
if (mState == State::INIT) {
LOG(WARNING) << __func__ << " instance already closed";
return ndk::ScopedAStatus::ok();
} else if (mState == State::PROCESSING) {
LOG(ERROR) << __func__ << " instance still processing";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
"EqInstanceProcessing");
}
// stop the worker thread
mState = State::INIT;
mWorker->destroy();
destroyFmq();
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Equalizer::close() {
LOG(DEBUG) << __func__;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Equalizer::getDescriptor(Descriptor* _aidl_return) {
LOG(DEBUG) << __func__ << "descriptor " << mDesc.toString();
ndk::ScopedAStatus EqualizerSw::getDescriptor(Descriptor* _aidl_return) {
LOG(DEBUG) << __func__ << mDesc.toString();
*_aidl_return = mDesc;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) {
LOG(DEBUG) << __func__ << ": receive command:" << toString(in_commandId);
if (mState == State::INIT) {
LOG(ERROR) << __func__ << ": instance not open yet";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
"CommandStateError");
}
switch (in_commandId) {
case CommandId::START:
// start processing.
mState = State::PROCESSING;
mWorker->start();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
case CommandId::STOP:
// stop processing.
mState = State::IDLE;
mWorker->stop();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
case CommandId::RESET:
// TODO: reset buffer status.
mState = State::IDLE;
mWorker->stop();
LOG(DEBUG) << __func__ << " state: " << toString(mState);
return ndk::ScopedAStatus::ok();
default:
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"CommandIdNotSupported");
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EqualizerSw::setParameter(const Parameter& in_param) {
if (mState == State::INIT) {
LOG(ERROR) << __func__ << ": instance not open yet";
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "StateError");
}
LOG(DEBUG) << __func__ << " with: " << in_param.toString();
auto tag = in_param.getTag();
switch (tag) {
case Parameter::common: {
return setCommonParameter(in_param.get<Parameter::common>());
}
case Parameter::specific: {
return setSpecificParameter(in_param.get<Parameter::specific>());
}
default:
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"ParameterNotSupported");
}
}
ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId,
Parameter* _aidl_return) {
LOG(DEBUG) << __func__ << in_paramId.toString();
auto tag = in_paramId.getTag();
switch (tag) {
case Parameter::Id::commonTag: {
_aidl_return->set<Parameter::common>(mCommonParam);
LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString();
return ndk::ScopedAStatus::ok();
}
case Parameter::Id::specificTag: {
auto& id = in_paramId.get<Parameter::Id::specificTag>();
if (id != Parameter::Specific::equalizer) {
LOG(ERROR) << " unsupported parameter Id: " << in_paramId.toString();
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
EX_ILLEGAL_ARGUMENT, "Parameter::IdNotSupported");
}
Parameter::Specific specific;
specific.set<Parameter::Specific::equalizer>(mEqualizerParam);
_aidl_return->set<Parameter::specific>(specific);
LOG(DEBUG) << __func__ << _aidl_return->toString();
return ndk::ScopedAStatus::ok();
}
default:
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"Parameter::IdNotSupported");
}
}
ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) {
*_aidl_return = mState;
return ndk::ScopedAStatus::ok();
}
/// Private methods.
bool EqualizerSw::createFmq(int statusDepth, int inBufferSize, int outBufferSize,
OpenEffectReturn* ret) {
mStatusMQ = std::make_unique<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
mInputMQ = std::make_unique<DataMQ>(inBufferSize);
mOutputMQ = std::make_unique<DataMQ>(outBufferSize);
if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
LOG(ERROR) << __func__ << " created invalid FMQ";
return false;
}
ret->statusMQ = mStatusMQ->dupeDesc();
ret->inputDataMQ = mInputMQ->dupeDesc();
ret->outputDataMQ = mOutputMQ->dupeDesc();
return true;
}
void EqualizerSw::destroyFmq() {
mStatusMQ.reset(nullptr);
mInputMQ.reset(nullptr);
mOutputMQ.reset(nullptr);
}
ndk::ScopedAStatus EqualizerSw::setCommonParameter(const Parameter::Common& common) {
mCommonParam = common;
LOG(DEBUG) << __func__ << " set: " << mCommonParam.toString();
return ndk::ScopedAStatus::ok();
}
// TODO: implementation need change to save all parameters.
ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& specific) {
if (Parameter::Specific::equalizer != specific.getTag()) {
LOG(ERROR) << " unsupported effect: " << specific.toString();
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
"EffectNotSupported");
}
mEqualizerParam = specific.get<Parameter::Specific::equalizer>();
LOG(DEBUG) << __func__ << mEqualizerParam.toString();
return ndk::ScopedAStatus::ok();
}
void EqualizerSw::cleanUp() {
if (State::PROCESSING == mState) {
command(CommandId::STOP);
}
if (State::INIT != mState) {
close();
}
}
// Processing method running in worker thread.
void EqualizerSwWorker::process() {
// TODO: add EQ processing with FMQ, should wait until data available before data processing.
}
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <atomic>
#include <string>
#include <thread>
#include <android-base/thread_annotations.h>
#include <system/thread_defs.h>
namespace aidl::android::hardware::audio::effect {
enum class RetCode { SUCCESS, ERROR };
std::string toString(RetCode& code);
class EffectThread {
public:
// default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO
EffectThread();
virtual ~EffectThread();
// called by effect implementation.
RetCode create(const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
RetCode destroy();
RetCode start();
RetCode stop();
// Will call process() in a loop if the thread is running.
void threadLoop();
// User of EffectThread must implement the effect processing logic in this method.
virtual void process() = 0;
const int MAX_TASK_COMM_LEN = 15;
private:
std::mutex mMutex;
std::condition_variable mCv;
bool mExit GUARDED_BY(mMutex) = false;
bool mStop GUARDED_BY(mMutex) = true;
std::thread mThread;
int mPriority;
std::string mName;
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <aidl/android/media/audio/common/AudioUuid.h>
namespace aidl::android::hardware::audio::effect {
using ::aidl::android::media::audio::common::AudioUuid;
// Equalizer type UUID.
static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
0xddd6,
0x11db,
0x8f34,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
// Equalizer implementation UUID.
static const AudioUuid EqualizerSwImplUUID = {static_cast<int32_t>(0x0bed4300),
0x847d,
0x11df,
0xbb17,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
// Visualizer type UUID.
static const AudioUuid VisualizerTypeUUID = {static_cast<int32_t>(0x1d4033c0),
0x8557,
0x11df,
0x9f2d,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -16,6 +16,8 @@
#pragma once
#include <any>
#include <map>
#include <optional>
#include <vector>
@@ -63,7 +65,26 @@ class Factory : public BnFactory {
override;
private:
~Factory();
// List of effect descriptors supported by the devices.
std::vector<Descriptor::Identity> mIdentityList;
typedef binder_exception_t (*EffectCreateFunctor)(std::shared_ptr<IEffect>*);
typedef binder_exception_t (*EffectDestroyFunctor)(const std::shared_ptr<IEffect>&);
struct effect_interface_s {
EffectCreateFunctor createEffectFunc;
EffectDestroyFunctor destroyEffectFunc;
};
std::map<aidl::android::media::audio::common::AudioUuid /* implementationUUID */,
std::pair<std::unique_ptr<void, std::function<void(void*)>> /* dlHandle */,
std::unique_ptr<struct effect_interface_s>>>
mEffectLibMap;
std::map<std::weak_ptr<IEffect>, aidl::android::media::audio::common::AudioUuid,
std::owner_less<>>
mEffectUuidMap;
ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
void cleanupEffectMap();
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -1,51 +0,0 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <cstdlib>
namespace aidl::android::hardware::audio::effect {
// Equalizer type UUID.
static const ::aidl::android::media::audio::common::AudioUuid EqualizerTypeUUID = {
static_cast<int32_t>(0x0bed4300),
0xddd6,
0x11db,
0x8f34,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
// Equalizer implementation UUID.
static const ::aidl::android::media::audio::common::AudioUuid EqualizerSwImplUUID = {
static_cast<int32_t>(0x0bed4300),
0x847d,
0x11df,
0xbb17,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
class Equalizer : public BnEffect {
public:
Equalizer() = default;
ndk::ScopedAStatus open() override;
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
private:
// Effect descriptor.
Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <aidl/android/hardware/audio/effect/BnEffect.h>
#include <fmq/AidlMessageQueue.h>
#include <cstdlib>
#include <memory>
#include "effect-impl/EffectThread.h"
namespace aidl::android::hardware::audio::effect {
class EqualizerSwWorker : public EffectThread {
// EqualizerSwWorker(const std::string name){EffectThread(name)};
void process() override;
};
class EqualizerSw : public BnEffect {
public:
EqualizerSw() {
// create the worker
mWorker = std::make_unique<EqualizerSwWorker>();
LOG(DEBUG) << __func__;
};
~EqualizerSw() {
cleanUp();
LOG(DEBUG) << __func__;
};
ndk::ScopedAStatus open(const Parameter::Common& common, const Parameter::Specific& specific,
OpenEffectReturn* _aidl_return) override;
ndk::ScopedAStatus close() override;
ndk::ScopedAStatus getDescriptor(Descriptor* _aidl_return) override;
ndk::ScopedAStatus getState(State* _aidl_return) override;
ndk::ScopedAStatus command(CommandId in_commandId) override;
ndk::ScopedAStatus setParameter(const Parameter& in_param) override;
ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
Parameter* _aidl_return) override;
private:
// effect processing thread.
std::unique_ptr<EqualizerSwWorker> mWorker;
// Effect descriptor.
const Descriptor mDesc = {
.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
// Parameters.
Parameter::Common mCommonParam;
Equalizer mEqualizerParam; // TODO: the equalizer parameter needs to update
// Instance state INIT by default.
State mState = State::INIT;
typedef ::android::AidlMessageQueue<
Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
StatusMQ;
typedef ::android::AidlMessageQueue<
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
DataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
std::unique_ptr<DataMQ> mInputMQ;
std::unique_ptr<DataMQ> mOutputMQ;
ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param);
ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific);
bool createFmq(int statusDepth, int inBufferSize, int outBufferSize, OpenEffectReturn* ret);
void destroyFmq();
void cleanUp();
};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -1,31 +0,0 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <cstdlib>
namespace aidl::android::hardware::audio::effect {
// Visualizer implementation UUID.
static const ::aidl::android::media::audio::common::AudioUuid VisualizerUUID = {
static_cast<int32_t>(0x1d4033c0),
0x8557,
0x11df,
0x9f2d,
{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
} // namespace aidl::android::hardware::audio::effect

View File

@@ -42,6 +42,36 @@ cc_test {
],
}
cc_test {
name: "VtsHalAudioEffectFactoryTargetTest",
defaults: [
"latest_android_media_audio_common_types_ndk_static",
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
"VtsHalAudioEffectFactoryTargetTest.cpp",
],
shared_libs: [
"libbinder_ndk",
],
static_libs: [
"android.hardware.audio.effect-V1-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-Wthread-safety",
],
test_suites: [
"general-tests",
"vts",
],
}
cc_test {
name: "VtsHalAudioEffectTargetTest",
defaults: [
@@ -57,6 +87,8 @@ cc_test {
],
static_libs: [
"android.hardware.audio.effect-V1-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
],
cflags: [
"-Wall",

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 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.
*/
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <android/binder_auto_utils.h>
#include "TestUtils.h"
using namespace android;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::media::audio::common::AudioUuid;
class EffectFactoryHelper {
public:
explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
void ConnectToFactoryService() {
mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
ASSERT_NE(mEffectFactory, nullptr);
}
void RestartFactoryService() {
ASSERT_NE(mEffectFactory, nullptr);
mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
ASSERT_NE(mEffectFactory, nullptr);
ClearEffectMap();
}
void QueryEffects(const std::optional<AudioUuid>& in_type,
const std::optional<AudioUuid>& in_instance,
std::vector<Descriptor::Identity>* _aidl_return) {
ASSERT_NE(mEffectFactory, nullptr);
EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
mIds = *_aidl_return;
}
void CreateEffects() {
ASSERT_NE(mEffectFactory, nullptr);
for (const auto& id : mIds) {
std::shared_ptr<IEffect> effect;
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
EXPECT_NE(effect, nullptr) << id.toString();
if (effect) {
mEffectIdMap[effect] = id;
}
}
}
void CreateEffectsAndExpect(
const std::vector<std::pair<Descriptor::Identity, binder_exception_t>>& uuid_status) {
ASSERT_NE(mEffectFactory, nullptr);
for (const auto& it : uuid_status) {
std::shared_ptr<IEffect> effect;
auto status = mEffectFactory->createEffect(it.first.uuid, &effect);
EXPECT_STATUS(it.second, status);
if (effect) {
mEffectIdMap[effect] = it.first;
}
}
}
void DestroyEffectAndExpect(std::shared_ptr<IEffect>& instance, binder_exception_t exception) {
ASSERT_NE(mEffectFactory, nullptr);
auto status = mEffectFactory->destroyEffect(instance);
EXPECT_STATUS(exception, status);
}
void QueryAndCreateAllEffects() {
ASSERT_NE(mEffectFactory, nullptr);
EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
for (const auto& id : mCompleteIds) {
std::shared_ptr<IEffect> effect;
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
EXPECT_NE(effect, nullptr) << id.toString();
mEffectIdMap[effect] = id;
}
}
void DestroyEffects(const binder_exception_t expected = EX_NONE, const int remaining = 0) {
ASSERT_NE(mEffectFactory, nullptr);
for (auto it = mEffectIdMap.begin(); it != mEffectIdMap.end();) {
auto erased = it++;
auto status = mEffectFactory->destroyEffect(erased->first);
EXPECT_STATUS(expected, status);
if (status.isOk()) {
mEffectIdMap.erase(erased);
}
}
EXPECT_EQ((unsigned int)remaining, mEffectIdMap.size());
}
std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
const std::map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
return mEffectIdMap;
}
void ClearEffectMap() { mEffectIdMap.clear(); }
private:
std::shared_ptr<IFactory> mEffectFactory;
std::string mServiceName;
AudioHalBinderServiceUtil binderUtil;
std::vector<Descriptor::Identity> mIds;
std::vector<Descriptor::Identity> mCompleteIds;
std::map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
};

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 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.
*/
#include <memory>
#include <string>
#include <vector>
#define LOG_TAG "VtsHalAudioEffectFactory"
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android/binder_interface_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include "AudioHalBinderServiceUtil.h"
#include "EffectFactoryHelper.h"
#include "TestUtils.h"
using namespace android;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::media::audio::common::AudioUuid;
/// Effect factory testing.
class EffectFactoryTest : public testing::TestWithParam<std::string> {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
void TearDown() override { mFactory.DestroyEffects(); }
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
// TODO: these UUID can get from config file
// ec7178ec-e5e1-4432-a3f4-4657e6795210
const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
0xe5e1,
0x4432,
0xa3f4,
{0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
const AudioUuid zeroUuid = {
static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
const Descriptor::Identity nullDesc = {.uuid = nullUuid};
const Descriptor::Identity zeroDesc = {.uuid = zeroUuid};
};
TEST_P(EffectFactoryTest, SetupAndTearDown) {
// Intentionally empty test body.
}
TEST_P(EffectFactoryTest, CanBeRestarted) {
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
}
TEST_P(EffectFactoryTest, QueriedDescriptorList) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
EXPECT_NE(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
// TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
for (auto& desc : descriptors) {
EXPECT_NE(desc.type, zeroUuid);
EXPECT_NE(desc.uuid, zeroUuid);
}
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateAndDestroyOnce) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
auto& effectMap = mFactory.GetEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
auto& effectMap = mFactory.GetEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
// Create and destroy again
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
auto& effectMap = mFactory.GetEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
// Create effect instances of same implementation
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), 2 * numIds);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), 3 * numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
}
// Expect EX_ILLEGAL_ARGUMENT when create with invalid UUID.
TEST_P(EffectFactoryTest, CreateWithInvalidUuid) {
std::vector<std::pair<Descriptor::Identity, binder_status_t>> descriptors;
descriptors.push_back(std::make_pair(nullDesc, EX_ILLEGAL_ARGUMENT));
descriptors.push_back(std::make_pair(zeroDesc, EX_ILLEGAL_ARGUMENT));
auto& effectMap = mFactory.GetEffectMap();
mFactory.CreateEffectsAndExpect(descriptors);
EXPECT_EQ(effectMap.size(), 0UL);
}
// Expect EX_ILLEGAL_ARGUMENT when destroy null interface.
TEST_P(EffectFactoryTest, DestroyWithInvalidInterface) {
std::shared_ptr<IEffect> spDummyEffect(nullptr);
mFactory.DestroyEffectAndExpect(spDummyEffect, EX_ILLEGAL_ARGUMENT);
}
TEST_P(EffectFactoryTest, CreateAndRemoveReference) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
auto& effectMap = mFactory.GetEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
// remove all reference
mFactory.ClearEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateRemoveReferenceAndCreateDestroy) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
auto& effectMap = mFactory.GetEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
// remove all reference
mFactory.ClearEffectMap();
EXPECT_EQ(effectMap.size(), 0UL);
// Create and destroy again
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateRestartAndCreateDestroy) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
auto& effectMap = mFactory.GetEffectMap();
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
mFactory.CreateEffects();
EXPECT_EQ(effectMap.size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(effectMap.size(), 0UL);
}
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}

View File

@@ -30,205 +30,68 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <aidl/android/hardware/audio/effect/IEffect.h>
#include <aidl/android/hardware/audio/effect/IFactory.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include "AudioHalBinderServiceUtil.h"
#include "EffectFactoryHelper.h"
#include "TestUtils.h"
using namespace android;
using ndk::ScopedAStatus;
using aidl::android::hardware::audio::effect::CommandId;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::media::audio::common::AudioUuid;
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::AudioDeviceType;
class EffectFactoryHelper {
public:
explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
void ConnectToFactoryService() {
mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
ASSERT_NE(mEffectFactory, nullptr);
}
void RestartFactoryService() {
ASSERT_NE(mEffectFactory, nullptr);
mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
ASSERT_NE(mEffectFactory, nullptr);
}
void QueryAllEffects() {
EXPECT_NE(mEffectFactory, nullptr);
EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &mCompleteIds));
}
void QueryEffects(const std::optional<AudioUuid>& in_type,
const std::optional<AudioUuid>& in_instance,
std::vector<Descriptor::Identity>* _aidl_return) {
EXPECT_NE(mEffectFactory, nullptr);
EXPECT_IS_OK(mEffectFactory->queryEffects(in_type, in_instance, _aidl_return));
mIds = *_aidl_return;
}
void CreateEffects() {
EXPECT_NE(mEffectFactory, nullptr);
for (const auto& id : mIds) {
std::shared_ptr<IEffect> effect;
EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect));
EXPECT_NE(effect, nullptr) << id.toString();
mEffectIdMap[effect] = id;
}
}
void DestroyEffects() {
EXPECT_NE(mEffectFactory, nullptr);
for (const auto& it : mEffectIdMap) {
EXPECT_IS_OK(mEffectFactory->destroyEffect(it.first));
}
mEffectIdMap.clear();
}
std::shared_ptr<IFactory> GetFactory() { return mEffectFactory; }
const std::vector<Descriptor::Identity>& GetEffectIds() { return mIds; }
const std::vector<Descriptor::Identity>& GetCompleteEffectIdList() { return mCompleteIds; }
const std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity>& GetEffectMap() {
return mEffectIdMap;
}
private:
std::shared_ptr<IFactory> mEffectFactory;
std::string mServiceName;
AudioHalBinderServiceUtil binderUtil;
std::vector<Descriptor::Identity> mIds;
std::vector<Descriptor::Identity> mCompleteIds;
std::unordered_map<std::shared_ptr<IEffect>, Descriptor::Identity> mEffectIdMap;
};
/// Effect factory testing.
class EffectFactoryTest : public testing::TestWithParam<std::string> {
public:
void SetUp() override { ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService()); }
void TearDown() override { mFactory.DestroyEffects(); }
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
// TODO: these UUID can get from config file
// ec7178ec-e5e1-4432-a3f4-4657e6795210
const AudioUuid nullUuid = {static_cast<int32_t>(0xec7178ec),
0xe5e1,
0x4432,
0xa3f4,
{0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
const AudioUuid zeroUuid = {
static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
};
TEST_P(EffectFactoryTest, SetupAndTearDown) {
// Intentionally empty test body.
}
TEST_P(EffectFactoryTest, CanBeRestarted) {
ASSERT_NO_FATAL_FAILURE(mFactory.RestartFactoryService());
}
TEST_P(EffectFactoryTest, QueriedDescriptorList) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
EXPECT_NE(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
// TODO: Factory eventually need to return the full list of MUST supported AOSP effects.
for (auto& desc : descriptors) {
EXPECT_NE(desc.type, zeroUuid);
EXPECT_NE(desc.uuid, zeroUuid);
}
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors);
EXPECT_EQ(descriptors.size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateAndDestroyRepeat) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
// Create and destroy again
mFactory.CreateEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
mFactory.DestroyEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
}
TEST_P(EffectFactoryTest, CreateMultipleInstanceOfSameEffect) {
std::vector<Descriptor::Identity> descriptors;
mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors);
auto numIds = mFactory.GetEffectIds().size();
EXPECT_NE(numIds, 0UL);
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
mFactory.CreateEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), numIds);
// Create effect instances of same implementation
mFactory.CreateEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), 2 * numIds);
mFactory.CreateEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), 3 * numIds);
mFactory.DestroyEffects();
EXPECT_EQ(mFactory.GetEffectMap().size(), 0UL);
}
INSTANTIATE_TEST_SUITE_P(EffectFactoryTest, EffectFactoryTest,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EffectFactoryTest);
/// Effect testing.
class AudioEffect : public testing::TestWithParam<std::string> {
public:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(mFactory.ConnectToFactoryService());
ASSERT_NO_FATAL_FAILURE(mFactory.CreateEffects());
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.ConnectToFactoryService());
CreateEffects();
initParamCommon();
initParamSpecific();
}
void TearDown() override {
CloseEffects();
ASSERT_NO_FATAL_FAILURE(mFactory.DestroyEffects());
DestroyEffects();
}
void OpenEffects() {
auto open = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->open()); };
auto open = [&](const std::shared_ptr<IEffect>& effect) {
IEffect::OpenEffectReturn ret;
EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(open));
}
void CloseEffects() {
auto close = [](const std::shared_ptr<IEffect>& effect) { EXPECT_IS_OK(effect->close()); };
void CloseEffects(const binder_status_t status = EX_NONE) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->close());
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CreateEffects(const int n = 1) {
for (int i = 0; i < n; i++) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects());
}
}
void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) {
ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining));
}
void GetEffectDescriptors() {
auto get = [](const std::shared_ptr<IEffect>& effect) {
Descriptor desc;
@@ -237,16 +100,101 @@ class AudioEffect : public testing::TestWithParam<std::string> {
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void CommandEffects(CommandId command) {
auto close = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_IS_OK(effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(close));
}
void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
EXPECT_STATUS(status, effect->command(command));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void ExpectState(State expected) {
auto get = [&](const std::shared_ptr<IEffect>& effect) {
State state = State::INIT;
EXPECT_IS_OK(effect->getState(&state));
EXPECT_EQ(expected, state);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(get));
}
void SetParameter() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter param;
param.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->setParameter(param));
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
void VerifyParameters() {
auto func = [&](const std::shared_ptr<IEffect>& effect) {
Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter();
Parameter::Id id;
id.set<Parameter::Id::commonTag>(0);
paramCommonExpect.set<Parameter::common>(mCommon);
EXPECT_IS_OK(effect->getParameter(id, &paramCommonGet));
EXPECT_EQ(paramCommonExpect, paramCommonGet)
<< paramCommonExpect.toString() << " vs " << paramCommonGet.toString();
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(func));
}
template <typename Functor>
void ForEachEffect(Functor functor) {
auto effectMap = mFactory.GetEffectMap();
auto effectMap = mFactoryHelper.GetEffectMap();
for (const auto& it : effectMap) {
SCOPED_TRACE(it.second.toString());
functor(it.first);
}
}
EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam());
void initParamCommon(int session = -1, int ioHandle = -1,
AudioDeviceType deviceType = AudioDeviceType::NONE,
int iSampleRate = 48000, int oSampleRate = 48000, long iFrameCount = 0x100,
long oFrameCount = 0x100) {
mCommon.session = session;
mCommon.ioHandle = ioHandle;
mCommon.device.type = deviceType;
mCommon.input.base.sampleRate = iSampleRate;
mCommon.input.base.channelMask = mInputChannelLayout;
mCommon.input.frameCount = iFrameCount;
mCommon.output.base.sampleRate = oSampleRate;
mCommon.output.base.channelMask = mOutputChannelLayout;
mCommon.output.frameCount = oFrameCount;
}
void initParamSpecific(Parameter::Specific::Tag tag = Parameter::Specific::equalizer) {
switch (tag) {
case Parameter::Specific::equalizer:
mSpecific.set<Parameter::Specific::equalizer>();
break;
default:
return;
}
}
void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; }
void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; }
EffectFactoryHelper mFactoryHelper = EffectFactoryHelper(GetParam());
private:
AudioChannelLayout mInputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
AudioChannelLayout mOutputChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
AudioChannelLayout::LAYOUT_STEREO);
Parameter::Common mCommon;
Parameter::Specific mSpecific;
static IEffect::OpenEffectReturn mOpenReturn;
};
TEST_P(AudioEffect, OpenEffectTest) {
@@ -286,7 +234,7 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
Descriptor desc;
std::vector<Descriptor::Identity> idList;
EXPECT_IS_OK(effect->getDescriptor(&desc));
mFactory.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
mFactoryHelper.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList);
EXPECT_EQ(idList.size(), 1UL);
};
EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker));
@@ -295,7 +243,7 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
auto stringHash = [](const Descriptor::Identity& id) {
return std::hash<std::string>()(id.toString());
};
auto vec = mFactory.GetCompleteEffectIdList();
auto vec = mFactoryHelper.GetCompleteEffectIdList();
std::unordered_set<Descriptor::Identity, decltype(stringHash)> idSet(0, stringHash);
for (auto it : vec) {
EXPECT_EQ(idSet.count(it), 0UL);
@@ -303,6 +251,212 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) {
}
}
/// State testing.
// An effect instance is in INIT state by default after it was created.
TEST_P(AudioEffect, InitStateAfterCreation) {
ExpectState(State::INIT);
}
// An effect instance transfer to INIT state after it was open successfully with IEffect.open().
TEST_P(AudioEffect, IdleStateAfterOpen) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// An effect instance is in PROCESSING state after it receive an START command.
TEST_P(AudioEffect, ProcessingStateAfterStart) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// An effect instance transfer to IDLE state after Command.Id.STOP in PROCESSING state.
TEST_P(AudioEffect, IdleStateAfterStop) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// An effect instance transfer to IDLE state after Command.Id.RESET in PROCESSING state.
TEST_P(AudioEffect, IdleStateAfterReset) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// An effect instance transfer to INIT if instance receive a close() call.
TEST_P(AudioEffect, InitStateAfterClose) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
ExpectState(State::INIT);
}
// An effect instance shouldn't accept any command before open.
TEST_P(AudioEffect, NoCommandAcceptedBeforeOpen) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE));
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE));
EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE));
ExpectState(State::INIT);
}
// No-op when receive STOP command in IDLE state.
TEST_P(AudioEffect, StopCommandInIdleStateNoOp) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// No-op when receive STOP command in IDLE state.
TEST_P(AudioEffect, ResetCommandInIdleStateNoOp) {
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Repeat START and STOP command.
TEST_P(AudioEffect, RepeatStartAndStop) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Repeat START and RESET command.
TEST_P(AudioEffect, RepeatStartAndReset) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Repeat START and STOP command, try to close at PROCESSING state.
TEST_P(AudioEffect, CloseProcessingStateEffects) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
EXPECT_NO_FATAL_FAILURE(CloseEffects(EX_ILLEGAL_STATE));
// cleanup
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
}
// Expect EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed.
TEST_P(AudioEffect, DestroyOpenEffects) {
// cleanup all effects.
EXPECT_NO_FATAL_FAILURE(CloseEffects());
ASSERT_NO_FATAL_FAILURE(DestroyEffects());
// open effects, destroy without close, expect to get EX_ILLEGAL_STATE status.
EXPECT_NO_FATAL_FAILURE(CreateEffects());
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(DestroyEffects(EX_ILLEGAL_STATE, 1));
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
/// Parameter testing.
// Verify parameters pass in open can be successfully get.
TEST_P(AudioEffect, VerifyParametersAfterOpen) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Verify parameters pass in set can be successfully get.
TEST_P(AudioEffect, SetAndGetParameter) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Verify parameters pass in set can be successfully get.
TEST_P(AudioEffect, SetAndGetParameterInProcessing) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Parameters kept after reset.
TEST_P(AudioEffect, ResetAndVerifyParameter) {
EXPECT_NO_FATAL_FAILURE(OpenEffects());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
// Multiple instances of same implementation running.
TEST_P(AudioEffect, MultipleInstancesRunning) {
EXPECT_NO_FATAL_FAILURE(CreateEffects(3));
ExpectState(State::INIT);
EXPECT_NO_FATAL_FAILURE(OpenEffects());
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START));
ExpectState(State::PROCESSING);
initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */,
44100 /* iSampleRate */, 44100 /* oSampleRate */);
EXPECT_NO_FATAL_FAILURE(SetParameter());
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP));
ExpectState(State::IDLE);
EXPECT_NO_FATAL_FAILURE(VerifyParameters());
EXPECT_NO_FATAL_FAILURE(CloseEffects());
}
INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect,
testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)),
android::PrintInstanceNameToString);