Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea24c1a1 authored by Shunkai Yao's avatar Shunkai Yao
Browse files

AIDL effect: Add effect AIDL implementationi and vts test

Bug: 238913361
Test: atest VtsHalAudioEffectTargetTest; atest VtsHalAudioEffectFactoryTargetTest
Merged-In: If8000b7396360996bdfb8eb269bc3de543871673
Change-Id: If8000b7396360996bdfb8eb269bc3de543871673
parent 0632ae6c
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -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",
+117 −18
Original line number Diff line number Diff line
@@ -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
+131 −0
Original line number Diff line number Diff line
/*
 * 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
+14 −3
Original line number Diff line number Diff line
@@ -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",
+222 −6
Original line number Diff line number Diff line
@@ -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 Equalizer::close() {
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::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
Loading