blob: 4fb6138020b7556a2d2194a81263c68688175523 [file] [log] [blame]
/*
**
** Copyright 2019, 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 "AudioFlinger::DeviceEffectManager"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <audio_utils/primitives.h>
#include "AudioFlinger.h"
#include "EffectConfiguration.h"
#include <media/audiohal/EffectsFactoryHalInterface.h>
// ----------------------------------------------------------------------------
namespace android {
using detail::AudioHalVersionInfo;
using media::IEffectClient;
void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle,
const PatchPanel::Patch& patch) {
ALOGV("%s handle %d mHalHandle %d device sink %08x",
__func__, handle, patch.mHalHandle,
patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
Mutex::Autolock _l(mLock);
for (auto& effect : mDeviceEffects) {
status_t status = effect.second->onCreatePatch(handle, patch);
ALOGV("%s Effect onCreatePatch status %d", __func__, status);
ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status);
}
}
void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
ALOGV("%s", __func__);
Mutex::Autolock _l(mLock);
for (auto& effect : mDeviceEffects) {
effect.second->onReleasePatch(handle);
}
}
// DeviceEffectManager::createEffect_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::EffectHandle> AudioFlinger::DeviceEffectManager::createEffect_l(
effect_descriptor_t *descriptor,
const AudioDeviceTypeAddr& device,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
int *enabled,
status_t *status,
bool probe,
bool notifyFramesProcessed) {
sp<DeviceEffectProxy> effect;
sp<EffectHandle> handle;
status_t lStatus;
lStatus = checkEffectCompatibility(descriptor);
if (probe || lStatus != NO_ERROR) {
*status = lStatus;
return handle;
}
{
Mutex::Autolock _l(mLock);
auto iter = mDeviceEffects.find(device);
if (iter != mDeviceEffects.end()) {
effect = iter->second;
} else {
effect = new DeviceEffectProxy(device, mMyCallback,
descriptor, mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT),
notifyFramesProcessed);
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/,
notifyFramesProcessed);
lStatus = handle->initCheck();
if (lStatus == NO_ERROR) {
lStatus = effect->addHandle(handle.get());
if (lStatus == NO_ERROR) {
lStatus = effect->init(patches);
if (lStatus == NAME_NOT_FOUND) {
lStatus = NO_ERROR;
}
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
mDeviceEffects.emplace(device, effect);
}
}
}
}
if (enabled != nullptr) {
*enabled = (int)effect->isEnabled();
}
*status = lStatus;
return handle;
}
status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility(
const effect_descriptor_t *desc) {
const sp<EffectsFactoryHalInterface> effectsFactory =
audioflinger::EffectConfiguration::getEffectsFactoryHal();
if (effectsFactory == nullptr) {
return BAD_VALUE;
}
static const AudioHalVersionInfo sMinDeviceEffectHalVersion =
AudioHalVersionInfo(AudioHalVersionInfo::Type::HIDL, 6, 0);
static const AudioHalVersionInfo halVersion =
audioflinger::EffectConfiguration::getAudioHalVersionInfo();
// We can trust AIDL generated AudioHalVersionInfo comparison operator (based on std::tie) as
// long as the type, major and minor sequence doesn't change in the definition.
if (((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC
&& (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC)
|| halVersion < sMinDeviceEffectHalVersion) {
ALOGW("%s() non pre/post processing device effect %s or incompatible API version %s",
__func__, desc->name, halVersion.toString().c_str());
return BAD_VALUE;
}
return NO_ERROR;
}
status_t AudioFlinger::DeviceEffectManager::createEffectHal(
const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
sp<EffectHalInterface> *effect) {
status_t status = NO_INIT;
const sp<EffectsFactoryHalInterface> effectsFactory =
audioflinger::EffectConfiguration::getEffectsFactoryHal();
if (effectsFactory != 0) {
status = effectsFactory->createEffect(
pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect);
}
return status;
}
void AudioFlinger::DeviceEffectManager::dump(int fd)
NO_THREAD_SAFETY_ANALYSIS // conditional try lock
{
const bool locked = dumpTryLock(mLock);
if (!locked) {
String8 result("DeviceEffectManager may be deadlocked\n");
write(fd, result.string(), result.size());
}
String8 heading("\nDevice Effects:\n");
write(fd, heading.string(), heading.size());
for (const auto& iter : mDeviceEffects) {
String8 outStr;
outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
::android::toString(iter.first.mType).c_str(), iter.first.getAddress());
write(fd, outStr.string(), outStr.size());
iter.second->dump(fd, 4);
}
if (locked) {
mLock.unlock();
}
}
size_t AudioFlinger::DeviceEffectManager::removeEffect(const sp<DeviceEffectProxy>& effect)
{
Mutex::Autolock _l(mLock);
mDeviceEffects.erase(effect->device());
return mDeviceEffects.size();
}
bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle(
EffectHandle *handle, bool unpinIfLast) {
sp<EffectBase> effectBase = handle->effect().promote();
if (effectBase == nullptr) {
return false;
}
sp<DeviceEffectProxy> effect = effectBase->asDeviceEffectProxy();
if (effect == nullptr) {
return false;
}
// restore suspended effects if the disconnected handle was enabled and the last one.
bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
if (remove) {
mManager.removeEffect(effect);
if (handle->enabled()) {
effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
}
}
return true;
}
} // namespace android