Files
hardware_interfaces/audio/aidl/default/EffectFactory.cpp
Mikhail Naganov df5feba141 audio: Retain IBinder for instances with MinSchedulerPolicy
The binder passed to AIBinder_setMinSchedulerPolicy must also be
returned to the client, otherwise setting the policy for it does
not make any sense. However, server side interface instance
classes only hold a weak binder reference. It's the caller of the
'asBinder' method who must retain a strong reference. This
reference must be retained past exiting from the method which
returns the instance to the client.

To solve this issue, add storing of binders along with server
object references. These binders get released after the client
calls a 'close'/'destroy'-type method to release instance
resources.

Bug: 205884982
Test: run `atest VtsHalAudioCoreTargetTest` and effect VTS,
      and grep logcat for
     'destroyed after setMinSchedulerPolicy before being parceled'
Change-Id: I8b905b85cb8263c85edae8839a126ffe4e4d1e69
2022-12-16 15:27:10 +00:00

262 lines
11 KiB
C++

/*
* 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 <iterator>
#include <memory>
#include <tuple>
#include "include/effect-impl/EffectTypes.h"
#define LOG_TAG "AHAL_EffectFactory"
#include <dlfcn.h>
#include <unordered_set>
#include <android-base/logging.h>
#include <android/binder_ibinder_platform.h>
#include <system/thread_defs.h>
#include "effect-impl/EffectTypes.h"
#include "effect-impl/EffectUUID.h"
#include "effectFactory-impl/EffectFactory.h"
using aidl::android::media::audio::common::AudioUuid;
namespace aidl::android::hardware::audio::effect {
Factory::Factory(const std::string& file) : mConfig(EffectConfig(file)) {
LOG(DEBUG) << __func__ << " with config file: " << file;
loadEffectLibs();
}
Factory::~Factory() {
if (auto count = mEffectMap.size()) {
LOG(ERROR) << __func__ << " remaining " << count
<< " effect instances not destroyed indicating resource leak!";
for (const auto& it : mEffectMap) {
if (auto spEffect = it.first.lock()) {
LOG(ERROR) << __func__ << " erase remaining instance UUID "
<< it.second.first.toString();
destroyEffectImpl(spEffect);
}
}
}
}
ndk::ScopedAStatus Factory::queryEffects(const std::optional<AudioUuid>& in_type_uuid,
const std::optional<AudioUuid>& in_impl_uuid,
const std::optional<AudioUuid>& in_proxy_uuid,
std::vector<Descriptor>* _aidl_return) {
// get the matching list
std::vector<Descriptor::Identity> idList;
std::copy_if(mIdentitySet.begin(), mIdentitySet.end(), std::back_inserter(idList),
[&](auto& id) {
return (!in_type_uuid.has_value() || in_type_uuid.value() == id.type) &&
(!in_impl_uuid.has_value() || in_impl_uuid.value() == id.uuid) &&
(!in_proxy_uuid.has_value() ||
(id.proxy.has_value() && in_proxy_uuid.value() == id.proxy.value()));
});
// query through the matching list
for (const auto& id : idList) {
if (mEffectLibMap.count(id.uuid)) {
Descriptor desc;
auto& entry = mEffectLibMap[id.uuid];
getDlSyms(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->queryEffectFunc, EX_NULL_POINTER,
"dlNullQueryEffectFunc");
RETURN_IF_BINDER_EXCEPTION(libInterface->queryEffectFunc(&id.uuid, &desc));
_aidl_return->emplace_back(std::move(desc));
}
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::queryProcessing(const std::optional<Processing::Type>& in_type,
std::vector<Processing>* _aidl_return) {
// TODO: implement this with audio_effect.xml.
if (in_type.has_value()) {
// return all matching process filter
LOG(DEBUG) << __func__ << " process type: " << in_type.value().toString();
}
LOG(DEBUG) << __func__ << " return " << _aidl_return->size();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
std::shared_ptr<IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << in_impl_uuid.toString();
if (mEffectLibMap.count(in_impl_uuid)) {
auto& entry = mEffectLibMap[in_impl_uuid];
getDlSyms(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->createEffectFunc, EX_NULL_POINTER,
"dlNullcreateEffectFunc");
std::shared_ptr<IEffect> effectSp;
RETURN_IF_BINDER_EXCEPTION(libInterface->createEffectFunc(&in_impl_uuid, &effectSp));
if (!effectSp) {
LOG(ERROR) << __func__ << ": library created null instance without return error!";
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
}
*_aidl_return = effectSp;
ndk::SpAIBinder effectBinder = effectSp->asBinder();
AIBinder_setMinSchedulerPolicy(effectBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
mEffectMap[std::weak_ptr<IEffect>(effectSp)] =
std::make_pair(in_impl_uuid, std::move(effectBinder));
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);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
std::weak_ptr<IEffect> wpHandle(in_handle);
// find the effect entry with key (std::weak_ptr<IEffect>)
if (auto effectIt = mEffectMap.find(wpHandle); effectIt != mEffectMap.end()) {
auto& uuid = effectIt->second.first;
// find implementation library with UUID
if (auto libIt = mEffectLibMap.find(uuid); libIt != mEffectLibMap.end()) {
auto& interface = std::get<kMapEntryInterfaceIndex>(libIt->second);
RETURN_IF(!interface || !interface->destroyEffectFunc, EX_NULL_POINTER,
"dlNulldestroyEffectFunc");
RETURN_IF_BINDER_EXCEPTION(interface->destroyEffectFunc(in_handle));
} else {
LOG(ERROR) << __func__ << ": UUID " << uuid.toString() << " does not exist in libMap!";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
mEffectMap.erase(effectIt);
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 = mEffectMap.begin(); it != mEffectMap.end();) {
if (nullptr == it->first.lock()) {
it = mEffectMap.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;
}
void Factory::openEffectLibrary(const AudioUuid& impl, const std::string& libName) {
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
}
};
auto libHandle =
std::unique_ptr<void, decltype(dlClose)>{dlopen(libName.c_str(), RTLD_LAZY), dlClose};
if (!libHandle) {
LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
return;
}
LOG(INFO) << __func__ << " dlopen lib:" << libName << "\nimpl:" << impl.toString()
<< "\nhandle:" << libHandle;
auto interface = new effect_dl_interface_s{nullptr, nullptr, nullptr};
mEffectLibMap.insert(
{impl,
std::make_tuple(std::move(libHandle),
std::unique_ptr<struct effect_dl_interface_s>(interface), libName)});
}
void Factory::createIdentityWithConfig(const EffectConfig::LibraryUuid& configLib,
const AudioUuid& typeUuid,
const std::optional<AudioUuid> proxyUuid) {
static const auto& libMap = mConfig.getLibraryMap();
const std::string& libName = configLib.name;
if (auto path = libMap.find(libName); path != libMap.end()) {
Descriptor::Identity id;
id.type = typeUuid;
id.uuid = configLib.uuid;
id.proxy = proxyUuid;
LOG(DEBUG) << __func__ << ": typeUuid " << id.type.toString() << "\nimplUuid "
<< id.uuid.toString() << " proxyUuid "
<< (proxyUuid.has_value() ? proxyUuid->toString() : "null");
openEffectLibrary(id.uuid, path->second);
mIdentitySet.insert(std::move(id));
} else {
LOG(ERROR) << __func__ << ": library " << libName << " not exist!";
return;
}
}
void Factory::loadEffectLibs() {
const auto& configEffectsMap = mConfig.getEffectsMap();
for (const auto& configEffects : configEffectsMap) {
if (auto typeUuid = kUuidNameTypeMap.find(configEffects.first /* effect name */);
typeUuid != kUuidNameTypeMap.end()) {
const auto& configLibs = configEffects.second;
std::optional<AudioUuid> proxyUuid;
if (configLibs.proxyLibrary.has_value()) {
const auto& proxyLib = configLibs.proxyLibrary.value();
proxyUuid = proxyLib.uuid;
}
for (const auto& configLib : configLibs.libraries) {
createIdentityWithConfig(configLib, typeUuid->second, proxyUuid);
}
} else {
LOG(ERROR) << __func__ << ": can not find type UUID for effect " << configEffects.first
<< " skipping!";
}
}
}
void Factory::getDlSyms(DlEntry& entry) {
auto& dlHandle = std::get<kMapEntryHandleIndex>(entry);
RETURN_VALUE_IF(!dlHandle, void(), "dlNullHandle");
// Get the reference of the DL interfaces in library map tuple.
auto& dlInterface = std::get<kMapEntryInterfaceIndex>(entry);
// return if interface already exist
if (!dlInterface->createEffectFunc) {
dlInterface->createEffectFunc = (EffectCreateFunctor)dlsym(dlHandle.get(), "createEffect");
}
if (!dlInterface->queryEffectFunc) {
dlInterface->queryEffectFunc = (EffectQueryFunctor)dlsym(dlHandle.get(), "queryEffect");
}
if (!dlInterface->destroyEffectFunc) {
dlInterface->destroyEffectFunc =
(EffectDestroyFunctor)dlsym(dlHandle.get(), "destroyEffect");
}
if (!dlInterface->createEffectFunc || !dlInterface->destroyEffectFunc ||
!dlInterface->queryEffectFunc) {
LOG(ERROR) << __func__ << ": create (" << dlInterface->createEffectFunc << "), query ("
<< dlInterface->queryEffectFunc << "), or destroy ("
<< dlInterface->destroyEffectFunc
<< ") not exist in library: " << std::get<kMapEntryLibNameIndex>(entry)
<< " handle: " << dlHandle << " with dlerror: " << dlerror();
return;
}
}
} // namespace aidl::android::hardware::audio::effect