Merge "benchmark: Fix NPD in Encode::deInitCodec()"
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index f56e1b5..bd6af83 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -22,6 +22,12 @@
namespace.default.search.paths = /apex/com.android.media.swcodec/${LIB}
namespace.default.asan.search.paths = /apex/com.android.media.swcodec/${LIB}
+# Below lines are required to be able to access libs in APEXes which are
+# actually symlinks to the files under /system/lib. The symlinks exist for
+# bundled APEXes to reduce space.
+namespace.default.permitted.paths = /system/${LIB}
+namespace.default.asan.permitted.paths = /system/${LIB}
+
namespace.default.links = platform
# TODO: replace the following when apex has a way to auto-generate this list
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index defc94f..7b447d3 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -23,6 +23,7 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_SYSTEM_EXT_MODULE:= true
LOCAL_MODULE:= stagefright
include $(BUILD_EXECUTABLE)
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 5303631..6a8db22 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -474,7 +474,7 @@
void AudioStream::MyPlayerBase::registerWithAudioManager() {
if (!mRegistered) {
- init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA);
+ init(android::PLAYER_TYPE_AAUDIO, AAudioConvert_usageToInternal(mParent->getUsage()));
mRegistered = true;
}
}
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index cf11936..28190ea 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -48,12 +48,13 @@
effect_callback_t cbf,
void* user,
audio_session_t sessionId,
- audio_io_handle_t io
+ audio_io_handle_t io,
+ const AudioDeviceTypeAddr& device
)
: mStatus(NO_INIT), mOpPackageName(opPackageName)
{
AutoMutex lock(mConstructLock);
- mStatus = set(type, uuid, priority, cbf, user, sessionId, io);
+ mStatus = set(type, uuid, priority, cbf, user, sessionId, io, device);
}
AudioEffect::AudioEffect(const char *typeStr,
@@ -63,7 +64,8 @@
effect_callback_t cbf,
void* user,
audio_session_t sessionId,
- audio_io_handle_t io
+ audio_io_handle_t io,
+ const AudioDeviceTypeAddr& device
)
: mStatus(NO_INIT), mOpPackageName(opPackageName)
{
@@ -87,7 +89,7 @@
}
AutoMutex lock(mConstructLock);
- mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io);
+ mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io, device);
}
status_t AudioEffect::set(const effect_uuid_t *type,
@@ -96,7 +98,8 @@
effect_callback_t cbf,
void* user,
audio_session_t sessionId,
- audio_io_handle_t io)
+ audio_io_handle_t io,
+ const AudioDeviceTypeAddr& device)
{
sp<IEffect> iEffect;
sp<IMemory> cblk;
@@ -109,6 +112,10 @@
return INVALID_OPERATION;
}
+ if (sessionId == AUDIO_SESSION_DEVICE && io != AUDIO_IO_HANDLE_NONE) {
+ ALOGW("IO handle should not be specified for device effect");
+ return BAD_VALUE;
+ }
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
ALOGE("set(): Could not get audioflinger");
@@ -133,7 +140,7 @@
mClientPid = IPCThreadState::self()->getCallingPid();
iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
- mIEffectClient, priority, io, mSessionId, mOpPackageName, mClientPid,
+ mIEffectClient, priority, io, mSessionId, device, mOpPackageName, mClientPid,
&mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
@@ -167,7 +174,7 @@
ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
mStatus, mEnabled, mClientPid);
- if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+ if (!audio_is_global_session(mSessionId)) {
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
}
@@ -180,7 +187,7 @@
ALOGV("Destructor %p", this);
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
- if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+ if (!audio_is_global_session(mSessionId)) {
AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
}
if (mIEffect != NULL) {
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 90783eb..3f8b52b 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -658,6 +658,7 @@
int32_t priority,
audio_io_handle_t output,
audio_session_t sessionId,
+ const AudioDeviceTypeAddr& device,
const String16& opPackageName,
pid_t pid,
status_t *status,
@@ -666,12 +667,11 @@
{
Parcel data, reply;
sp<IEffect> effect;
-
if (pDesc == NULL) {
if (status != NULL) {
*status = BAD_VALUE;
}
- return effect;
+ return nullptr;
}
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
@@ -680,6 +680,12 @@
data.writeInt32(priority);
data.writeInt32((int32_t) output);
data.writeInt32(sessionId);
+ if (data.writeParcelable(device) != NO_ERROR) {
+ if (status != NULL) {
+ *status = NO_INIT;
+ }
+ return nullptr;
+ }
data.writeString16(opPackageName);
data.writeInt32((int32_t) pid);
@@ -1378,14 +1384,18 @@
int32_t priority = data.readInt32();
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
audio_session_t sessionId = (audio_session_t) data.readInt32();
+ AudioDeviceTypeAddr device;
+ status_t status = NO_ERROR;
+ if ((status = data.readParcelable(&device)) != NO_ERROR) {
+ return status;
+ }
const String16 opPackageName = data.readString16();
pid_t pid = (pid_t)data.readInt32();
- status_t status = NO_ERROR;
int id = 0;
int enabled = 0;
- sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId,
+ sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId, device,
opPackageName, pid, &status, &id, &enabled);
reply->writeInt32(status);
reply->writeInt32(id);
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index 6bd4137..f17d737 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -362,17 +362,21 @@
* (AudioTrack or MediaPLayer) within the same audio session.
* io: HAL audio output or input stream to which this effect must be attached. Leave at 0 for
* automatic output selection by AudioFlinger.
+ * device: An audio device descriptor. Only used when "sessionID" is AUDIO_SESSION_DEVICE.
+ * Specifies the audio device type and address the effect must be attached to.
+ * If "sessionID" is AUDIO_SESSION_DEVICE then "io" must be AUDIO_IO_HANDLE_NONE.
*/
AudioEffect(const effect_uuid_t *type,
const String16& opPackageName,
const effect_uuid_t *uuid = NULL,
- int32_t priority = 0,
- effect_callback_t cbf = NULL,
- void* user = NULL,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
- );
+ int32_t priority = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
+ audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
+ const AudioDeviceTypeAddr& device = {}
+ );
/* Constructor.
* Same as above but with type and uuid specified by character strings
@@ -384,7 +388,8 @@
effect_callback_t cbf = NULL,
void* user = NULL,
audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
+ const AudioDeviceTypeAddr& device = {}
);
/* Terminates the AudioEffect and unregisters it from AudioFlinger.
@@ -406,7 +411,8 @@
effect_callback_t cbf = NULL,
void* user = NULL,
audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
+ const AudioDeviceTypeAddr& device = {}
);
/* Result of constructing the AudioEffect. This must be checked
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 8e8a1f5..1c35ff0 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -453,6 +453,7 @@
// AudioFlinger doesn't take over handle reference from client
audio_io_handle_t output,
audio_session_t sessionId,
+ const AudioDeviceTypeAddr& device,
const String16& callingPackage,
pid_t pid,
status_t *status,
diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
index d390467..b44043a 100644
--- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
+++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp
@@ -26,6 +26,16 @@
return mType == other.mType && mAddress == other.mAddress;
}
+bool AudioDeviceTypeAddr::operator<(const AudioDeviceTypeAddr& other) const {
+ if (mType < other.mType) return true;
+ if (mType > other.mType) return false;
+
+ if (mAddress < other.mAddress) return true;
+ // if (mAddress > other.mAddress) return false;
+
+ return false;
+}
+
void AudioDeviceTypeAddr::reset() {
mType = AUDIO_DEVICE_NONE;
mAddress = "";
diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
index acc37ca..60ea78e 100644
--- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
+++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h
@@ -39,6 +39,8 @@
AudioDeviceTypeAddr& operator= (const AudioDeviceTypeAddr&) = default;
+ bool operator<(const AudioDeviceTypeAddr& other) const;
+
void reset();
status_t readFromParcel(const Parcel *parcel) override;
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index 42c16c0..f529cd1 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -28,6 +28,7 @@
#include <common/all-versions/VersionUtils.h>
#include "DeviceHalHidl.h"
+#include "EffectHalHidl.h"
#include "HidlUtils.h"
#include "StreamHalHidl.h"
#include "VersionUtils.h"
@@ -43,6 +44,8 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::CPP_VERSION;
+using EffectHalHidl = ::android::effect::CPP_VERSION::EffectHalHidl;
+
namespace {
status_t deviceAddressFromHal(
@@ -385,6 +388,36 @@
}
#endif
+#if MAJOR_VERSION >= 6
+status_t DeviceHalHidl::addDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("addDeviceEffect", mDevice->addDeviceEffect(
+ static_cast<AudioPortHandle>(device),
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+#else
+status_t DeviceHalHidl::addDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+#endif
+
+#if MAJOR_VERSION >= 6
+status_t DeviceHalHidl::removeDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ if (mDevice == 0) return NO_INIT;
+ return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect(
+ static_cast<AudioPortHandle>(device),
+ static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+#else
+status_t DeviceHalHidl::removeDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+#endif
+
status_t DeviceHalHidl::dump(int fd) {
if (mDevice == 0) return NO_INIT;
native_handle_t* hidlHandle = native_handle_create(1, 0);
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index f7d465f..d342d4a 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -113,6 +113,9 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+ status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+ status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
virtual status_t dump(int fd);
private:
diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp
index dfbb6b2..8021d92 100644
--- a/media/libaudiohal/impl/DeviceHalLocal.cpp
+++ b/media/libaudiohal/impl/DeviceHalLocal.cpp
@@ -206,6 +206,17 @@
}
#endif
+// Local HAL implementation does not support effects
+status_t DeviceHalLocal::addDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+
+status_t DeviceHalLocal::removeDeviceEffect(
+ audio_port_handle_t device __unused, sp<EffectHalInterface> effect __unused) {
+ return INVALID_OPERATION;
+}
+
status_t DeviceHalLocal::dump(int fd) {
return mDev->dump(mDev, fd);
}
diff --git a/media/libaudiohal/impl/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h
index 36db72e..d85e2a7 100644
--- a/media/libaudiohal/impl/DeviceHalLocal.h
+++ b/media/libaudiohal/impl/DeviceHalLocal.h
@@ -106,6 +106,9 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+ status_t addDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+ status_t removeDeviceEffect(audio_port_handle_t device, sp<EffectHalInterface> effect) override;
+
virtual status_t dump(int fd);
void closeOutputStream(struct audio_stream_out *stream_out);
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index ba7b195..867b72d 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -105,12 +105,26 @@
status_t EffectsFactoryHalHidl::createEffect(
const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
- sp<EffectHalInterface> *effect) {
+ int32_t deviceId __unused, sp<EffectHalInterface> *effect) {
if (mEffectsFactory == 0) return NO_INIT;
Uuid hidlUuid;
HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
Result retval = Result::NOT_INITIALIZED;
- Return<void> ret = mEffectsFactory->createEffect(
+ Return<void> ret;
+#if MAJOR_VERSION >= 6
+ ret = mEffectsFactory->createEffect(
+ hidlUuid, sessionId, ioId, deviceId,
+ [&](Result r, const sp<IEffect>& result, uint64_t effectId) {
+ retval = r;
+ if (retval == Result::OK) {
+ *effect = new EffectHalHidl(result, effectId);
+ }
+ });
+#else
+ if (sessionId == AUDIO_SESSION_DEVICE && ioId == AUDIO_IO_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
+ ret = mEffectsFactory->createEffect(
hidlUuid, sessionId, ioId,
[&](Result r, const sp<IEffect>& result, uint64_t effectId) {
retval = r;
@@ -118,6 +132,7 @@
*effect = new EffectHalHidl(result, effectId);
}
});
+#endif
if (ret.isOk()) {
if (retval == Result::OK) return OK;
else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index 2828513..dece1bb 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -49,7 +49,7 @@
// To release the effect engine, it is necessary to release references
// to the returned effect object.
virtual status_t createEffect(const effect_uuid_t *pEffectUuid,
- int32_t sessionId, int32_t ioId,
+ int32_t sessionId, int32_t ioId, int32_t deviceId,
sp<EffectHalInterface> *effect);
virtual status_t dumpEffects(int fd);
diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
index 2200a7f..1e04b21 100644
--- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
#define ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H
+#include <media/audiohal/EffectHalInterface.h>
#include <media/MicrophoneInfo.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -111,6 +112,11 @@
// List microphones
virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
+ virtual status_t addDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
+ virtual status_t removeDeviceEffect(
+ audio_port_handle_t device, sp<EffectHalInterface> effect) = 0;
+
virtual status_t dump(int fd) = 0;
protected:
diff --git a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
index 316a46c..3a76f9f 100644
--- a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
@@ -41,7 +41,7 @@
// To release the effect engine, it is necessary to release references
// to the returned effect object.
virtual status_t createEffect(const effect_uuid_t *pEffectUuid,
- int32_t sessionId, int32_t ioId,
+ int32_t sessionId, int32_t ioId, int32_t deviceId,
sp<EffectHalInterface> *effect) = 0;
virtual status_t dumpEffects(int fd) = 0;
diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp
index 21d25e1..6d31c12 100644
--- a/media/libaudioprocessing/BufferProviders.cpp
+++ b/media/libaudioprocessing/BufferProviders.cpp
@@ -164,6 +164,7 @@
if (mEffectsFactory->createEffect(&sDwnmFxDesc.uuid,
sessionId,
SESSION_ID_INVALID_AND_IGNORED,
+ AUDIO_PORT_HANDLE_NONE,
&mDownmixInterface) != 0) {
ALOGE("DownmixerBufferProvider() error creating downmixer effect");
mDownmixInterface.clear();
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index c1ce513..dcdf634 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -254,7 +254,8 @@
return ret;
}
-int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
+int doEffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, int32_t deviceId,
+ effect_handle_t *pHandle)
{
list_elem_t *e = gLibraryList;
lib_entry_t *l = NULL;
@@ -268,9 +269,9 @@
}
ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
- uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
- uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
- uuid->node[3],uuid->node[4],uuid->node[5]);
+ uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
+ uuid->clockSeq, uuid->node[0], uuid->node[1], uuid->node[2],
+ uuid->node[3], uuid->node[4], uuid->node[5]);
ret = init();
@@ -282,17 +283,29 @@
pthread_mutex_lock(&gLibLock);
ret = findEffect(NULL, uuid, &l, &d);
- if (ret < 0){
+ if (ret < 0) {
// Sub effects are not associated with the library->effects,
// so, findEffect will fail. Search for the effect in gSubEffectList.
ret = findSubEffect(uuid, &l, &d);
- if (ret < 0 ) {
+ if (ret < 0) {
goto exit;
}
}
// create effect in library
- ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
+ if (sessionId == AUDIO_SESSION_DEVICE) {
+ if (l->desc->version >= EFFECT_LIBRARY_API_VERSION_3_1) {
+ ALOGI("EffectCreate() create_effect_3_1");
+ ret = l->desc->create_effect_3_1(uuid, sessionId, ioId, deviceId, &itfe);
+ } else {
+ ALOGE("EffectCreate() cannot create device effect on library with API version < 3.1");
+ ret = -ENOSYS;
+ }
+ } else {
+ ALOGI("EffectCreate() create_effect");
+ ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
+ }
+
if (ret != 0) {
ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
goto exit;
@@ -324,6 +337,16 @@
return ret;
}
+int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId,
+ effect_handle_t *pHandle) {
+ return doEffectCreate(uuid, sessionId, ioId, AUDIO_PORT_HANDLE_NONE, pHandle);
+}
+
+int EffectCreateOnDevice(const effect_uuid_t *uuid, int32_t deviceId, int32_t ioId,
+ effect_handle_t *pHandle) {
+ return doEffectCreate(uuid, AUDIO_SESSION_DEVICE, ioId, deviceId, pHandle);
+}
+
int EffectRelease(effect_handle_t handle)
{
effect_entry_t *fx;
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index 29dbc9c..1936343 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -27,6 +27,8 @@
extern "C" {
#endif
+#define EFFECT_LIBRARY_API_VERSION_CURRENT EFFECT_LIBRARY_API_VERSION_3_1
+
#define PROPERTY_IGNORE_EFFECTS "ro.audio.ignore_effects"
typedef struct list_elem_s {
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 052a88b..505be7c 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -94,7 +94,7 @@
}
uint32_t majorVersion = EFFECT_API_VERSION_MAJOR(description->version);
- uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION);
+ uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION_CURRENT);
if (majorVersion != expectedMajorVersion) {
ALOGE("Unsupported major version %#08x, expected %#08x for library %s",
majorVersion, expectedMajorVersion, path);
diff --git a/media/libeffects/factory/include/media/EffectsFactoryApi.h b/media/libeffects/factory/include/media/EffectsFactoryApi.h
index a5a12eb..8f7239e 100644
--- a/media/libeffects/factory/include/media/EffectsFactoryApi.h
+++ b/media/libeffects/factory/include/media/EffectsFactoryApi.h
@@ -119,6 +119,36 @@
////////////////////////////////////////////////////////////////////////////////
//
+// Function: EffectCreateOnDevice
+//
+// Description: Same as EffectCreate but uesed when creating an effect attached to a
+// particular audio device instance
+//
+// Input:
+// pEffectUuid: pointer to the effect uuid.
+// deviceId: identifies the sink or source device this effect is directed to in
+// audio HAL. Must be specified if sessionId is AUDIO_SESSION_DEVICE.
+// deviceId is the audio_port_handle_t used for the device when the audio
+// patch is created at the audio HAL.//
+// ioId: identifies the output or input stream this effect is directed to at audio HAL.
+// For future use especially with tunneled HW accelerated effects
+// Input/Output:
+// pHandle: address where to return the effect handle.
+//
+// Output:
+// returned value: 0 successful operation.
+// -ENODEV factory failed to initialize
+// -EINVAL invalid pEffectUuid or pHandle
+// -ENOENT no effect with this uuid found
+// *pHandle: updated with the effect handle.
+//
+////////////////////////////////////////////////////////////////////////////////
+ANDROID_API
+int EffectCreateOnDevice(const effect_uuid_t *pEffectUuid, int32_t deviceId, int32_t ioId,
+ effect_handle_t *pHandle);
+
+////////////////////////////////////////////////////////////////////////////////
+//
// Function: EffectRelease
//
// Description: Releases the effect engine whose handle is given as argument.
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
index 89d6ce2..1179d6c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Runs Media Benchmark Tests">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="false" />
<option name="test-file-name" value="MediaBenchmarkTest.apk" />
</target_preparer>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index 07d414d..c41f198 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -57,7 +57,7 @@
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
private static final String mStatsFile =
- mContext.getFilesDir() + "/Decoder." + System.currentTimeMillis() + ".csv";
+ mContext.getExternalFilesDir(null) + "/Decoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "DecoderTest";
private static final long PER_TEST_TIMEOUT_MS = 60000;
private static final boolean DEBUG = false;
@@ -114,6 +114,7 @@
Stats mStats = new Stats();
boolean status = mStats.writeStatsHeader(mStatsFile);
assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
}
@Test(timeout = PER_TEST_TIMEOUT_MS)
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 00e5e21..831467a 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -57,7 +57,7 @@
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mOutputFilePath = mContext.getString(R.string.output_file_path);
private static final String mStatsFile =
- mContext.getFilesDir() + "/Encoder." + System.currentTimeMillis() + ".csv";
+ mContext.getExternalFilesDir(null) + "/Encoder." + System.currentTimeMillis() + ".csv";
private static final String TAG = "EncoderTest";
private static final long PER_TEST_TIMEOUT_MS = 120000;
private static final boolean DEBUG = false;
@@ -94,6 +94,7 @@
Stats mStats = new Stats();
boolean status = mStats.writeStatsHeader(mStatsFile);
assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
}
@Test(timeout = PER_TEST_TIMEOUT_MS)
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
index a33ecfe..6b7aad1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/ExtractorTest.java
@@ -50,8 +50,8 @@
private static Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
- private static final String mStatsFile =
- mContext.getFilesDir() + "/Extractor." + System.currentTimeMillis() + ".csv";
+ private static final String mStatsFile = mContext.getExternalFilesDir(null) + "/Extractor."
+ + System.currentTimeMillis() + ".csv";
private static final String TAG = "ExtractorTest";
private String mInputFileName;
private int mTrackId;
@@ -84,6 +84,7 @@
Stats mStats = new Stats();
boolean status = mStats.writeStatsHeader(mStatsFile);
assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
}
@Test
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
index b69c57b..2efdba2 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/MuxerTest.java
@@ -58,7 +58,7 @@
InstrumentationRegistry.getInstrumentation().getTargetContext();
private static final String mInputFilePath = mContext.getString(R.string.input_file_path);
private static final String mStatsFile =
- mContext.getFilesDir() + "/Muxer." + System.currentTimeMillis() + ".csv";
+ mContext.getExternalFilesDir(null) + "/Muxer." + System.currentTimeMillis() + ".csv";
private static final String TAG = "MuxerTest";
private static final Map<String, Integer> mMapFormat = new Hashtable<String, Integer>() {
{
@@ -106,6 +106,7 @@
Stats mStats = new Stats();
boolean status = mStats.writeStatsHeader(mStatsFile);
assertTrue("Unable to open stats file for writing!", status);
+ Log.d(TAG, "Saving Benchmark results in: " + mStatsFile);
}
@Test
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 02141a8..a661470 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -174,12 +174,19 @@
}
bool modifyDefaultAudioEffectsAllowed() {
+ return modifyDefaultAudioEffectsAllowed(
+ IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+}
+
+bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid) {
+ if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
+
static const String16 sModifyDefaultAudioEffectsAllowed(
"android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
- bool ok = PermissionCache::checkCallingPermission(sModifyDefaultAudioEffectsAllowed);
-
- if (!ok) ALOGE("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS");
+ bool ok = PermissionCache::checkPermission(sModifyDefaultAudioEffectsAllowed, pid, uid);
+ ALOGE_IF(!ok, "%s(): android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS denied for uid %d",
+ __func__, uid);
return ok;
}
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e1089d5..f5768bd 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -85,6 +85,7 @@
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool modifyDefaultAudioEffectsAllowed();
+bool modifyDefaultAudioEffectsAllowed(pid_t pid, uid_t uid);
bool dumpAllowed();
bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
bool bypassInterruptionPolicyAllowed(pid_t pid, uid_t uid);
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index de8c7e7..c58360d 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -9,6 +9,7 @@
"AudioStreamOut.cpp",
"AudioWatchdog.cpp",
"BufLog.cpp",
+ "DeviceEffectManager.cpp",
"Effects.cpp",
"FastCapture.cpp",
"FastCaptureDumpState.cpp",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 525ac68..10a9c63 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -170,6 +170,7 @@
mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),
mGlobalEffectEnableTime(0),
mPatchPanel(this),
+ mDeviceEffectManager(this),
mSystemReady(false)
{
// unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enum
@@ -382,6 +383,24 @@
}
}
+status_t AudioFlinger::addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
+ if (audioHwDevice == nullptr) {
+ return NO_INIT;
+ }
+ return audioHwDevice->hwDevice()->addDeviceEffect(deviceId, effect);
+}
+
+status_t AudioFlinger::removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
+ if (audioHwDevice == nullptr) {
+ return NO_INIT;
+ }
+ return audioHwDevice->hwDevice()->removeDeviceEffect(deviceId, effect);
+}
+
static const char * const audio_interfaces[] = {
AUDIO_HARDWARE_MODULE_ID_PRIMARY,
AUDIO_HARDWARE_MODULE_ID_A2DP,
@@ -558,6 +577,8 @@
mPatchPanel.dump(fd);
+ mDeviceEffectManager.dump(fd);
+
// dump external setParameters
auto dumpLogger = [fd](SimpleLog& logger, const char* name) {
dprintf(fd, "\n%s setParameters:\n", name);
@@ -2944,7 +2965,7 @@
Mutex::Autolock _l(t->mLock);
for (size_t j = 0; j < t->mEffectChains.size(); j++) {
sp<EffectChain> ec = t->mEffectChains[j];
- if (ec->sessionId() > AUDIO_SESSION_OUTPUT_MIX) {
+ if (!audio_is_global_session(ec->sessionId())) {
chains.push(ec);
}
}
@@ -2971,7 +2992,7 @@
for (size_t i = 0; i < chains.size(); i++) {
sp<EffectChain> ec = chains[i];
int sessionid = ec->sessionId();
- sp<ThreadBase> t = ec->mThread.promote();
+ sp<ThreadBase> t = ec->thread().promote();
if (t == 0) {
continue;
}
@@ -2994,7 +3015,7 @@
effect->unPin();
t->removeEffect_l(effect, /*release*/ true);
if (effect->purgeHandles()) {
- t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
+ effect->checkSuspendOnEffectEnabled(false, true /*threadLocked*/);
}
removedEffects.push_back(effect);
}
@@ -3294,6 +3315,7 @@
int32_t priority,
audio_io_handle_t io,
audio_session_t sessionId,
+ const AudioDeviceTypeAddr& device,
const String16& opPackageName,
pid_t pid,
status_t *status,
@@ -3346,6 +3368,17 @@
lStatus = BAD_VALUE;
goto Exit;
}
+ } else if (sessionId == AUDIO_SESSION_DEVICE) {
+ if (!modifyDefaultAudioEffectsAllowed(pid, callingUid)) {
+ ALOGE("%s: device effect permission denied for uid %d", __func__, callingUid);
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+ if (io != AUDIO_IO_HANDLE_NONE) {
+ ALOGE("%s: io handle should not be specified for device effect", __func__);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
} else {
// general sessionId.
@@ -3381,7 +3414,7 @@
// check recording permission for visualizer
if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
// TODO: Do we need to start/stop op - i.e. is there recording being performed?
- !recordingAllowed(opPackageName, pid, IPCThreadState::self()->getCallingUid())) {
+ !recordingAllowed(opPackageName, pid, callingUid)) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -3398,6 +3431,23 @@
Mutex::Autolock _l(mLock);
+ if (sessionId == AUDIO_SESSION_DEVICE) {
+ sp<Client> client = registerPid(pid);
+ ALOGV("%s device type %d address %s", __func__, device.mType, device.getAddress());
+ handle = mDeviceEffectManager.createEffect_l(
+ &desc, device, client, effectClient, mPatchPanel.patches_l(),
+ enabled, &lStatus);
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ // remove local strong reference to Client with mClientLock held
+ Mutex::Autolock _cl(mClientLock);
+ client.clear();
+ } else {
+ // handle must be valid here, but check again to be safe.
+ if (handle.get() != nullptr && id != nullptr) *id = handle->id();
+ }
+ goto Register;
+ }
+
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
// If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
@@ -3479,7 +3529,7 @@
sp<Client> client = registerPid(pid);
// create effect on selected output thread
- bool pinned = (sessionId > AUDIO_SESSION_OUTPUT_MIX) && isSessionAcquired_l(sessionId);
+ bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus, pinned);
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
@@ -3492,9 +3542,10 @@
}
}
+Register:
if (lStatus == NO_ERROR || lStatus == ALREADY_EXISTS) {
// Check CPU and memory usage
- sp<EffectModule> effect = handle->effect().promote();
+ sp<EffectBase> effect = handle->effect().promote();
if (effect != nullptr) {
status_t rStatus = effect->updatePolicyState();
if (rStatus != NO_ERROR) {
@@ -3604,7 +3655,7 @@
// if the move request is not received from audio policy manager, the effect must be
// re-registered with the new strategy and output
if (dstChain == 0) {
- dstChain = effect->chain().promote();
+ dstChain = effect->callback()->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
status = NO_INIT;
@@ -3654,7 +3705,7 @@
goto Exit;
}
- dstChain = effect->chain().promote();
+ dstChain = effect->callback()->chain().promote();
if (dstChain == 0) {
thread->addEffect_l(effect);
status = INVALID_OPERATION;
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9e3ec92..a43a6dc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -232,6 +232,7 @@
int32_t priority,
audio_io_handle_t io,
audio_session_t sessionId,
+ const AudioDeviceTypeAddr& device,
const String16& opPackageName,
pid_t pid,
status_t *status /*non-NULL*/,
@@ -307,6 +308,12 @@
static int onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration);
static void onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration);
+
+ status_t addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect);
+ status_t removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect);
+
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;
@@ -532,9 +539,14 @@
class AsyncCallbackThread;
class Track;
class RecordTrack;
+ class EffectBase;
class EffectModule;
class EffectHandle;
class EffectChain;
+ class DeviceEffectProxy;
+ class DeviceEffectManager;
+ class PatchPanel;
+ class DeviceEffectManagerCallback;
struct AudioStreamIn;
struct TeePatch;
@@ -572,9 +584,11 @@
#include "Threads.h"
+#include "PatchPanel.h"
+
#include "Effects.h"
-#include "PatchPanel.h"
+#include "DeviceEffectManager.h"
// Find io handle by session id.
// Preference is given to an io handle with a matching effect chain to session id.
@@ -920,6 +934,8 @@
PatchPanel mPatchPanel;
sp<EffectsFactoryHalInterface> mEffectsFactoryHal;
+ DeviceEffectManager mDeviceEffectManager;
+
bool mSystemReady;
SimpleLog mRejectedSetParameterLog;
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
new file mode 100644
index 0000000..87a4c6e
--- /dev/null
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -0,0 +1,277 @@
+/*
+**
+** 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 <media/audiohal/EffectsFactoryHalInterface.h>
+
+// ----------------------------------------------------------------------------
+
+
+namespace android {
+
+void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch) {
+ ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
+ __func__, handle, patch.mHalHandle,
+ patch.mAudioPatch.num_sinks,
+ patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
+
+ mCommandThread->createAudioPatchCommand(handle, patch);
+}
+
+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::releaseAudioPatch(audio_patch_handle_t handle) {
+ ALOGV("%s", __func__);
+ mCommandThread->releaseAudioPatchCommand(handle);
+}
+
+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) {
+ sp<DeviceEffectProxy> effect;
+ sp<EffectHandle> handle;
+ status_t lStatus;
+
+ lStatus = checkEffectCompatibility(descriptor);
+ if (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));
+ }
+ // create effect handle and connect it to effect module
+ handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/);
+ lStatus = handle->initCheck();
+ if (lStatus == NO_ERROR) {
+ lStatus = effect->addHandle(handle.get());
+ if (lStatus == NO_ERROR) {
+ effect->init(patches);
+ mDeviceEffects.emplace(device, effect);
+ }
+ }
+ }
+ if (enabled != NULL) {
+ *enabled = (int)effect->isEnabled();
+ }
+ *status = lStatus;
+ return handle;
+}
+
+status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility(
+ const effect_descriptor_t *desc) {
+
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC
+ && (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) {
+ ALOGW("%s() non pre/post processing device effect %s", __func__, desc->name);
+ 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;
+ sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory();
+ if (effectsFactory != 0) {
+ status = effectsFactory->createEffect(
+ pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect);
+ }
+ return status;
+}
+
+void AudioFlinger::DeviceEffectManager::dump(int fd) {
+ const bool locked = dumpTryLock(mLock);
+ if (!locked) {
+ String8 result("DeviceEffectManager may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n"));
+ 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;
+}
+
+// ----------- DeviceEffectManager::CommandThread implementation ----------
+
+
+AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread()
+{
+ Mutex::Autolock _l(mLock);
+ mCommands.clear();
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef()
+{
+ run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO);
+}
+
+bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop()
+{
+ mLock.lock();
+ while (!exitPending())
+ {
+ while (!mCommands.empty() && !exitPending()) {
+ sp<Command> command = mCommands.front();
+ mCommands.pop_front();
+ mLock.unlock();
+
+ switch (command->mCommand) {
+ case CREATE_AUDIO_PATCH: {
+ CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get();
+ ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle);
+ mManager.onCreateAudioPatch(data->mHandle, data->mPatch);
+ } break;
+ case RELEASE_AUDIO_PATCH: {
+ ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get();
+ ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle);
+ mManager.onReleaseAudioPatch(data->mHandle);
+ } break;
+ default:
+ ALOGW("CommandThread() unknown command %d", command->mCommand);
+ }
+ mLock.lock();
+ }
+
+ // At this stage we have either an empty command queue or the first command in the queue
+ // has a finite delay. So unless we are exiting it is safe to wait.
+ if (!exitPending()) {
+ ALOGV("CommandThread() going to sleep");
+ mWaitWorkCV.wait(mLock);
+ }
+ }
+ mLock.unlock();
+ return false;
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp<Command> command) {
+ Mutex::Autolock _l(mLock);
+ mCommands.push_back(command);
+ mWaitWorkCV.signal();
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand(
+ audio_patch_handle_t handle, const PatchPanel::Patch& patch)
+{
+ sp<Command> command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch));
+ ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle);
+ sendCommand(command);
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand(
+ audio_patch_handle_t handle)
+{
+ sp<Command> command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle));
+ ALOGV("CommandThread() adding release patch");
+ sendCommand(command);
+}
+
+void AudioFlinger::DeviceEffectManager::CommandThread::exit()
+{
+ ALOGV("CommandThread::exit");
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ // Note that we can call it from the thread loop if all other references have been released
+ // but it will safely return WOULD_BLOCK in this case
+ requestExitAndWait();
+}
+
+} // namespace android
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
new file mode 100644
index 0000000..14ff14d
--- /dev/null
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -0,0 +1,203 @@
+/*
+**
+** 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.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+// DeviceEffectManager is concealed within AudioFlinger, their lifetimes are the same.
+class DeviceEffectManager {
+public:
+ explicit DeviceEffectManager(AudioFlinger* audioFlinger)
+ : mCommandThread(new CommandThread(*this)), mAudioFlinger(*audioFlinger),
+ mMyCallback(new DeviceEffectManagerCallback(this)) {}
+
+ ~DeviceEffectManager() {
+ mCommandThread->exit();
+ }
+
+ sp<EffectHandle> 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);
+ void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
+ void releaseAudioPatch(audio_patch_handle_t handle);
+
+ size_t removeEffect(const sp<DeviceEffectProxy>& effect);
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect);
+ status_t addEffectToHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
+ sp<EffectHalInterface> effect) {
+ return mAudioFlinger.addEffectToHal(deviceId, hwModuleId, effect);
+ };
+ status_t removeEffectFromHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
+ sp<EffectHalInterface> effect) {
+ return mAudioFlinger.removeEffectFromHal(deviceId, hwModuleId, effect);
+ };
+
+ AudioFlinger& audioFlinger() const { return mAudioFlinger; }
+
+ void dump(int fd);
+
+private:
+
+ // Thread to execute create and release patch commands asynchronously. This is needed because
+ // PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service
+ // with mutex locked and effect management requires to call back into audio policy service
+ class Command;
+ class CommandThread : public Thread {
+ public:
+
+ enum {
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ };
+
+ CommandThread(DeviceEffectManager& manager)
+ : Thread(false), mManager(manager) {}
+ ~CommandThread() override;
+
+ // Thread virtuals
+ void onFirstRef() override;
+ bool threadLoop() override;
+
+ void exit();
+
+ void createAudioPatchCommand(audio_patch_handle_t handle,
+ const PatchPanel::Patch& patch);
+ void releaseAudioPatchCommand(audio_patch_handle_t handle);
+
+ private:
+ class CommandData;
+
+ // descriptor for requested tone playback event
+ class Command: public RefBase {
+ public:
+ Command() = default;
+ Command(int command, sp<CommandData> data)
+ : mCommand(command), mData(data) {}
+
+ int mCommand = -1;
+ sp<CommandData> mData;
+ };
+
+ class CommandData: public RefBase {
+ public:
+ virtual ~CommandData() = default;
+ };
+
+ class CreateAudioPatchData : public CommandData {
+ public:
+ CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch)
+ : mHandle(handle), mPatch(patch) {}
+
+ audio_patch_handle_t mHandle;
+ const PatchPanel::Patch mPatch;
+ };
+
+ class ReleaseAudioPatchData : public CommandData {
+ public:
+ ReleaseAudioPatchData(audio_patch_handle_t handle)
+ : mHandle(handle) {}
+
+ audio_patch_handle_t mHandle;
+ };
+
+ void sendCommand(sp<Command> command);
+
+ Mutex mLock;
+ Condition mWaitWorkCV;
+ std::deque <sp<Command>> mCommands; // list of pending commands
+ DeviceEffectManager& mManager;
+ };
+
+ void onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
+ void onReleaseAudioPatch(audio_patch_handle_t handle);
+
+ status_t checkEffectCompatibility(const effect_descriptor_t *desc);
+
+ Mutex mLock;
+ sp<CommandThread> mCommandThread;
+ AudioFlinger &mAudioFlinger;
+ const sp<DeviceEffectManagerCallback> mMyCallback;
+ std::map<AudioDeviceTypeAddr, sp<DeviceEffectProxy>> mDeviceEffects;
+};
+
+class DeviceEffectManagerCallback : public EffectCallbackInterface {
+public:
+ DeviceEffectManagerCallback(DeviceEffectManager *manager)
+ : mManager(*manager) {}
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) override {
+ return mManager.createEffectHal(pEffectUuid, sessionId, deviceId, effect);
+ }
+ status_t allocateHalBuffer(size_t size __unused,
+ sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect __unused) override { return false; }
+
+ audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
+ bool isOutput() const override { return false; }
+ bool isOffload() const override { return false; }
+ bool isOffloadOrDirect() const override { return false; }
+ bool isOffloadOrMmap() const override { return false; }
+
+ uint32_t sampleRate() const override { return 0; }
+ audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; }
+ uint32_t channelCount() const override { return 0; }
+ size_t frameCount() const override { return 0; }
+ uint32_t latency() const override { return 0; }
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect __unused) override {
+ return NO_ERROR;
+ }
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect __unused) override {
+ return NO_ERROR;
+ }
+
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left __unused, float right __unused) const override {}
+
+ // check if effects should be suspended or restored when a given effect is enable or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
+ bool enabled __unused, bool threadLocked __unused) override {}
+ void resetVolume() override {}
+ uint32_t strategy() const override { return 0; }
+ int32_t activeTrackCnt() const override { return 0; }
+ void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
+ void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
+
+ wp<EffectChain> chain() const override { return nullptr; }
+
+ int newEffectId() { return mManager.audioFlinger().nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); }
+
+ status_t addEffectToHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ return mManager.addEffectToHal(deviceId, hwModuleId, effect);
+ }
+ status_t removeEffectFromHal(audio_port_handle_t deviceId,
+ audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ return mManager.removeEffectFromHal(deviceId, hwModuleId, effect);
+ }
+private:
+ DeviceEffectManager& mManager;
+};
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index bbd0d2c..ca8fd2d 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -58,81 +58,115 @@
namespace android {
// ----------------------------------------------------------------------------
-// EffectModule implementation
+// EffectBase implementation
// ----------------------------------------------------------------------------
#undef LOG_TAG
-#define LOG_TAG "AudioFlinger::EffectModule"
+#define LOG_TAG "AudioFlinger::EffectBase"
-AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
+AudioFlinger::EffectBase::EffectBase(const sp<AudioFlinger::EffectCallbackInterface>& callback,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
: mPinned(pinned),
- mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
- mDescriptor(*desc),
- // clear mConfig to ensure consistent initial value of buffer framecount
- // in case buffers are associated by setInBuffer() or setOutBuffer()
- // prior to configure().
- mConfig{{}, {}},
- mStatus(NO_INIT), mState(IDLE),
- mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
- mDisableWaitCnt(0), // set by process() and updateState()
- mSuspended(false),
- mOffloaded(false),
- mAudioFlinger(thread->mAudioFlinger)
-#ifdef FLOAT_EFFECT_CHAIN
- , mSupportsFloat(false)
-#endif
+ mCallback(callback), mId(id), mSessionId(sessionId),
+ mDescriptor(*desc)
{
- ALOGV("Constructor %p pinned %d", this, pinned);
- int lStatus;
+}
- // create effect engine from effect factory
- mStatus = -ENODEV;
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- if (audioFlinger != 0) {
- sp<EffectsFactoryHalInterface> effectsFactory = audioFlinger->getEffectsFactory();
- if (effectsFactory != 0) {
- mStatus = effectsFactory->createEffect(
- &desc->uuid, sessionId, thread->id(), &mEffectInterface);
+// must be called with EffectModule::mLock held
+status_t AudioFlinger::EffectBase::setEnabled_l(bool enabled)
+{
+
+ ALOGV("setEnabled %p enabled %d", this, enabled);
+
+ if (enabled != isEnabled()) {
+ switch (mState) {
+ // going from disabled to enabled
+ case IDLE:
+ mState = STARTING;
+ break;
+ case STOPPED:
+ mState = RESTART;
+ break;
+ case STOPPING:
+ mState = ACTIVE;
+ break;
+
+ // going from enabled to disabled
+ case RESTART:
+ mState = STOPPED;
+ break;
+ case STARTING:
+ mState = IDLE;
+ break;
+ case ACTIVE:
+ mState = STOPPING;
+ break;
+ case DESTROYED:
+ return NO_ERROR; // simply ignore as we are being destroyed
+ }
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->disconnected()) {
+ h->setEnabled(enabled);
+ }
}
}
-
- if (mStatus != NO_ERROR) {
- return;
- }
- lStatus = init();
- if (lStatus < 0) {
- mStatus = lStatus;
- goto Error;
- }
-
- setOffloaded(thread->type() == ThreadBase::OFFLOAD, thread->id());
- ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
-
- return;
-Error:
- mEffectInterface.clear();
- ALOGV("Constructor Error %d", mStatus);
+ return NO_ERROR;
}
-AudioFlinger::EffectModule::~EffectModule()
+status_t AudioFlinger::EffectBase::setEnabled(bool enabled, bool fromHandle)
{
- ALOGV("Destructor %p", this);
- if (mEffectInterface != 0) {
- char uuidStr[64];
- AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
- ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
- this, uuidStr);
- release_l();
+ status_t status;
+ {
+ Mutex::Autolock _l(mLock);
+ status = setEnabled_l(enabled);
}
-
+ if (fromHandle) {
+ if (enabled) {
+ if (status != NO_ERROR) {
+ mCallback->checkSuspendOnEffectEnabled(this, false, false /*threadLocked*/);
+ } else {
+ mCallback->onEffectEnable(this);
+ }
+ } else {
+ mCallback->onEffectDisable(this);
+ }
+ }
+ return status;
}
-status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
+bool AudioFlinger::EffectBase::isEnabled() const
+{
+ switch (mState) {
+ case RESTART:
+ case STARTING:
+ case ACTIVE:
+ return true;
+ case IDLE:
+ case STOPPING:
+ case STOPPED:
+ case DESTROYED:
+ default:
+ return false;
+ }
+}
+
+void AudioFlinger::EffectBase::setSuspended(bool suspended)
+{
+ Mutex::Autolock _l(mLock);
+ mSuspended = suspended;
+}
+
+bool AudioFlinger::EffectBase::suspended() const
+{
+ Mutex::Autolock _l(mLock);
+ return mSuspended;
+}
+
+status_t AudioFlinger::EffectBase::addHandle(EffectHandle *handle)
{
status_t status;
@@ -171,7 +205,7 @@
return status;
}
-status_t AudioFlinger::EffectModule::updatePolicyState()
+status_t AudioFlinger::EffectBase::updatePolicyState()
{
status_t status = NO_ERROR;
bool doRegister = false;
@@ -188,14 +222,8 @@
doRegister = true;
mPolicyRegistered = mHandles.size() > 0;
if (mPolicyRegistered) {
- sp <EffectChain> chain = mChain.promote();
- sp <ThreadBase> thread = mThread.promote();
-
- if (thread == nullptr || chain == nullptr) {
- return INVALID_OPERATION;
- }
- io = thread->id();
- strategy = chain->strategy();
+ io = mCallback->io();
+ strategy = mCallback->strategy();
}
}
// enable effect when registered according to enable state requested by controlling handle
@@ -233,13 +261,13 @@
}
-ssize_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
+ssize_t AudioFlinger::EffectBase::removeHandle(EffectHandle *handle)
{
Mutex::Autolock _l(mLock);
return removeHandle_l(handle);
}
-ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+ssize_t AudioFlinger::EffectBase::removeHandle_l(EffectHandle *handle)
{
size_t size = mHandles.size();
size_t i;
@@ -263,19 +291,15 @@
}
}
- // Prevent calls to process() and other functions on effect interface from now on.
- // The effect engine will be released by the destructor when the last strong reference on
- // this object is released which can happen after next process is called.
if (mHandles.size() == 0 && !mPinned) {
mState = DESTROYED;
- mEffectInterface->close();
}
return mHandles.size();
}
// must be called with EffectModule::mLock held
-AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
+AudioFlinger::EffectHandle *AudioFlinger::EffectBase::controlHandle_l()
{
// the first valid handle in the list has control over the module
for (size_t i = 0; i < mHandles.size(); i++) {
@@ -289,22 +313,271 @@
}
// unsafe method called when the effect parent thread has been destroyed
-ssize_t AudioFlinger::EffectModule::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
+ssize_t AudioFlinger::EffectBase::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
{
ALOGV("disconnect() %p handle %p", this, handle);
+ if (mCallback->disconnectEffectHandle(handle, unpinIfLast)) {
+ return mHandles.size();
+ }
+
Mutex::Autolock _l(mLock);
ssize_t numHandles = removeHandle_l(handle);
if ((numHandles == 0) && (!mPinned || unpinIfLast)) {
- sp<AudioFlinger> af = mAudioFlinger.promote();
- if (af != 0) {
- mLock.unlock();
- af->updateOrphanEffectChains(this);
- mLock.lock();
- }
+ mLock.unlock();
+ mCallback->updateOrphanEffectChains(this);
+ mLock.lock();
}
return numHandles;
}
+bool AudioFlinger::EffectBase::purgeHandles()
+{
+ bool enabled = false;
+ Mutex::Autolock _l(mLock);
+ EffectHandle *handle = controlHandle_l();
+ if (handle != NULL) {
+ enabled = handle->enabled();
+ }
+ mHandles.clear();
+ return enabled;
+}
+
+void AudioFlinger::EffectBase::checkSuspendOnEffectEnabled(bool enabled, bool threadLocked) {
+ mCallback->checkSuspendOnEffectEnabled(this, enabled, threadLocked);
+}
+
+static String8 effectFlagsToString(uint32_t flags) {
+ String8 s;
+
+ s.append("conn. mode: ");
+ switch (flags & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
+ case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
+ case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
+ case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
+ case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("insert pref: ");
+ switch (flags & EFFECT_FLAG_INSERT_MASK) {
+ case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
+ case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
+ case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
+ case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ s.append("volume mgmt: ");
+ switch (flags & EFFECT_FLAG_VOLUME_MASK) {
+ case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
+ case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
+ case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
+ case EFFECT_FLAG_VOLUME_MONITOR: s.append("monitors volume"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+
+ uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
+ if (devind) {
+ s.append("device indication: ");
+ switch (devind) {
+ case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ s.append("input mode: ");
+ switch (flags & EFFECT_FLAG_INPUT_MASK) {
+ case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ s.append("output mode: ");
+ switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
+ case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
+ case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
+ case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
+ default: s.append("not set"); break;
+ }
+ s.append(", ");
+
+ uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
+ if (accel) {
+ s.append("hardware acceleration: ");
+ switch (accel) {
+ case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
+ case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
+ if (modeind) {
+ s.append("mode indication: ");
+ switch (modeind) {
+ case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
+ if (srcind) {
+ s.append("source indication: ");
+ switch (srcind) {
+ case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
+ default: s.append("unknown/reserved"); break;
+ }
+ s.append(", ");
+ }
+
+ if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
+ s.append("offloadable, ");
+ }
+
+ int len = s.length();
+ if (s.length() > 2) {
+ (void) s.lockBuffer(len);
+ s.unlockBuffer(len - 2);
+ }
+ return s;
+}
+
+void AudioFlinger::EffectBase::dump(int fd, const Vector<String16>& args __unused)
+{
+ String8 result;
+
+ result.appendFormat("\tEffect ID %d:\n", mId);
+
+ bool locked = AudioFlinger::dumpTryLock(mLock);
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ result.append("\t\tCould not lock Fx mutex:\n");
+ }
+
+ result.append("\t\tSession State Registered Enabled Suspended:\n");
+ result.appendFormat("\t\t%05d %03d %s %s %s\n",
+ mSessionId, mState, mPolicyRegistered ? "y" : "n",
+ mPolicyEnabled ? "y" : "n", mSuspended ? "y" : "n");
+
+ result.append("\t\tDescriptor:\n");
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ result.appendFormat("\t\t- UUID: %s\n", uuidStr);
+ AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+ result.appendFormat("\t\t- TYPE: %s\n", uuidStr);
+ result.appendFormat("\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
+ mDescriptor.apiVersion,
+ mDescriptor.flags,
+ effectFlagsToString(mDescriptor.flags).string());
+ result.appendFormat("\t\t- name: %s\n",
+ mDescriptor.name);
+
+ result.appendFormat("\t\t- implementor: %s\n",
+ mDescriptor.implementor);
+
+ result.appendFormat("\t\t%zu Clients:\n", mHandles.size());
+ result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
+ char buffer[256];
+ for (size_t i = 0; i < mHandles.size(); ++i) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->disconnected()) {
+ handle->dumpToBuffer(buffer, sizeof(buffer));
+ result.append(buffer);
+ }
+ }
+ if (locked) {
+ mLock.unlock();
+ }
+
+ write(fd, result.string(), result.length());
+}
+
+// ----------------------------------------------------------------------------
+// EffectModule implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackInterface>& callback,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned,
+ audio_port_handle_t deviceId)
+ : EffectBase(callback, desc, id, sessionId, pinned),
+ // clear mConfig to ensure consistent initial value of buffer framecount
+ // in case buffers are associated by setInBuffer() or setOutBuffer()
+ // prior to configure().
+ mConfig{{}, {}},
+ mStatus(NO_INIT),
+ mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
+ mDisableWaitCnt(0), // set by process() and updateState()
+ mOffloaded(false)
+#ifdef FLOAT_EFFECT_CHAIN
+ , mSupportsFloat(false)
+#endif
+{
+ ALOGV("Constructor %p pinned %d", this, pinned);
+ int lStatus;
+
+ // create effect engine from effect factory
+ mStatus = callback->createEffectHal(
+ &desc->uuid, sessionId, deviceId, &mEffectInterface);
+ if (mStatus != NO_ERROR) {
+ return;
+ }
+ lStatus = init();
+ if (lStatus < 0) {
+ mStatus = lStatus;
+ goto Error;
+ }
+
+ setOffloaded(callback->isOffload(), callback->io());
+ ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
+
+ return;
+Error:
+ mEffectInterface.clear();
+ ALOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+ ALOGV("Destructor %p", this);
+ if (mEffectInterface != 0) {
+ char uuidStr[64];
+ AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+ ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+ this, uuidStr);
+ release_l();
+ }
+
+}
+
+ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+{
+ ssize_t status = EffectBase::removeHandle_l(handle);
+
+ // Prevent calls to process() and other functions on effect interface from now on.
+ // The effect engine will be released by the destructor when the last strong reference on
+ // this object is released which can happen after next process is called.
+ if (status == 0 && !mPinned) {
+ mEffectInterface->close();
+ }
+
+ return status;
+}
+
bool AudioFlinger::EffectModule::updateState() {
Mutex::Autolock _l(mLock);
@@ -542,8 +815,7 @@
mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
// If an insert effect is idle and input buffer is different from output buffer,
// accumulate input onto output
- sp<EffectChain> chain = mChain.promote();
- if (chain.get() != nullptr && chain->activeTrackCnt() != 0) {
+ if (mCallback->activeTrackCnt() != 0) {
// similar handling with data_bypass above.
if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
accumulateInputToOutput();
@@ -566,7 +838,6 @@
{
ALOGVV("configure() started");
status_t status;
- sp<ThreadBase> thread;
uint32_t size;
audio_channel_mask_t channelMask;
@@ -575,17 +846,11 @@
goto exit;
}
- thread = mThread.promote();
- if (thread == 0) {
- status = DEAD_OBJECT;
- goto exit;
- }
-
// TODO: handle configuration of effects replacing track process
// TODO: handle configuration of input (record) SW effects above the HAL,
// similar to output EFFECT_FLAG_TYPE_INSERT/REPLACE,
// in which case input channel masks should be used here.
- channelMask = thread->channelMask();
+ channelMask = mCallback->channelMask();
mConfig.inputCfg.channels = channelMask;
mConfig.outputCfg.channels = channelMask;
@@ -622,11 +887,11 @@
mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
// Don't use sample rate for thread if effect isn't offloadable.
- if (isOffloadedOrDirect() && !isOffloaded()) {
+ if (mCallback->isOffloadOrDirect() && !isOffloaded()) {
mConfig.inputCfg.samplingRate = DEFAULT_OUTPUT_SAMPLE_RATE;
ALOGV("Overriding effect input as 48kHz");
} else {
- mConfig.inputCfg.samplingRate = thread->sampleRate();
+ mConfig.inputCfg.samplingRate = mCallback->sampleRate();
}
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
@@ -637,7 +902,7 @@
mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
// Insert effect:
- // - in session AUDIO_SESSION_OUTPUT_MIX or AUDIO_SESSION_OUTPUT_STAGE,
+ // - in global sessions (e.g AUDIO_SESSION_OUTPUT_MIX),
// always overwrites output buffer: input buffer == output buffer
// - in other sessions:
// last effect in the chain accumulates in output buffer: input buffer != output buffer
@@ -652,11 +917,13 @@
}
mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
- mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ mConfig.inputCfg.buffer.frameCount = mCallback->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
- ALOGV("configure() %p thread %p buffer %p framecount %zu",
- this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
+ ALOGV("configure() %p chain %p buffer %p framecount %zu",
+ this, mCallback->chain().promote() != nullptr ? mCallback->chain().promote().get() :
+ nullptr,
+ mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
status_t cmdStatus;
size = sizeof(int);
@@ -671,7 +938,7 @@
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
- thread->isOutput() &&
+ mCallback->isOutput() &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
@@ -738,11 +1005,7 @@
size = sizeof(int);
*(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
- uint32_t latency = 0;
- PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
- if (pbt != NULL) {
- latency = pbt->latency_l();
- }
+ uint32_t latency = mCallback->latency();
*((int32_t *)p->data + 1)= latency;
mEffectInterface->command(EFFECT_CMD_SET_PARAM,
@@ -789,31 +1052,20 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- sp<StreamHalInterface> stream = thread->stream();
- if (stream != 0) {
- status_t result = stream->addEffect(mEffectInterface);
- ALOGE_IF(result != OK, "Error when adding effect: %d", result);
- }
- }
+ (void)mCallback->addEffectToHal(mEffectInterface);
}
}
// start() must be called with PlaybackThread::mLock or EffectChain::mLock held
status_t AudioFlinger::EffectModule::start()
{
- sp<EffectChain> chain;
status_t status;
{
Mutex::Autolock _l(mLock);
status = start_l();
- if (status == NO_ERROR) {
- chain = mChain.promote();
- }
}
- if (chain != 0) {
- chain->resetVolume_l();
+ if (status == NO_ERROR) {
+ mCallback->resetVolume();
}
return status;
}
@@ -860,11 +1112,10 @@
uint32_t size = sizeof(status_t);
if (isVolumeControl() && isOffloadedOrDirect()) {
- sp<EffectChain>chain = mChain.promote();
// We have the EffectChain and EffectModule lock, permit a reentrant call to setVolume:
// resetVolume_l --> setVolume_l --> EffectModule::setVolume
mSetVolumeReentrantTid = gettid();
- chain->resetVolume_l();
+ mCallback->resetVolume();
mSetVolumeReentrantTid = INVALID_PID;
}
@@ -877,7 +1128,7 @@
status = cmdStatus;
}
if (status == NO_ERROR) {
- status = remove_effect_from_hal_l();
+ status = removeEffectFromHal_l();
}
return status;
}
@@ -886,25 +1137,18 @@
void AudioFlinger::EffectModule::release_l()
{
if (mEffectInterface != 0) {
- remove_effect_from_hal_l();
+ removeEffectFromHal_l();
// release effect engine
mEffectInterface->close();
mEffectInterface.clear();
}
}
-status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
+status_t AudioFlinger::EffectModule::removeEffectFromHal_l()
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- sp<StreamHalInterface> stream = thread->stream();
- if (stream != 0) {
- status_t result = stream->removeEffect(mEffectInterface);
- ALOGE_IF(result != OK, "Error when removing effect: %d", result);
- }
- }
+ mCallback->removeEffectFromHal(mEffectInterface);
}
return NO_ERROR;
}
@@ -994,70 +1238,6 @@
return status;
}
-status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
-{
- Mutex::Autolock _l(mLock);
- return setEnabled_l(enabled);
-}
-
-// must be called with EffectModule::mLock held
-status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
-{
-
- ALOGV("setEnabled %p enabled %d", this, enabled);
-
- if (enabled != isEnabled()) {
- switch (mState) {
- // going from disabled to enabled
- case IDLE:
- mState = STARTING;
- break;
- case STOPPED:
- mState = RESTART;
- break;
- case STOPPING:
- mState = ACTIVE;
- break;
-
- // going from enabled to disabled
- case RESTART:
- mState = STOPPED;
- break;
- case STARTING:
- mState = IDLE;
- break;
- case ACTIVE:
- mState = STOPPING;
- break;
- case DESTROYED:
- return NO_ERROR; // simply ignore as we are being destroyed
- }
- for (size_t i = 1; i < mHandles.size(); i++) {
- EffectHandle *h = mHandles[i];
- if (h != NULL && !h->disconnected()) {
- h->setEnabled(enabled);
- }
- }
- }
- return NO_ERROR;
-}
-
-bool AudioFlinger::EffectModule::isEnabled() const
-{
- switch (mState) {
- case RESTART:
- case STARTING:
- case ACTIVE:
- return true;
- case IDLE:
- case STOPPING:
- case STOPPED:
- case DESTROYED:
- default:
- return false;
- }
-}
-
bool AudioFlinger::EffectModule::isProcessEnabled() const
{
if (mStatus != NO_ERROR) {
@@ -1080,7 +1260,7 @@
bool AudioFlinger::EffectModule::isOffloadedOrDirect() const
{
- return (mThreadType == ThreadBase::OFFLOAD || mThreadType == ThreadBase::DIRECT);
+ return mCallback->isOffloadOrDirect();
}
bool AudioFlinger::EffectModule::isVolumeControlEnabled() const
@@ -1124,9 +1304,7 @@
|| size > mInConversionBuffer->getSize())) {
mInConversionBuffer.clear();
ALOGV("%s: allocating mInConversionBuffer %zu", __func__, size);
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
- (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mInConversionBuffer);
+ (void)mCallback->allocateHalBuffer(size, &mInConversionBuffer);
}
if (mInConversionBuffer.get() != nullptr) {
mInConversionBuffer->setFrameCount(inFrameCount);
@@ -1170,9 +1348,7 @@
|| size > mOutConversionBuffer->getSize())) {
mOutConversionBuffer.clear();
ALOGV("%s: allocating mOutConversionBuffer %zu", __func__, size);
- sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
- LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
- (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mOutConversionBuffer);
+ (void)mCallback->allocateHalBuffer(size, &mOutConversionBuffer);
}
if (mOutConversionBuffer.get() != nullptr) {
mOutConversionBuffer->setFrameCount(outFrameCount);
@@ -1220,14 +1396,10 @@
void AudioFlinger::EffectChain::setVolumeForOutput_l(uint32_t left, uint32_t right)
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0 &&
- (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT) &&
- !isNonOffloadableEnabled_l()) {
- PlaybackThread *t = (PlaybackThread *)thread.get();
+ if (mEffectCallback->isOffloadOrDirect() && !isNonOffloadableEnabled_l()) {
float vol_l = (float)left / (1 << 24);
float vol_r = (float)right / (1 << 24);
- t->setVolumeForOutput_l(vol_l, vol_r);
+ mEffectCallback->setVolumeForOutput(vol_l, vol_r);
}
}
@@ -1307,30 +1479,6 @@
return status;
}
-void AudioFlinger::EffectModule::setSuspended(bool suspended)
-{
- Mutex::Autolock _l(mLock);
- mSuspended = suspended;
-}
-
-bool AudioFlinger::EffectModule::suspended() const
-{
- Mutex::Autolock _l(mLock);
- return mSuspended;
-}
-
-bool AudioFlinger::EffectModule::purgeHandles()
-{
- bool enabled = false;
- Mutex::Autolock _l(mLock);
- EffectHandle *handle = controlHandle_l();
- if (handle != NULL) {
- enabled = handle->enabled();
- }
- mHandles.clear();
- return enabled;
-}
-
status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io)
{
Mutex::Autolock _l(mLock);
@@ -1370,111 +1518,6 @@
return mOffloaded;
}
-String8 effectFlagsToString(uint32_t flags) {
- String8 s;
-
- s.append("conn. mode: ");
- switch (flags & EFFECT_FLAG_TYPE_MASK) {
- case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break;
- case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break;
- case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break;
- case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break;
- case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- s.append("insert pref: ");
- switch (flags & EFFECT_FLAG_INSERT_MASK) {
- case EFFECT_FLAG_INSERT_ANY: s.append("any"); break;
- case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break;
- case EFFECT_FLAG_INSERT_LAST: s.append("last"); break;
- case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- s.append("volume mgmt: ");
- switch (flags & EFFECT_FLAG_VOLUME_MASK) {
- case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break;
- case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break;
- case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break;
- case EFFECT_FLAG_VOLUME_MONITOR: s.append("monitors volume"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
-
- uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK;
- if (devind) {
- s.append("device indication: ");
- switch (devind) {
- case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- s.append("input mode: ");
- switch (flags & EFFECT_FLAG_INPUT_MASK) {
- case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break;
- case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break;
- case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break;
- default: s.append("not set"); break;
- }
- s.append(", ");
-
- s.append("output mode: ");
- switch (flags & EFFECT_FLAG_OUTPUT_MASK) {
- case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break;
- case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break;
- case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break;
- default: s.append("not set"); break;
- }
- s.append(", ");
-
- uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK;
- if (accel) {
- s.append("hardware acceleration: ");
- switch (accel) {
- case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break;
- case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK;
- if (modeind) {
- s.append("mode indication: ");
- switch (modeind) {
- case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK;
- if (srcind) {
- s.append("source indication: ");
- switch (srcind) {
- case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break;
- default: s.append("unknown/reserved"); break;
- }
- s.append(", ");
- }
-
- if (flags & EFFECT_FLAG_OFFLOAD_MASK) {
- s.append("offloadable, ");
- }
-
- int len = s.length();
- if (s.length() > 2) {
- (void) s.lockBuffer(len);
- s.unlockBuffer(len - 2);
- }
- return s;
-}
-
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -1490,38 +1533,16 @@
return ss.str();
}
-void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args __unused)
+void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
+ EffectBase::dump(fd, args);
+
String8 result;
-
- result.appendFormat("\tEffect ID %d:\n", mId);
-
bool locked = AudioFlinger::dumpTryLock(mLock);
- // failed to lock - AudioFlinger is probably deadlocked
- if (!locked) {
- result.append("\t\tCould not lock Fx mutex:\n");
- }
- result.append("\t\tSession Status State Registered Enabled Suspended Engine:\n");
- result.appendFormat("\t\t%05d %03d %03d %s %s %s %p\n",
- mSessionId, mStatus, mState, mPolicyRegistered ? "y" : "n", mPolicyEnabled ? "y" : "n",
- mSuspended ? "y" : "n", mEffectInterface.get());
-
- result.append("\t\tDescriptor:\n");
- char uuidStr[64];
- AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
- result.appendFormat("\t\t- UUID: %s\n", uuidStr);
- AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
- result.appendFormat("\t\t- TYPE: %s\n", uuidStr);
- result.appendFormat("\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
- mDescriptor.apiVersion,
- mDescriptor.flags,
- effectFlagsToString(mDescriptor.flags).string());
- result.appendFormat("\t\t- name: %s\n",
- mDescriptor.name);
-
- result.appendFormat("\t\t- implementor: %s\n",
- mDescriptor.implementor);
+ result.append("\t\tStatus Engine:\n");
+ result.appendFormat("\t\t%03d %p\n",
+ mStatus, mEffectInterface.get());
result.appendFormat("\t\t- data: %s\n", mSupportsFloat ? "float" : "int16");
@@ -1555,17 +1576,6 @@
dumpInOutBuffer(false /* isInput */, mOutConversionBuffer).c_str());
#endif
- result.appendFormat("\t\t%zu Clients:\n", mHandles.size());
- result.append("\t\t\t Pid Priority Ctrl Locked client server\n");
- char buffer[256];
- for (size_t i = 0; i < mHandles.size(); ++i) {
- EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->disconnected()) {
- handle->dumpToBuffer(buffer, sizeof(buffer));
- result.append(buffer);
- }
- }
-
write(fd, result.string(), result.length());
if (mEffectInterface != 0) {
@@ -1585,7 +1595,7 @@
#undef LOG_TAG
#define LOG_TAG "AudioFlinger::EffectHandle"
-AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
+AudioFlinger::EffectHandle::EffectHandle(const sp<EffectBase>& effect,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority)
@@ -1593,7 +1603,7 @@
mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false)
{
- ALOGV("constructor %p", this);
+ ALOGV("constructor %p client %p", this, client.get());
if (client == 0) {
return;
@@ -1626,7 +1636,7 @@
{
AutoMutex _l(mLock);
ALOGV("enable %p", this);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1646,38 +1656,16 @@
return status;
}
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId());
- }
+ effect->checkSuspendOnEffectEnabled(true, false /*threadLocked*/);
// checkSuspendOnEffectEnabled() can suspend this same effect when enabled
if (effect->suspended()) {
return NO_ERROR;
}
- status = effect->setEnabled(true);
+ status = effect->setEnabled(true, true /*fromHandle*/);
if (status != NO_ERROR) {
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
- }
mEnabled = false;
- } else {
- if (thread != 0) {
- if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
- Mutex::Autolock _l(thread->mLock);
- thread->broadcast_l();
- }
- if (!effect->isOffloadable()) {
- if (thread->type() == ThreadBase::OFFLOAD) {
- PlaybackThread *t = (PlaybackThread *)thread.get();
- t->invalidateTracks(AUDIO_STREAM_MUSIC);
- }
- if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
- thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
- }
- }
- }
}
return status;
}
@@ -1686,7 +1674,7 @@
{
ALOGV("disable %p", this);
AutoMutex _l(mLock);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1705,17 +1693,7 @@
return NO_ERROR;
}
- status_t status = effect->setEnabled(false);
-
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
- if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
- Mutex::Autolock _l(thread->mLock);
- thread->broadcast_l();
- }
- }
-
+ status_t status = effect->setEnabled(false, true /*fromHandle*/);
return status;
}
@@ -1737,12 +1715,9 @@
}
mDisconnected = true;
{
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect != 0) {
- sp<ThreadBase> thread = effect->thread().promote();
- if (thread != 0) {
- thread->disconnectEffectHandle(this, unpinIfLast);
- } else if (effect->disconnectHandle(this, unpinIfLast) > 0) {
+ if (effect->disconnectHandle(this, unpinIfLast) > 0) {
ALOGW("%s Effect handle %p disconnected after thread destruction",
__func__, this);
}
@@ -1808,7 +1783,7 @@
}
AutoMutex _l(mLock);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1816,12 +1791,13 @@
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
return INVALID_OPERATION;
}
- if (mClient == 0) {
- return INVALID_OPERATION;
- }
// handle commands that are not forwarded transparently to effect engine
if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
+ if (mClient == 0) {
+ return INVALID_OPERATION;
+ }
+
if (*replySize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
return BAD_VALUE;
@@ -1963,12 +1939,13 @@
AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
audio_session_t sessionId)
- : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
+ : mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX),
+ mEffectCallback(new EffectCallback(this, thread, thread->mAudioFlinger.get()))
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
- if (thread == NULL) {
+ if (thread == nullptr) {
return;
}
mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
@@ -2034,43 +2011,30 @@
void AudioFlinger::EffectChain::clearInputBuffer()
{
Mutex::Autolock _l(mLock);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("clearInputBuffer(): cannot promote mixer thread");
- return;
- }
- clearInputBuffer_l(thread);
+ clearInputBuffer_l();
}
// Must be called with EffectChain::mLock locked
-void AudioFlinger::EffectChain::clearInputBuffer_l(const sp<ThreadBase>& thread)
+void AudioFlinger::EffectChain::clearInputBuffer_l()
{
if (mInBuffer == NULL) {
return;
}
const size_t frameSize =
- audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * thread->channelCount();
+ audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * mEffectCallback->channelCount();
- memset(mInBuffer->audioBuffer()->raw, 0, thread->frameCount() * frameSize);
+ memset(mInBuffer->audioBuffer()->raw, 0, mEffectCallback->frameCount() * frameSize);
mInBuffer->commit();
}
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::process_l()
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("process_l(): cannot promote mixer thread");
- return;
- }
- bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
- (mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
// never process effects when:
// - on an OFFLOAD thread
// - no more tracks are on the session and the effect tail has been rendered
- bool doProcess = (thread->type() != ThreadBase::OFFLOAD)
- && (thread->type() != ThreadBase::MMAP);
- if (!isGlobalSession) {
+ bool doProcess = !mEffectCallback->isOffloadOrMmap();
+ if (!audio_is_global_session(mSessionId)) {
bool tracksOnSession = (trackCnt() != 0);
if (!tracksOnSession && mTailBufferCount == 0) {
@@ -2081,7 +2045,7 @@
// if no track is active and the effect tail has not been rendered,
// the input buffer must be cleared here as the mixer process will not do it
if (tracksOnSession || mTailBufferCount > 0) {
- clearInputBuffer_l(thread);
+ clearInputBuffer_l();
if (mTailBufferCount > 0) {
mTailBufferCount--;
}
@@ -2117,14 +2081,13 @@
// createEffect_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
- ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
{
Mutex::Autolock _l(mLock);
- effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
+ effect = new EffectModule(mEffectCallback, desc, id, sessionId, pinned, AUDIO_PORT_HANDLE_NONE);
status_t lStatus = effect->status();
if (lStatus == NO_ERROR) {
lStatus = addEffect_ll(effect);
@@ -2147,12 +2110,7 @@
effect_descriptor_t desc = effect->desc();
uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
- effect->setChain(this);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- return NO_INIT;
- }
- effect->setThread(thread);
+ effect->setCallback(mEffectCallback);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
// Auxiliary effects are inserted at the beginning of mEffects vector as
@@ -2163,13 +2121,13 @@
// 32 bit format. This is to avoid saturation in AudoMixer
// accumulation stage. Saturation is done in EffectModule::process() before
// calling the process in effect engine
- size_t numSamples = thread->frameCount();
+ size_t numSamples = mEffectCallback->frameCount();
sp<EffectBufferHalInterface> halBuffer;
#ifdef FLOAT_EFFECT_CHAIN
- status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
+ status_t result = mEffectCallback->allocateHalBuffer(
numSamples * sizeof(float), &halBuffer);
#else
- status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
+ status_t result = mEffectCallback->allocateHalBuffer(
numSamples * sizeof(int32_t), &halBuffer);
#endif
if (result != OK) return result;
@@ -2487,7 +2445,7 @@
if (effect != 0) {
desc->mEffect = effect;
effect->setSuspended(true);
- effect->setEnabled(false);
+ effect->setEnabled(false, false /*fromHandle*/);
}
}
} else {
@@ -2645,7 +2603,7 @@
// if effect is requested to suspended but was not yet enabled, suspend it now.
if (desc->mEffect == 0) {
desc->mEffect = effect;
- effect->setEnabled(false);
+ effect->setEnabled(false, false /*fromHandle*/);
effect->setSuspended(true);
}
} else {
@@ -2680,10 +2638,7 @@
void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
{
Mutex::Autolock _l(mLock);
- mThread = thread;
- for (size_t i = 0; i < mEffects.size(); i++) {
- mEffects[i]->setThread(thread);
- }
+ mEffectCallback->setThread(thread.get());
}
void AudioFlinger::EffectChain::checkOutputFlagCompatibility(audio_output_flags_t *flags) const
@@ -2743,4 +2698,549 @@
return true;
}
+// EffectCallbackInterface implementation
+status_t AudioFlinger::EffectChain::EffectCallback::createEffectHal(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) {
+ status_t status = NO_INIT;
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af == nullptr) {
+ return status;
+ }
+ sp<EffectsFactoryHalInterface> effectsFactory = af->getEffectsFactory();
+ if (effectsFactory != 0) {
+ status = effectsFactory->createEffect(pEffectUuid, sessionId, io(), deviceId, effect);
+ }
+ return status;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::updateOrphanEffectChains(
+ const sp<AudioFlinger::EffectBase>& effect) {
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af == nullptr) {
+ return false;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ return af->updateOrphanEffectChains(effect->asEffectModule());
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::allocateHalBuffer(
+ size_t size, sp<EffectBufferHalInterface>* buffer) {
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ LOG_ALWAYS_FATAL_IF(af == nullptr, "allocateHalBuffer() could not retrieved audio flinger");
+ return af->mEffectsFactoryHal->allocateBuffer(size, buffer);
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ status_t result = NO_INIT;
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return result;
+ }
+ sp <StreamHalInterface> st = t->stream();
+ if (st == nullptr) {
+ return result;
+ }
+ result = st->addEffect(effect);
+ ALOGE_IF(result != OK, "Error when adding effect: %d", result);
+ return result;
+}
+
+status_t AudioFlinger::EffectChain::EffectCallback::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ status_t result = NO_INIT;
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return result;
+ }
+ sp <StreamHalInterface> st = t->stream();
+ if (st == nullptr) {
+ return result;
+ }
+ result = st->removeEffect(effect);
+ ALOGE_IF(result != OK, "Error when removing effect: %d", result);
+ return result;
+}
+
+audio_io_handle_t AudioFlinger::EffectChain::EffectCallback::io() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_IO_HANDLE_NONE;
+ }
+ return t->id();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOutput() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return true;
+ }
+ return t->isOutput();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffload() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrDirect() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::DIRECT;
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrMmap() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::MMAP;
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->sampleRate();
+}
+
+audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::channelMask() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ return t->channelMask();
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::channelCount() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->channelCount();
+}
+
+size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->frameCount();
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::latency() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return 0;
+ }
+ return t->latency_l();
+}
+
+void AudioFlinger::EffectChain::EffectCallback::setVolumeForOutput(float left, float right) const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->setVolumeForOutput_l(left, right);
+}
+
+void AudioFlinger::EffectChain::EffectCallback::checkSuspendOnEffectEnabled(
+ const sp<EffectBase>& effect, bool enabled, bool threadLocked) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->checkSuspendOnEffectEnabled(enabled, effect->sessionId(), threadLocked);
+
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ c->checkSuspendOnEffectEnabled(effect->asEffectModule(), enabled);
+}
+
+void AudioFlinger::EffectChain::EffectCallback::onEffectEnable(const sp<EffectBase>& effect) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ // in EffectChain context, an EffectBase is always from an EffectModule so static cast is safe
+ t->onEffectEnable(effect->asEffectModule());
+}
+
+void AudioFlinger::EffectChain::EffectCallback::onEffectDisable(const sp<EffectBase>& effect) {
+ checkSuspendOnEffectEnabled(effect, false, false /*threadLocked*/);
+
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return;
+ }
+ t->onEffectDisable();
+}
+
+bool AudioFlinger::EffectChain::EffectCallback::disconnectEffectHandle(EffectHandle *handle,
+ bool unpinIfLast) {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return false;
+ }
+ t->disconnectEffectHandle(handle, unpinIfLast);
+ return true;
+}
+
+void AudioFlinger::EffectChain::EffectCallback::resetVolume() {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return;
+ }
+ c->resetVolume_l();
+
+}
+
+uint32_t AudioFlinger::EffectChain::EffectCallback::strategy() const {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return PRODUCT_STRATEGY_NONE;
+ }
+ return c->strategy();
+}
+
+int32_t AudioFlinger::EffectChain::EffectCallback::activeTrackCnt() const {
+ sp<EffectChain> c = mChain.promote();
+ if (c == nullptr) {
+ return 0;
+ }
+ return c->activeTrackCnt();
+}
+
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::DeviceEffectProxy"
+
+status_t AudioFlinger::DeviceEffectProxy::setEnabled(bool enabled, bool fromHandle)
+{
+ status_t status = EffectBase::setEnabled(enabled, fromHandle);
+ Mutex::Autolock _l(mProxyLock);
+ if (status == NO_ERROR) {
+ for (auto& handle : mEffectHandles) {
+ if (enabled) {
+ status = handle.second->enable();
+ } else {
+ status = handle.second->disable();
+ }
+ }
+ }
+ ALOGV("%s enable %d status %d", __func__, enabled, status);
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::init(
+ const std::map <audio_patch_handle_t, PatchPanel::Patch>& patches) {
+//For all audio patches
+//If src or sink device match
+//If the effect is HW accelerated
+// if no corresponding effect module
+// Create EffectModule: mHalEffect
+//Create and attach EffectHandle
+//If the effect is not HW accelerated and the patch sink or src is a mixer port
+// Create Effect on patch input or output thread on session -1
+//Add EffectHandle to EffectHandle map of Effect Proxy:
+ ALOGV("%s device type %d address %s", __func__, mDevice.mType, mDevice.getAddress());
+ status_t status = NO_ERROR;
+ for (auto &patch : patches) {
+ status = onCreatePatch(patch.first, patch.second);
+ ALOGV("%s onCreatePatch status %d", __func__, status);
+ if (status == BAD_VALUE) {
+ return status;
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::onCreatePatch(
+ audio_patch_handle_t patchHandle, const AudioFlinger::PatchPanel::Patch& patch) {
+ status_t status = NAME_NOT_FOUND;
+ sp<EffectHandle> handle;
+ // only consider source[0] as this is the only "true" source of a patch
+ status = checkPort(patch, &patch.mAudioPatch.sources[0], &handle);
+ ALOGV("%s source checkPort status %d", __func__, status);
+ for (uint32_t i = 0; i < patch.mAudioPatch.num_sinks && status == NAME_NOT_FOUND; i++) {
+ status = checkPort(patch, &patch.mAudioPatch.sinks[i], &handle);
+ ALOGV("%s sink %d checkPort status %d", __func__, i, status);
+ }
+ if (status == NO_ERROR || status == ALREADY_EXISTS) {
+ Mutex::Autolock _l(mProxyLock);
+ mEffectHandles.emplace(patchHandle, handle);
+ }
+ ALOGW_IF(status == BAD_VALUE,
+ "%s cannot attach effect %s on patch %d", __func__, mDescriptor.name, patchHandle);
+
+ return status;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::checkPort(const PatchPanel::Patch& patch,
+ const struct audio_port_config *port, sp <EffectHandle> *handle) {
+
+ ALOGV("%s type %d device type %d address %s device ID %d patch.isSoftware() %d",
+ __func__, port->type, port->ext.device.type,
+ port->ext.device.address, port->id, patch.isSoftware());
+ if (port->type != AUDIO_PORT_TYPE_DEVICE || port->ext.device.type != mDevice.mType
+ || port->ext.device.address != mDevice.mAddress) {
+ return NAME_NOT_FOUND;
+ }
+ status_t status = NAME_NOT_FOUND;
+
+ if (mDescriptor.flags & EFFECT_FLAG_HW_ACC_TUNNEL) {
+ Mutex::Autolock _l(mProxyLock);
+ mDevicePort = *port;
+ mHalEffect = new EffectModule(mMyCallback,
+ const_cast<effect_descriptor_t *>(&mDescriptor),
+ mMyCallback->newEffectId(), AUDIO_SESSION_DEVICE,
+ false /* pinned */, port->id);
+ if (audio_is_input_device(mDevice.mType)) {
+ mHalEffect->setInputDevice(mDevice);
+ } else {
+ mHalEffect->setDevices({mDevice});
+ }
+ *handle = new EffectHandle(mHalEffect, nullptr, nullptr, 0 /*priority*/);
+ status = (*handle)->initCheck();
+ if (status == OK) {
+ status = mHalEffect->addHandle((*handle).get());
+ } else {
+ mHalEffect.clear();
+ mDevicePort.id = AUDIO_PORT_HANDLE_NONE;
+ }
+ } else if (patch.isSoftware() || patch.thread().promote() != nullptr) {
+ sp <ThreadBase> thread;
+ if (audio_port_config_has_input_direction(port)) {
+ if (patch.isSoftware()) {
+ thread = patch.mRecord.thread();
+ } else {
+ thread = patch.thread().promote();
+ }
+ } else {
+ if (patch.isSoftware()) {
+ thread = patch.mPlayback.thread();
+ } else {
+ thread = patch.thread().promote();
+ }
+ }
+ int enabled;
+ *handle = thread->createEffect_l(nullptr, nullptr, 0, AUDIO_SESSION_DEVICE,
+ const_cast<effect_descriptor_t *>(&mDescriptor),
+ &enabled, &status, false);
+ ALOGV("%s thread->createEffect_l status %d", __func__, status);
+ } else {
+ status = BAD_VALUE;
+ }
+ if (status == NO_ERROR || status == ALREADY_EXISTS) {
+ if (isEnabled()) {
+ (*handle)->enable();
+ } else {
+ (*handle)->disable();
+ }
+ }
+ return status;
+}
+
+void AudioFlinger::DeviceEffectProxy::onReleasePatch(audio_patch_handle_t patchHandle) {
+ Mutex::Autolock _l(mProxyLock);
+ mEffectHandles.erase(patchHandle);
+}
+
+
+size_t AudioFlinger::DeviceEffectProxy::removeEffect(const sp<EffectModule>& effect)
+{
+ Mutex::Autolock _l(mProxyLock);
+ if (effect == mHalEffect) {
+ mHalEffect.clear();
+ mDevicePort.id = AUDIO_PORT_HANDLE_NONE;
+ }
+ return mHalEffect == nullptr ? 0 : 1;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ if (mHalEffect == nullptr) {
+ return NO_INIT;
+ }
+ return mManagerCallback->addEffectToHal(
+ mDevicePort.id, mDevicePort.ext.device.hw_module, effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ if (mHalEffect == nullptr) {
+ return NO_INIT;
+ }
+ return mManagerCallback->removeEffectFromHal(
+ mDevicePort.id, mDevicePort.ext.device.hw_module, effect);
+}
+
+bool AudioFlinger::DeviceEffectProxy::isOutput() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE) {
+ return mDevicePort.role == AUDIO_PORT_ROLE_SINK;
+ }
+ return true;
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::sampleRate() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE &&
+ (mDevicePort.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) != 0) {
+ return mDevicePort.sample_rate;
+ }
+ return DEFAULT_OUTPUT_SAMPLE_RATE;
+}
+
+audio_channel_mask_t AudioFlinger::DeviceEffectProxy::channelMask() const {
+ if (mDevicePort.id != AUDIO_PORT_HANDLE_NONE &&
+ (mDevicePort.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) != 0) {
+ return mDevicePort.channel_mask;
+ }
+ return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::channelCount() const {
+ if (isOutput()) {
+ return audio_channel_count_from_out_mask(channelMask());
+ }
+ return audio_channel_count_from_in_mask(channelMask());
+}
+
+void AudioFlinger::DeviceEffectProxy::dump(int fd, int spaces) {
+ const Vector<String16> args;
+ EffectBase::dump(fd, args);
+
+ const bool locked = dumpTryLock(mProxyLock);
+ if (!locked) {
+ String8 result("DeviceEffectProxy may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ String8 outStr;
+ if (mHalEffect != nullptr) {
+ outStr.appendFormat("%*sHAL Effect Id: %d\n", spaces, "", mHalEffect->id());
+ } else {
+ outStr.appendFormat("%*sNO HAL Effect\n", spaces, "");
+ }
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+
+ outStr.appendFormat("%*sSub Effects:\n", spaces, "");
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+
+ for (const auto& iter : mEffectHandles) {
+ outStr.appendFormat("%*sEffect for patch handle %d:\n", spaces + 2, "", iter.first);
+ write(fd, outStr.string(), outStr.size());
+ outStr.clear();
+ sp<EffectBase> effect = iter.second->effect().promote();
+ if (effect != nullptr) {
+ effect->dump(fd, args);
+ }
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::DeviceEffectProxy::ProxyCallback"
+
+int AudioFlinger::DeviceEffectProxy::ProxyCallback::newEffectId() {
+ return mManagerCallback->newEffectId();
+}
+
+
+bool AudioFlinger::DeviceEffectProxy::ProxyCallback::disconnectEffectHandle(
+ EffectHandle *handle, bool unpinIfLast) {
+ sp<EffectBase> effectBase = handle->effect().promote();
+ if (effectBase == nullptr) {
+ return false;
+ }
+
+ sp<EffectModule> effect = effectBase->asEffectModule();
+ 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) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy != nullptr) {
+ proxy->removeEffect(effect);
+ }
+ if (handle->enabled()) {
+ effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
+ }
+ }
+ return true;
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::createEffectHal(
+ const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
+ sp<EffectHalInterface> *effect) {
+ return mManagerCallback->createEffectHal(pEffectUuid, sessionId, deviceId, effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal(
+ sp<EffectHalInterface> effect) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return NO_INIT;
+ }
+ return proxy->addEffectToHal(effect);
+}
+
+status_t AudioFlinger::DeviceEffectProxy::ProxyCallback::removeEffectFromHal(
+ sp<EffectHalInterface> effect) {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return NO_INIT;
+ }
+ return proxy->addEffectToHal(effect);
+}
+
+bool AudioFlinger::DeviceEffectProxy::ProxyCallback::isOutput() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return true;
+ }
+ return proxy->isOutput();
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::sampleRate() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return DEFAULT_OUTPUT_SAMPLE_RATE;
+ }
+ return proxy->sampleRate();
+}
+
+audio_channel_mask_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelMask() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return AUDIO_CHANNEL_OUT_STEREO;
+ }
+ return proxy->channelMask();
+}
+
+uint32_t AudioFlinger::DeviceEffectProxy::ProxyCallback::channelCount() const {
+ sp<DeviceEffectProxy> proxy = mProxy.promote();
+ if (proxy == nullptr) {
+ return 2;
+ }
+ return proxy->channelCount();
+}
+
} // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index dbf63c8..40bb226 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -21,34 +21,78 @@
//--- Audio Effect Management
-// EffectModule and EffectChain classes both have their own mutex to protect
+// Interface implemented by the EffectModule parent or owner (e.g an EffectChain) to abstract
+// interactions between the EffectModule and the reset of the audio framework.
+class EffectCallbackInterface : public RefBase {
+public:
+ ~EffectCallbackInterface() override = default;
+
+ // Trivial methods usually implemented with help from ThreadBase
+ virtual audio_io_handle_t io() const = 0;
+ virtual bool isOutput() const = 0;
+ virtual bool isOffload() const = 0;
+ virtual bool isOffloadOrDirect() const = 0;
+ virtual bool isOffloadOrMmap() const = 0;
+ virtual uint32_t sampleRate() const = 0;
+ virtual audio_channel_mask_t channelMask() const = 0;
+ virtual uint32_t channelCount() const = 0;
+ virtual size_t frameCount() const = 0;
+
+ // Non trivial methods usually implemented with help from ThreadBase:
+ // pay attention to mutex locking order
+ virtual uint32_t latency() const { return 0; }
+ virtual status_t addEffectToHal(sp<EffectHalInterface> effect) = 0;
+ virtual status_t removeEffectFromHal(sp<EffectHalInterface> effect) = 0;
+ virtual void setVolumeForOutput(float left, float right) const = 0;
+ virtual bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) = 0;
+ virtual void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
+ bool enabled,
+ bool threadLocked) = 0;
+ virtual void onEffectEnable(const sp<EffectBase>& effect) = 0;
+ virtual void onEffectDisable(const sp<EffectBase>& effect) = 0;
+
+ // Methods usually implemented with help from AudioFlinger: pay attention to mutex locking order
+ virtual status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) = 0;
+ virtual status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) = 0;
+ virtual bool updateOrphanEffectChains(const sp<EffectBase>& effect) = 0;
+
+ // Methods usually implemented with help from EffectChain: pay attention to mutex locking order
+ virtual uint32_t strategy() const = 0;
+ virtual int32_t activeTrackCnt() const = 0;
+ virtual void resetVolume() = 0;
+
+ virtual wp<EffectChain> chain() const = 0;
+};
+
+// EffectBase(EffectModule) and EffectChain classes both have their own mutex to protect
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
-// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
-// AudioHandle -> ThreadBase -> EffectChain -> EffectModule
+// AudioFlinger -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
+// AudioHandle -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
+
+// NOTE: When implementing the EffectCallbackInterface, in an EffectChain or other, it is important
+// to pay attention to this locking order as some callback methods can be called from a state where
+// EffectModule and/or EffectChain mutexes are held.
+
// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
// startOutput(), getInputForAttr(), releaseInput()...) should never be called with AudioFlinger or
// Threadbase mutex locked to avoid cross deadlock with other clients calling AudioPolicyService
// methods that in turn call AudioFlinger thus locking the same mutexes in the reverse order.
-// The EffectModule class is a wrapper object controlling the effect engine implementation
-// in the effect library. It prevents concurrent calls to process() and command() functions
-// from different client threads. It keeps a list of EffectHandle objects corresponding
-// to all client applications using this effect and notifies applications of effect state,
-// control or parameter changes. It manages the activation state machine to send appropriate
-// reset, enable, disable commands to effect engine and provide volume
-// ramping when effects are activated/deactivated.
-// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
-// the attached track(s) to accumulate their auxiliary channel.
-class EffectModule : public RefBase {
+
+// The EffectBase class contains common properties, state and behavior for and EffectModule or
+// other derived classes managing an audio effect instance within the effect framework.
+// It also contains the class mutex (see comment on locking order above).
+class EffectBase : public RefBase {
public:
- EffectModule(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
- effect_descriptor_t *desc,
- int id,
- audio_session_t sessionId,
- bool pinned);
- virtual ~EffectModule();
+ EffectBase(const sp<EffectCallbackInterface>& callback,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned);
+
+ ~EffectBase() override = default;
enum effect_state {
IDLE,
@@ -60,72 +104,14 @@
DESTROYED
};
- int id() const { return mId; }
- void process();
- bool updateState();
- status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData);
-
- void reset_l();
- status_t configure();
- status_t init();
+ int id() const { return mId; }
effect_state state() const {
return mState;
}
- uint32_t status() {
- return mStatus;
- }
audio_session_t sessionId() const {
return mSessionId;
}
- status_t setEnabled(bool enabled);
- status_t setEnabled_l(bool enabled);
- bool isEnabled() const;
- bool isProcessEnabled() const;
- bool isOffloadedOrDirect() const;
- bool isVolumeControlEnabled() const;
-
- void setInBuffer(const sp<EffectBufferHalInterface>& buffer);
- int16_t *inBuffer() const {
- return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL;
- }
- void setOutBuffer(const sp<EffectBufferHalInterface>& buffer);
- int16_t *outBuffer() const {
- return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
- }
- void setChain(const wp<EffectChain>& chain) { mChain = chain; }
- void setThread(const wp<ThreadBase>& thread)
- { mThread = thread; mThreadType = thread.promote()->type(); }
- const wp<ThreadBase>& thread() { return mThread; }
-
- status_t addHandle(EffectHandle *handle);
- ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
- ssize_t removeHandle(EffectHandle *handle);
- ssize_t removeHandle_l(EffectHandle *handle);
-
const effect_descriptor_t& desc() const { return mDescriptor; }
- wp<EffectChain>& chain() { return mChain; }
-
- status_t setDevices(const AudioDeviceTypeAddrVector &devices);
- status_t setInputDevice(const AudioDeviceTypeAddr &device);
- status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
- status_t setMode(audio_mode_t mode);
- status_t setAudioSource(audio_source_t source);
- status_t start();
- status_t stop();
- void setSuspended(bool suspended);
- bool suspended() const;
-
- EffectHandle* controlHandle_l();
-
- bool isPinned() const { return mPinned; }
- void unPin() { mPinned = false; }
- bool purgeHandles();
- void lock() { mLock.lock(); }
- void unlock() { mLock.unlock(); }
bool isOffloadable() const
{ return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
bool isImplementationSoftware() const
@@ -138,18 +124,143 @@
bool isVolumeMonitor() const
{ return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK)
== EFFECT_FLAG_VOLUME_MONITOR; }
- status_t setOffloaded(bool offloaded, audio_io_handle_t io);
- bool isOffloaded() const;
- void addEffectToHal_l();
- void release_l();
+
+ virtual status_t setEnabled(bool enabled, bool fromHandle);
+ status_t setEnabled_l(bool enabled);
+ bool isEnabled() const;
+
+ void setSuspended(bool suspended);
+ bool suspended() const;
+
+ virtual status_t command(uint32_t cmdCode __unused,
+ uint32_t cmdSize __unused,
+ void *pCmdData __unused,
+ uint32_t *replySize __unused,
+ void *pReplyData __unused) { return NO_ERROR; };
+
+ void setCallback(const sp<EffectCallbackInterface>& callback) { mCallback = callback; }
+ sp<EffectCallbackInterface>& callback() { return mCallback; }
+
+ status_t addHandle(EffectHandle *handle);
+ ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
+ ssize_t removeHandle(EffectHandle *handle);
+ virtual ssize_t removeHandle_l(EffectHandle *handle);
+ EffectHandle* controlHandle_l();
+ bool purgeHandles();
+
+ void checkSuspendOnEffectEnabled(bool enabled, bool threadLocked);
+
+ bool isPinned() const { return mPinned; }
+ void unPin() { mPinned = false; }
+
+ void lock() { mLock.lock(); }
+ void unlock() { mLock.unlock(); }
status_t updatePolicyState();
+ virtual sp<EffectModule> asEffectModule() { return nullptr; }
+ virtual sp<DeviceEffectProxy> asDeviceEffectProxy() { return nullptr; }
+
void dump(int fd, const Vector<String16>& args);
private:
friend class AudioFlinger; // for mHandles
- bool mPinned;
+ bool mPinned = false;
+
+ DISALLOW_COPY_AND_ASSIGN(EffectBase);
+
+mutable Mutex mLock; // mutex for process, commands and handles list protection
+ sp<EffectCallbackInterface> mCallback; // parent effect chain
+ const int mId; // this instance unique ID
+ const audio_session_t mSessionId; // audio session ID
+ const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ effect_state mState = IDLE; // current activation state
+ // effect is suspended: temporarily disabled by framework
+ bool mSuspended = false;
+
+ Vector<EffectHandle *> mHandles; // list of client handles
+ // First handle in mHandles has highest priority and controls the effect module
+
+ // Audio policy effect state management
+ // Mutex protecting transactions with audio policy manager as mLock cannot
+ // be held to avoid cross deadlocks with audio policy mutex
+ Mutex mPolicyLock;
+ // Effect is registered in APM or not
+ bool mPolicyRegistered = false;
+ // Effect enabled state communicated to APM. Enabled state corresponds to
+ // state requested by the EffectHandle with control
+ bool mPolicyEnabled = false;
+};
+
+// The EffectModule class is a wrapper object controlling the effect engine implementation
+// in the effect library. It prevents concurrent calls to process() and command() functions
+// from different client threads. It keeps a list of EffectHandle objects corresponding
+// to all client applications using this effect and notifies applications of effect state,
+// control or parameter changes. It manages the activation state machine to send appropriate
+// reset, enable, disable commands to effect engine and provide volume
+// ramping when effects are activated/deactivated.
+// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+// the attached track(s) to accumulate their auxiliary channel.
+class EffectModule : public EffectBase {
+public:
+ EffectModule(const sp<EffectCallbackInterface>& callabck,
+ effect_descriptor_t *desc,
+ int id,
+ audio_session_t sessionId,
+ bool pinned,
+ audio_port_handle_t deviceId);
+ virtual ~EffectModule();
+
+ void process();
+ bool updateState();
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData) override;
+
+ void reset_l();
+ status_t configure();
+ status_t init();
+
+ uint32_t status() {
+ return mStatus;
+ }
+
+ bool isProcessEnabled() const;
+ bool isOffloadedOrDirect() const;
+ bool isVolumeControlEnabled() const;
+
+ void setInBuffer(const sp<EffectBufferHalInterface>& buffer);
+ int16_t *inBuffer() const {
+ return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL;
+ }
+ void setOutBuffer(const sp<EffectBufferHalInterface>& buffer);
+ int16_t *outBuffer() const {
+ return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
+ }
+
+ ssize_t removeHandle_l(EffectHandle *handle) override;
+
+ status_t setDevices(const AudioDeviceTypeAddrVector &devices);
+ status_t setInputDevice(const AudioDeviceTypeAddr &device);
+ status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
+ status_t setMode(audio_mode_t mode);
+ status_t setAudioSource(audio_source_t source);
+ status_t start();
+ status_t stop();
+
+ status_t setOffloaded(bool offloaded, audio_io_handle_t io);
+ bool isOffloaded() const;
+ void addEffectToHal_l();
+ void release_l();
+
+ sp<EffectModule> asEffectModule() override { return this; }
+
+ void dump(int fd, const Vector<String16>& args);
+
+private:
+ friend class AudioFlinger; // for mHandles
// Maximum time allocated to effect engines to complete the turn off sequence
static const uint32_t MAX_DISABLE_TIME_MS = 10000;
@@ -158,30 +269,19 @@
status_t start_l();
status_t stop_l();
- status_t remove_effect_from_hal_l();
+ status_t removeEffectFromHal_l();
status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);
-mutable Mutex mLock; // mutex for process, commands and handles list protection
- wp<ThreadBase> mThread; // parent thread
- ThreadBase::type_t mThreadType; // parent thread type
- wp<EffectChain> mChain; // parent effect chain
- const int mId; // this instance unique ID
- const audio_session_t mSessionId; // audio session ID
- const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
effect_config_t mConfig; // input and output audio configuration
sp<EffectHalInterface> mEffectInterface; // Effect module HAL
sp<EffectBufferHalInterface> mInBuffer; // Buffers for interacting with HAL
sp<EffectBufferHalInterface> mOutBuffer;
status_t mStatus; // initialization status
- effect_state mState; // current activation state
- Vector<EffectHandle *> mHandles; // list of client handles
// First handle in mHandles has highest priority and controls the effect module
uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
- bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
- wp<AudioFlinger> mAudioFlinger;
#ifdef FLOAT_EFFECT_CHAIN
bool mSupportsFloat; // effect supports float processing
@@ -208,16 +308,6 @@
static constexpr pid_t INVALID_PID = (pid_t)-1;
// this tid is allowed to call setVolume() without acquiring the mutex.
pid_t mSetVolumeReentrantTid = INVALID_PID;
-
- // Audio policy effect state management
- // Mutex protecting transactions with audio policy manager as mLock cannot
- // be held to avoid cross deadlocks with audio policy mutex
- Mutex mPolicyLock;
- // Effect is registered in APM or not
- bool mPolicyRegistered = false;
- // Effect enabled state communicated to APM. Enabled state corresponds to
- // state requested by the EffectHandle with control
- bool mPolicyEnabled = false;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -229,7 +319,7 @@
class EffectHandle: public android::BnEffect {
public:
- EffectHandle(const sp<EffectModule>& effect,
+ EffectHandle(const sp<EffectBase>& effect,
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority);
@@ -267,9 +357,9 @@
bool enabled() const { return mEnabled; }
// Getters
- wp<EffectModule> effect() const { return mEffect; }
+ wp<EffectBase> effect() const { return mEffect; }
int id() const {
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0) {
return 0;
}
@@ -286,7 +376,7 @@
DISALLOW_COPY_AND_ASSIGN(EffectHandle);
Mutex mLock; // protects IEffect method calls
- wp<EffectModule> mEffect; // pointer to controlled EffectModule
+ wp<EffectBase> mEffect; // pointer to controlled EffectModule
sp<IEffectClient> mEffectClient; // callback interface for client notifications
/*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
sp<IMemory> mCblkMemory; // shared memory for control block
@@ -333,7 +423,6 @@
}
status_t createEffect_l(sp<EffectModule>& effect,
- ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -389,9 +478,8 @@
bool suspend);
// suspend all eligible effects
void setEffectSuspendedAll_l(bool suspend);
- // check if effects should be suspend or restored when a given effect is enable or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled);
+ // check if effects should be suspended or restored when a given effect is enable or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect, bool enabled);
void clearInputBuffer();
@@ -416,9 +504,60 @@
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+ sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
+ wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
+
void dump(int fd, const Vector<String16>& args);
private:
+
+ class EffectCallback : public EffectCallbackInterface {
+ public:
+ EffectCallback(EffectChain *chain, ThreadBase *thread, AudioFlinger *audioFlinger)
+ : mChain(chain), mThread(thread), mAudioFlinger(audioFlinger) {}
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
+ status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect) override;
+
+ audio_io_handle_t io() const override;
+ bool isOutput() const override;
+ bool isOffload() const override;
+ bool isOffloadOrDirect() const override;
+ bool isOffloadOrMmap() const override;
+
+ uint32_t sampleRate() const override;
+ audio_channel_mask_t channelMask() const override;
+ uint32_t channelCount() const override;
+ size_t frameCount() const override;
+ uint32_t latency() const override;
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect) override;
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left, float right) const override;
+
+ // check if effects should be suspended/restored when a given effect is enable/disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
+ bool enabled, bool threadLocked) override;
+ void resetVolume() override;
+ uint32_t strategy() const override;
+ int32_t activeTrackCnt() const override;
+ void onEffectEnable(const sp<EffectBase>& effect) override;
+ void onEffectDisable(const sp<EffectBase>& effect) override;
+
+ wp<EffectChain> chain() const override { return mChain; }
+
+ wp<ThreadBase> thread() { return mThread; }
+ void setThread(ThreadBase *thread) { mThread = thread; };
+
+ private:
+ wp<EffectChain> mChain;
+ wp<ThreadBase> mThread;
+ wp<AudioFlinger> mAudioFlinger;
+ };
+
friend class AudioFlinger; // for mThread, mEffects
DISALLOW_COPY_AND_ASSIGN(EffectChain);
@@ -444,13 +583,12 @@
static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
- void clearInputBuffer_l(const sp<ThreadBase>& thread);
+ void clearInputBuffer_l();
void setThread(const sp<ThreadBase>& thread);
void setVolumeForOutput_l(uint32_t left, uint32_t right);
- wp<ThreadBase> mThread; // parent mixer thread
mutable Mutex mLock; // mutex protecting effect list
Vector< sp<EffectModule> > mEffects; // list of effect modules
audio_session_t mSessionId; // audio session ID
@@ -474,4 +612,99 @@
// timeLow fields among effect type UUIDs.
// Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+
+ const sp<EffectCallback> mEffectCallback;
+};
+
+class DeviceEffectProxy : public EffectBase {
+public:
+ DeviceEffectProxy (const AudioDeviceTypeAddr& device,
+ const sp<DeviceEffectManagerCallback>& callback,
+ effect_descriptor_t *desc, int id)
+ : EffectBase(callback, desc, id, AUDIO_SESSION_DEVICE, false),
+ mDevice(device), mManagerCallback(callback),
+ mMyCallback(new ProxyCallback(this, callback)) {}
+
+ status_t setEnabled(bool enabled, bool fromHandle) override;
+ sp<DeviceEffectProxy> asDeviceEffectProxy() override { return this; }
+
+ status_t init(const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches);
+ status_t onCreatePatch(audio_patch_handle_t patchHandle, const PatchPanel::Patch& patch);
+ void onReleasePatch(audio_patch_handle_t patchHandle);
+
+ size_t removeEffect(const sp<EffectModule>& effect);
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect);
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect);
+
+ const AudioDeviceTypeAddr& device() { return mDevice; };
+ bool isOutput() const;
+ uint32_t sampleRate() const;
+ audio_channel_mask_t channelMask() const;
+ uint32_t channelCount() const;
+
+ void dump(int fd, int spaces);
+
+private:
+
+ class ProxyCallback : public EffectCallbackInterface {
+ public:
+ ProxyCallback(DeviceEffectProxy *proxy,
+ const sp<DeviceEffectManagerCallback>& callback)
+ : mProxy(proxy), mManagerCallback(callback) {}
+
+ status_t createEffectHal(const effect_uuid_t *pEffectUuid,
+ int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
+ status_t allocateHalBuffer(size_t size __unused,
+ sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
+ bool updateOrphanEffectChains(const sp<EffectBase>& effect __unused) override {
+ return false;
+ }
+
+ audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
+ bool isOutput() const override;
+ bool isOffload() const override { return false; }
+ bool isOffloadOrDirect() const override { return false; }
+ bool isOffloadOrMmap() const override { return false; }
+
+ uint32_t sampleRate() const override;
+ audio_channel_mask_t channelMask() const override;
+ uint32_t channelCount() const override;
+ size_t frameCount() const override { return 0; }
+ uint32_t latency() const override { return 0; }
+
+ status_t addEffectToHal(sp<EffectHalInterface> effect) override;
+ status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
+
+ bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
+ void setVolumeForOutput(float left __unused, float right __unused) const override {}
+
+ void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
+ bool enabled __unused, bool threadLocked __unused) override {}
+ void resetVolume() override {}
+ uint32_t strategy() const override { return 0; }
+ int32_t activeTrackCnt() const override { return 0; }
+ void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
+ void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
+
+ wp<EffectChain> chain() const override { return nullptr; }
+
+ int newEffectId();
+
+ private:
+ const wp<DeviceEffectProxy> mProxy;
+ const sp<DeviceEffectManagerCallback> mManagerCallback;
+ };
+
+ status_t checkPort(const PatchPanel::Patch& patch, const struct audio_port_config *port,
+ sp<EffectHandle> *handle);
+
+ const AudioDeviceTypeAddr mDevice;
+ const sp<DeviceEffectManagerCallback> mManagerCallback;
+ const sp<ProxyCallback> mMyCallback;
+
+ Mutex mProxyLock;
+ std::map<audio_patch_handle_t, sp<EffectHandle>> mEffectHandles; // protected by mProxyLock
+ sp<EffectModule> mHalEffect; // protected by mProxyLock
+ struct audio_port_config mDevicePort = { .id = AUDIO_PORT_HANDLE_NONE };
};
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index ee5bd75..d8abb49 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -169,8 +169,7 @@
hwDevice->releaseAudioPatch(removedPatch.mHalHandle);
}
}
- mPatches.erase(iter);
- removeSoftwarePatchFromInsertedModules(*handle);
+ erasePatch(*handle);
}
}
@@ -325,10 +324,14 @@
}
}
status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ if (status == NO_ERROR) {
+ newPatch.setThread(thread);
+ }
+
// remove stale audio patch with same input as sink if any
for (auto& iter : mPatches) {
if (iter.second.mAudioPatch.sinks[0].ext.mix.handle == thread->id()) {
- mPatches.erase(iter.first);
+ erasePatch(iter.first);
break;
}
}
@@ -387,11 +390,14 @@
}
status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ if (status == NO_ERROR) {
+ newPatch.setThread(thread);
+ }
// remove stale audio patch with same output as source if any
for (auto& iter : mPatches) {
if (iter.second.mAudioPatch.sources[0].ext.mix.handle == thread->id()) {
- mPatches.erase(iter.first);
+ erasePatch(iter.first);
break;
}
}
@@ -405,11 +411,11 @@
if (status == NO_ERROR) {
*handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
newPatch.mHalHandle = halHandle;
+ mAudioFlinger.mDeviceEffectManager.createAudioPatch(*handle, newPatch);
mPatches.insert(std::make_pair(*handle, std::move(newPatch)));
if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
addSoftwarePatchToInsertedModules(insertedModule, *handle);
}
- ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle);
} else {
newPatch.clearConnections(this);
}
@@ -633,8 +639,21 @@
String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
{
// TODO: Consider table dump form for patches, just like tracks.
- String8 result = String8::format("Patch %d: thread %p => thread %p",
- myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get());
+ String8 result = String8::format("Patch %d: %s (thread %p => thread %p)",
+ myHandle, isSoftware() ? "Software bridge between" : "No software bridge",
+ mRecord.const_thread().get(), mPlayback.const_thread().get());
+
+ bool hasSinkDevice =
+ mAudioPatch.num_sinks > 0 && mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE;
+ bool hasSourceDevice =
+ mAudioPatch.num_sources > 0 && mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE;
+ result.appendFormat(" thread %p %s (%d) first device type %08x", mThread.unsafe_get(),
+ hasSinkDevice ? "num sinks" :
+ (hasSourceDevice ? "num sources" : "no devices"),
+ hasSinkDevice ? mAudioPatch.num_sinks :
+ (hasSourceDevice ? mAudioPatch.num_sources : 0),
+ hasSinkDevice ? mAudioPatch.sinks[0].ext.device.type :
+ (hasSourceDevice ? mAudioPatch.sources[0].ext.device.type : 0));
// add latency if it exists
double latencyMs;
@@ -710,11 +729,16 @@
status = BAD_VALUE;
}
- mPatches.erase(iter);
- removeSoftwarePatchFromInsertedModules(handle);
+ erasePatch(handle);
return status;
}
+void AudioFlinger::PatchPanel::erasePatch(audio_patch_handle_t handle) {
+ mPatches.erase(handle);
+ removeSoftwarePatchFromInsertedModules(handle);
+ mAudioFlinger.mDeviceEffectManager.releaseAudioPatch(handle);
+}
+
/* List connected audio ports and they attributes */
status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
struct audio_patch *patches __unused)
@@ -798,16 +822,13 @@
String8 patchPanelDump;
const char *indent = " ";
- // Only dump software patches.
bool headerPrinted = false;
for (const auto& iter : mPatches) {
- if (iter.second.isSoftware()) {
- if (!headerPrinted) {
- patchPanelDump += "\nSoftware patches:\n";
- headerPrinted = true;
- }
- patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
+ if (!headerPrinted) {
+ patchPanelDump += "\nPatches:\n";
+ headerPrinted = true;
}
+ patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
}
headerPrinted = false;
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 181e27c..4e0d243 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -76,13 +76,18 @@
void dump(int fd) const;
-private:
template<typename ThreadType, typename TrackType>
- class Endpoint {
+ class Endpoint final {
public:
Endpoint() = default;
Endpoint(const Endpoint&) = delete;
- Endpoint& operator=(const Endpoint&) = delete;
+ Endpoint& operator=(const Endpoint& other) noexcept {
+ mThread = other.mThread;
+ mCloseThread = other.mCloseThread;
+ mHandle = other.mHandle;
+ mTrack = other.mTrack;
+ return *this;
+ }
Endpoint(Endpoint&& other) noexcept { swap(other); }
Endpoint& operator=(Endpoint&& other) noexcept {
swap(other);
@@ -98,8 +103,8 @@
return trackOrNull->initCheck();
}
audio_patch_handle_t handle() const { return mHandle; }
- sp<ThreadType> thread() { return mThread; }
- sp<TrackType> track() { return mTrack; }
+ sp<ThreadType> thread() const { return mThread; }
+ sp<TrackType> track() const { return mTrack; }
sp<const ThreadType> const_thread() const { return mThread; }
sp<const TrackType> const_track() const { return mTrack; }
@@ -150,14 +155,36 @@
sp<TrackType> mTrack;
};
- class Patch {
+ class Patch final {
public:
explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {}
+ Patch() = default;
~Patch();
- Patch(const Patch&) = delete;
- Patch(Patch&&) = default;
- Patch& operator=(const Patch&) = delete;
- Patch& operator=(Patch&&) = default;
+ Patch(const Patch& other) noexcept {
+ mAudioPatch = other.mAudioPatch;
+ mHalHandle = other.mHalHandle;
+ mPlayback = other.mPlayback;
+ mRecord = other.mRecord;
+ mThread = other.mThread;
+ }
+ Patch(Patch&& other) noexcept { swap(other); }
+ Patch& operator=(Patch&& other) noexcept {
+ swap(other);
+ return *this;
+ }
+
+ void swap(Patch &other) noexcept {
+ using std::swap;
+ swap(mAudioPatch, other.mAudioPatch);
+ swap(mHalHandle, other.mHalHandle);
+ swap(mPlayback, other.mPlayback);
+ swap(mRecord, other.mRecord);
+ swap(mThread, other.mThread);
+ }
+
+ friend void swap(Patch &a, Patch &b) noexcept {
+ a.swap(b);
+ }
status_t createConnections(PatchPanel *panel);
void clearConnections(PatchPanel *panel);
@@ -165,6 +192,9 @@
return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
+ void setThread(sp<ThreadBase> thread) { mThread = thread; }
+ wp<ThreadBase> thread() const { return mThread; }
+
// returns the latency of the patch (from record to playback).
status_t getLatencyMs(double *latencyMs) const;
@@ -182,13 +212,20 @@
Endpoint<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
// connects source device to record thread input
Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
+
+ wp<ThreadBase> mThread;
};
+ // Call with AudioFlinger mLock held
+ std::map<audio_patch_handle_t, Patch>& patches_l() { return mPatches; }
+
+private:
AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
void addSoftwarePatchToInsertedModules(
audio_module_handle_t module, audio_patch_handle_t handle);
void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);
+ void erasePatch(audio_patch_handle_t handle);
AudioFlinger &mAudioFlinger;
std::map<audio_patch_handle_t, Patch> mPatches;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b5592e3..598caeb 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1146,32 +1146,26 @@
}
}
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId)
-{
- Mutex::Autolock _l(mLock);
- checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
-}
+void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(bool enabled,
+ audio_session_t sessionId,
+ bool threadLocked) {
+ if (!threadLocked) {
+ mLock.lock();
+ }
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId)
-{
if (mType != RECORD) {
// suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
// another session. This gives the priority to well behaved effect control panels
// and applications not using global effects.
// Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
// global effects
- if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) && (sessionId != AUDIO_SESSION_OUTPUT_STAGE)) {
+ if (!audio_is_global_session(sessionId)) {
setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
}
}
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- if (chain != 0) {
- chain->checkSuspendOnEffectEnabled(effect, enabled);
+ if (!threadLocked) {
+ mLock.unlock();
}
}
@@ -1179,8 +1173,9 @@
status_t AudioFlinger::RecordThread::checkEffectCompatibility_l(
const effect_descriptor_t *desc, audio_session_t sessionId)
{
- // No global effect sessions on record threads
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX || sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ // No global output effect sessions on record threads
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX
+ || sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
ALOGW("checkEffectCompatibility_l(): global effect %s on record thread %s",
desc->name, mThreadName);
return BAD_VALUE;
@@ -1254,6 +1249,13 @@
" on output stage session", desc->name);
return BAD_VALUE;
}
+ } else if (sessionId == AUDIO_SESSION_DEVICE) {
+ // only post processing on output stage session
+ if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) {
+ ALOGW("checkEffectCompatibility_l(): non post processing effect %s not allowed"
+ " on device session", desc->name);
+ return BAD_VALUE;
+ }
} else {
// no restriction on effects applied on non fast tracks
if ((hasAudioSession_l(sessionId) & ThreadBase::FAST_SESSION) == 0) {
@@ -1295,7 +1297,7 @@
return BAD_VALUE;
}
#endif
- if ((sessionId == AUDIO_SESSION_OUTPUT_STAGE) || (sessionId == AUDIO_SESSION_OUTPUT_MIX)) {
+ if (audio_is_global_session(sessionId)) {
ALOGW("checkEffectCompatibility_l(): global effect %s on DUPLICATING"
" thread %s", desc->name, mThreadName);
return BAD_VALUE;
@@ -1371,7 +1373,7 @@
if (effect == 0) {
effectId = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
// create a new effect module if none present in the chain
- lStatus = chain->createEffect_l(effect, this, desc, effectId, sessionId, pinned);
+ lStatus = chain->createEffect_l(effect, desc, effectId, sessionId, pinned);
if (lStatus != NO_ERROR) {
goto Exit;
}
@@ -1417,9 +1419,12 @@
sp<EffectModule> effect;
{
Mutex::Autolock _l(mLock);
-
- effect = handle->effect().promote();
- if (effect == 0) {
+ sp<EffectBase> effectBase = handle->effect().promote();
+ if (effectBase == nullptr) {
+ return;
+ }
+ effect = effectBase->asEffectModule();
+ if (effect == nullptr) {
return;
}
// restore suspended effects if the disconnected handle was enabled and the last one.
@@ -1431,11 +1436,34 @@
if (remove) {
mAudioFlinger->updateOrphanEffectChains(effect);
if (handle->enabled()) {
- checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
+ effect->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
}
}
}
+void AudioFlinger::ThreadBase::onEffectEnable(const sp<EffectModule>& effect) {
+ if (mType == OFFLOAD || mType == MMAP) {
+ Mutex::Autolock _l(mLock);
+ broadcast_l();
+ }
+ if (!effect->isOffloadable()) {
+ if (mType == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)this;
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::onEffectDisable() {
+ if (mType == OFFLOAD || mType == MMAP) {
+ Mutex::Autolock _l(mLock);
+ broadcast_l();
+ }
+}
+
sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(audio_session_t sessionId,
int effectId)
{
@@ -1511,7 +1539,7 @@
detachAuxEffect_l(effect->id());
}
- sp<EffectChain> chain = effect->chain().promote();
+ sp<EffectChain> chain = effect->callback()->chain().promote();
if (chain != 0) {
// remove effect chain if removing last effect
if (chain->removeEffect_l(effect, release) == 0) {
@@ -2051,6 +2079,7 @@
{ // scope for mLock
Mutex::Autolock _l(mLock);
for (audio_session_t session : {
+ AUDIO_SESSION_DEVICE,
AUDIO_SESSION_OUTPUT_STAGE,
AUDIO_SESSION_OUTPUT_MIX,
sessionId,
@@ -3103,7 +3132,7 @@
halOutBuffer = halInBuffer;
effect_buffer_t *buffer = reinterpret_cast<effect_buffer_t*>(halInBuffer->externalData());
ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
- if (session > AUDIO_SESSION_OUTPUT_MIX) {
+ if (!audio_is_global_session(session)) {
// Only one effect chain can be present in direct output thread and it uses
// the sink buffer as input
if (mType != DIRECT) {
@@ -3143,8 +3172,11 @@
chain->setThread(this);
chain->setInBuffer(halInBuffer);
chain->setOutBuffer(halOutBuffer);
- // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect
- // chains list in order to be processed last as it contains output stage effects.
+ // Effect chain for session AUDIO_SESSION_DEVICE is inserted at end of effect
+ // chains list in order to be processed last as it contains output device effects.
+ // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted just before to apply post
+ // processing effects specific to an output stream before effects applied to all streams
+ // routed to a given device.
// Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
// session AUDIO_SESSION_OUTPUT_STAGE to be processed
// after track specific effects and before output stage.
@@ -3154,7 +3186,8 @@
// chains list to be processed before output mix effects. Relative order between other
// sessions is not important.
static_assert(AUDIO_SESSION_OUTPUT_MIX == 0 &&
- AUDIO_SESSION_OUTPUT_STAGE < AUDIO_SESSION_OUTPUT_MIX,
+ AUDIO_SESSION_OUTPUT_STAGE < AUDIO_SESSION_OUTPUT_MIX &&
+ AUDIO_SESSION_DEVICE < AUDIO_SESSION_OUTPUT_STAGE,
"audio_session_t constants misdefined");
size_t size = mEffectChains.size();
size_t i = 0;
@@ -9093,8 +9126,8 @@
const effect_descriptor_t *desc, audio_session_t sessionId)
{
// No global effect sessions on mmap threads
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX || sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
- ALOGW("checkEffectCompatibility_l(): global effect %s on record thread %s",
+ if (audio_is_global_session(sessionId)) {
+ ALOGW("checkEffectCompatibility_l(): global effect %s on MMAP thread %s",
desc->name, mThreadName);
return BAD_VALUE;
}
@@ -9116,7 +9149,6 @@
}
return NO_ERROR;
-
}
void AudioFlinger::MmapThread::checkInvalidTracks_l()
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 8de7632..4c53e28 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -271,6 +271,8 @@
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
// and returns the [normal mix] buffer's frame count.
virtual size_t frameCount() const = 0;
+ virtual uint32_t latency_l() const { return 0; }
+ virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
// Return's the HAL's frame count i.e. fast mixer buffer size.
size_t frameCountHAL() const { return mFrameCount; }
@@ -424,14 +426,9 @@
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId =
- AUDIO_SESSION_OUTPUT_MIX);
- void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- audio_session_t sessionId =
- AUDIO_SESSION_OUTPUT_MIX);
+ void checkSuspendOnEffectEnabled(bool enabled,
+ audio_session_t sessionId,
+ bool threadLocked);
virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
@@ -465,6 +462,9 @@
mutable Mutex mLock;
+ void onEffectEnable(const sp<EffectModule>& effect);
+ void onEffectDisable();
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -814,7 +814,7 @@
// return estimated latency in milliseconds, as reported by HAL
uint32_t latency() const;
// same, but lock must already be held
- uint32_t latency_l() const;
+ uint32_t latency_l() const override;
// VolumeInterface
virtual void setMasterVolume(float value);
@@ -824,7 +824,7 @@
virtual void setStreamMute(audio_stream_type_t stream, bool muted);
virtual float streamVolume(audio_stream_type_t stream) const;
- void setVolumeForOutput_l(float left, float right) const;
+ void setVolumeForOutput_l(float left, float right) const override;
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index b427794..fbe5d0d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -2756,12 +2756,14 @@
int session,
int id)
{
- ssize_t index = mOutputs.indexOfKey(io);
- if (index < 0) {
- index = mInputs.indexOfKey(io);
+ if (session != AUDIO_SESSION_DEVICE) {
+ ssize_t index = mOutputs.indexOfKey(io);
if (index < 0) {
- ALOGW("registerEffect() unknown io %d", io);
- return INVALID_OPERATION;
+ index = mInputs.indexOfKey(io);
+ if (index < 0) {
+ ALOGW("registerEffect() unknown io %d", io);
+ return INVALID_OPERATION;
+ }
}
}
return mEffects.registerEffect(desc, io, session, id,
diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk
index 1144970..fdf3eae 100644
--- a/services/audiopolicy/service/Android.mk
+++ b/services/audiopolicy/service/Android.mk
@@ -24,6 +24,7 @@
libbinder \
libaudioclient \
libaudioutils \
+ libaudiofoundation \
libhardware_legacy \
libaudiopolicymanager \
libmedia_helper \