Merge "stagefright: move to system_ext"
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/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/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/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/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 525ac68..d0d1c32 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2944,7 +2944,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 +2971,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 +2994,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 +3294,7 @@
int32_t priority,
audio_io_handle_t io,
audio_session_t sessionId,
+ const AudioDeviceTypeAddr& device __unused,
const String16& opPackageName,
pid_t pid,
status_t *status,
@@ -3346,6 +3347,18 @@
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;
+ }
+ //TODO: add check on device ID when added to arguments
} else {
// general sessionId.
@@ -3381,7 +3394,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;
}
@@ -3479,7 +3492,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) {
@@ -3494,7 +3507,7 @@
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 +3617,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 +3667,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..e49229e 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*/,
@@ -532,6 +533,7 @@
class AsyncCallbackThread;
class Track;
class RecordTrack;
+ class EffectBase;
class EffectModule;
class EffectHandle;
class EffectChain;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index bbd0d2c..7f34c14 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,270 @@
}
// 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)
+ : 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, AUDIO_PORT_HANDLE_NONE, &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 +814,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 +837,6 @@
{
ALOGVV("configure() started");
status_t status;
- sp<ThreadBase> thread;
uint32_t size;
audio_channel_mask_t channelMask;
@@ -575,17 +845,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 +886,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 +901,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 +916,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 +937,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 +1004,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 +1051,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 +1111,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 +1127,7 @@
status = cmdStatus;
}
if (status == NO_ERROR) {
- status = remove_effect_from_hal_l();
+ status = removeEffectFromHal_l();
}
return status;
}
@@ -886,25 +1136,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 +1237,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 +1259,7 @@
bool AudioFlinger::EffectModule::isOffloadedOrDirect() const
{
- return (mThreadType == ThreadBase::OFFLOAD || mThreadType == ThreadBase::DIRECT);
+ return mCallback->isOffloadOrDirect();
}
bool AudioFlinger::EffectModule::isVolumeControlEnabled() const
@@ -1124,9 +1303,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 +1347,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 +1395,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 +1478,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 +1517,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 +1532,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 +1575,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 +1594,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)
@@ -1626,7 +1635,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 +1655,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 +1673,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 +1692,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 +1714,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 +1782,7 @@
}
AutoMutex _l(mLock);
- sp<EffectModule> effect = mEffect.promote();
+ sp<EffectBase> effect = mEffect.promote();
if (effect == 0 || mDisconnected) {
return DEAD_OBJECT;
}
@@ -1963,12 +1937,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 +2009,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 +2043,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 +2079,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);
status_t lStatus = effect->status();
if (lStatus == NO_ERROR) {
lStatus = addEffect_ll(effect);
@@ -2147,12 +2108,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 +2119,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 +2443,7 @@
if (effect != 0) {
desc->mEffect = effect;
effect->setSuspended(true);
- effect->setEnabled(false);
+ effect->setEnabled(false, false /*fromHandle*/);
}
}
} else {
@@ -2645,7 +2601,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 +2636,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 +2696,227 @@
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();
+}
+
} // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index dbf63c8..ea51c2c 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,141 @@
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; }
+
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);
+ 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 +267,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 +306,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 +317,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 +355,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 +374,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 +421,6 @@
}
status_t createEffect_l(sp<EffectModule>& effect,
- ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@@ -389,9 +476,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 +502,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 +581,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 +610,6 @@
// timeLow fields among effect type UUIDs.
// Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+
+ const sp<EffectCallback> mEffectCallback;
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b5592e3..14364b7 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,11 +1419,11 @@
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 = static_cast<EffectModule *>(effectBase.get());
// restore suspended effects if the disconnected handle was enabled and the last one.
remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
if (remove) {
@@ -1431,11 +1433,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 +1536,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 +2076,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 +3129,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 +3169,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 +3183,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 +9123,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 +9146,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/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 \