Keep effects sessions active when the caller dies.
Don't remove effects until the session they are in goes away or all
AudioEffects have been explicitly released. This allows the control
panel process to die without stopping the effects.
Change-Id: I4496e5df080230ca1af149dec95c1309ab8ea888
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f20e234..eb22e32 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -108,6 +108,8 @@
static unsigned int getInputFramesLost(audio_io_handle_t ioHandle);
static int newAudioSessionId();
+ static void acquireAudioSessionId(int audioSession);
+ static void releaseAudioSessionId(int audioSession);
// types of io configuration change events received with ioConfigChanged()
enum io_config_event {
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 4037c46..9e3cb7f 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -139,6 +139,9 @@
virtual int newAudioSessionId() = 0;
+ virtual void acquireAudioSessionId(int audioSession) = 0;
+ virtual void releaseAudioSessionId(int audioSession) = 0;
+
virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
virtual status_t queryEffect(uint32_t index, effect_descriptor_t *pDescriptor) = 0;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 16554c2..e5062ab 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -114,6 +114,7 @@
}
mAudioRecord.clear();
IPCThreadState::self()->flushCommands();
+ AudioSystem::releaseAudioSessionId(mSessionId);
}
}
@@ -233,6 +234,7 @@
mInputSource = (uint8_t)inputSource;
mFlags = flags;
mInput = input;
+ AudioSystem::acquireAudioSessionId(mSessionId);
return NO_ERROR;
}
@@ -465,6 +467,7 @@
((uint16_t)flags) << 16,
&mSessionId,
&status);
+
if (record == 0) {
LOGE("AudioFlinger could not create record track, status: %d", status);
return status;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 5009957..b26ed71 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -356,6 +356,20 @@
return af->newAudioSessionId();
}
+void AudioSystem::acquireAudioSessionId(int audioSession) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af != 0) {
+ af->acquireAudioSessionId(audioSession);
+ }
+}
+
+void AudioSystem::releaseAudioSessionId(int audioSession) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af != 0) {
+ af->releaseAudioSessionId(audioSession);
+ }
+}
+
// ---------------------------------------------------------------------------
void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 31eb97a..3949c39 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -134,6 +134,7 @@
}
mAudioTrack.clear();
IPCThreadState::self()->flushCommands();
+ AudioSystem::releaseAudioSessionId(mSessionId);
}
}
@@ -259,6 +260,7 @@
mNewPosition = 0;
mUpdatePeriod = 0;
mFlags = flags;
+ AudioSystem::acquireAudioSessionId(mSessionId);
return NO_ERROR;
}
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 4a12962..d58834b 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -1,4 +1,4 @@
-/* //device/extlibs/pv/android/IAudioflinger.cpp
+/*
**
** Copyright 2007, The Android Open Source Project
**
@@ -63,6 +63,8 @@
GET_RENDER_POSITION,
GET_INPUT_FRAMES_LOST,
NEW_AUDIO_SESSION_ID,
+ ACQUIRE_AUDIO_SESSION_ID,
+ RELEASE_AUDIO_SESSION_ID,
QUERY_NUM_EFFECTS,
QUERY_EFFECT,
GET_EFFECT_DESCRIPTOR,
@@ -526,6 +528,22 @@
return id;
}
+ virtual void acquireAudioSessionId(int audioSession)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(audioSession);
+ remote()->transact(ACQUIRE_AUDIO_SESSION_ID, data, &reply);
+ }
+
+ virtual void releaseAudioSessionId(int audioSession)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(audioSession);
+ remote()->transact(RELEASE_AUDIO_SESSION_ID, data, &reply);
+ }
+
virtual status_t queryNumberEffects(uint32_t *numEffects)
{
Parcel data, reply;
@@ -919,6 +937,18 @@
reply->writeInt32(newAudioSessionId());
return NO_ERROR;
} break;
+ case ACQUIRE_AUDIO_SESSION_ID: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ int audioSession = data.readInt32();
+ acquireAudioSessionId(audioSession);
+ return NO_ERROR;
+ } break;
+ case RELEASE_AUDIO_SESSION_ID: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ int audioSession = data.readInt32();
+ releaseAudioSessionId(audioSession);
+ return NO_ERROR;
+ } break;
case QUERY_NUM_EFFECTS: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
uint32_t numEffects;
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 3dd9249..67a66a2 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -61,12 +61,14 @@
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
mAudioSessionId = AudioSystem::newAudioSessionId();
+ AudioSystem::acquireAudioSessionId(mAudioSessionId);
mSendLevel = 0;
}
MediaPlayer::~MediaPlayer()
{
LOGV("destructor");
+ AudioSystem::releaseAudioSessionId(mAudioSessionId);
disconnect();
IPCThreadState::self()->flushCommands();
}
@@ -618,7 +620,11 @@
if (sessionId < 0) {
return BAD_VALUE;
}
- mAudioSessionId = sessionId;
+ if (sessionId != mAudioSessionId) {
+ AudioSystem::releaseAudioSessionId(mAudioSessionId);
+ AudioSystem::acquireAudioSessionId(sessionId);
+ mAudioSessionId = sessionId;
+ }
return NO_ERROR;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e201b17..d6bfda6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -264,6 +264,14 @@
}
}
}
+
+ result.append("Global session refs:\n");
+ result.append(" session pid cnt\n");
+ for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
+ AudioSessionRef *r = mAudioSessionRefs[i];
+ snprintf(buffer, SIZE, " %7d %3d %3d\n", r->sessionid, r->pid, r->cnt);
+ result.append(buffer);
+ }
write(fd, result.string(), result.size());
return NO_ERROR;
}
@@ -892,6 +900,25 @@
LOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
mNotificationClients.removeItem(pid);
}
+
+ LOGV("%d died, releasing its sessions", pid);
+ int num = mAudioSessionRefs.size();
+ bool removed = false;
+ for (int i = 0; i< num; i++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
+ LOGV(" pid %d @ %d", ref->pid, i);
+ if (ref->pid == pid) {
+ LOGV(" removing entry for pid %d session %d", pid, ref->sessionid);
+ mAudioSessionRefs.removeAt(i);
+ delete ref;
+ removed = true;
+ i--;
+ num--;
+ }
+ }
+ if (removed) {
+ purgeStaleEffects_l();
+ }
}
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
@@ -5042,6 +5069,109 @@
return nextUniqueId();
}
+void AudioFlinger::acquireAudioSessionId(int audioSession)
+{
+ Mutex::Autolock _l(mLock);
+ int caller = IPCThreadState::self()->getCallingPid();
+ LOGV("acquiring %d from %d", audioSession, caller);
+ int num = mAudioSessionRefs.size();
+ for (int i = 0; i< num; i++) {
+ AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
+ if (ref->sessionid == audioSession && ref->pid == caller) {
+ ref->cnt++;
+ LOGV(" incremented refcount to %d", ref->cnt);
+ return;
+ }
+ }
+ AudioSessionRef *ref = new AudioSessionRef();
+ ref->sessionid = audioSession;
+ ref->pid = caller;
+ ref->cnt = 1;
+ mAudioSessionRefs.push(ref);
+ LOGV(" added new entry for %d", ref->sessionid);
+}
+
+void AudioFlinger::releaseAudioSessionId(int audioSession)
+{
+ Mutex::Autolock _l(mLock);
+ int caller = IPCThreadState::self()->getCallingPid();
+ LOGV("releasing %d from %d", audioSession, caller);
+ int num = mAudioSessionRefs.size();
+ for (int i = 0; i< num; i++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
+ if (ref->sessionid == audioSession && ref->pid == caller) {
+ ref->cnt--;
+ LOGV(" decremented refcount to %d", ref->cnt);
+ if (ref->cnt == 0) {
+ mAudioSessionRefs.removeAt(i);
+ delete ref;
+ purgeStaleEffects_l();
+ }
+ return;
+ }
+ }
+ LOGW("session id %d not found for pid %d", audioSession, caller);
+}
+
+void AudioFlinger::purgeStaleEffects_l() {
+
+ LOGV("purging stale effects");
+
+ Vector< sp<EffectChain> > chains;
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ for (size_t j = 0; j < t->mEffectChains.size(); j++) {
+ sp<EffectChain> ec = t->mEffectChains[j];
+ chains.push(ec);
+ }
+ }
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ sp<RecordThread> t = mRecordThreads.valueAt(i);
+ for (size_t j = 0; j < t->mEffectChains.size(); j++) {
+ sp<EffectChain> ec = t->mEffectChains[j];
+ chains.push(ec);
+ }
+ }
+
+ for (size_t i = 0; i < chains.size(); i++) {
+ sp<EffectChain> ec = chains[i];
+ int sessionid = ec->sessionId();
+ sp<ThreadBase> t = ec->mThread.promote();
+ if (t == 0) {
+ continue;
+ }
+ size_t numsessionrefs = mAudioSessionRefs.size();
+ bool found = false;
+ for (size_t k = 0; k < numsessionrefs; k++) {
+ AudioSessionRef *ref = mAudioSessionRefs.itemAt(k);
+ if (ref->sessionid == sessionid) {
+ LOGV(" session %d still exists for %d with %d refs",
+ sessionid, ref->pid, ref->cnt);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // remove all effects from the chain
+ while (ec->mEffects.size()) {
+ sp<EffectModule> effect = ec->mEffects[0];
+ effect->unPin();
+ Mutex::Autolock _l (t->mLock);
+ t->removeEffect_l(effect);
+ for (size_t j = 0; j < effect->mHandles.size(); j++) {
+ sp<EffectHandle> handle = effect->mHandles[j].promote();
+ if (handle != 0) {
+ handle->mEffect.clear();
+ }
+ }
+ AudioSystem::unregisterEffect(effect->id());
+ }
+ }
+ }
+ return;
+}
+
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
{
@@ -5199,6 +5329,7 @@
}
uint32_t numEffects = 0;
effect_descriptor_t d;
+ d.flags = 0; // prevent compiler warning
bool found = false;
lStatus = EffectQueryNumberEffects(&numEffects);
@@ -5302,7 +5433,7 @@
mClients.add(pid, client);
}
- // create effect on selected output trhead
+ // create effect on selected output thread
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus);
if (handle != 0 && id != NULL) {
@@ -5344,7 +5475,7 @@
return NO_ERROR;
}
-// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
+// moveEffectChain_l must be called with both srcThread and dstThread mLocks held
status_t AudioFlinger::moveEffectChain_l(int sessionId,
AudioFlinger::PlaybackThread *srcThread,
AudioFlinger::PlaybackThread *dstThread,
@@ -5370,7 +5501,7 @@
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
int dstOutput = dstThread->id();
sp<EffectChain> dstChain;
- uint32_t strategy;
+ uint32_t strategy = 0; // prevent compiler warning
sp<EffectModule> effect = chain->getEffectFromId_l(0);
while (effect != 0) {
srcThread->removeEffect_l(effect);
@@ -5632,14 +5763,17 @@
}
void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
- const wp<EffectHandle>& handle) {
+ const wp<EffectHandle>& handle,
+ bool unpiniflast) {
Mutex::Autolock _l(mLock);
LOGV("disconnectEffect() %p effect %p", this, effect.get());
// delete the effect module if removing last handle on it
if (effect->removeHandle(handle) == 0) {
- removeEffect_l(effect);
- AudioSystem::unregisterEffect(effect->id());
+ if (!effect->isPinned() || unpiniflast) {
+ removeEffect_l(effect);
+ AudioSystem::unregisterEffect(effect->id());
+ }
}
}
@@ -5847,6 +5981,9 @@
goto Error;
}
+ if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+ mPinned = true;
+ }
LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
return;
Error:
@@ -5938,7 +6075,7 @@
// 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 (size == 0) {
+ if (size == 0 && !mPinned) {
mState = DESTROYED;
}
@@ -5955,9 +6092,7 @@
return handle;
}
-
-
-void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle)
+void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool unpiniflast)
{
LOGV("disconnect() %p handle %p ", this, handle.unsafe_get());
// keep a strong reference on this EffectModule to avoid calling the
@@ -5966,7 +6101,7 @@
{
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- thread->disconnectEffect(keep, handle);
+ thread->disconnectEffect(keep, handle, unpiniflast);
}
}
}
@@ -6533,11 +6668,14 @@
const sp<IEffectClient>& effectClient,
int32_t priority)
: BnEffect(),
- mEffect(effect), mEffectClient(effectClient), mClient(client),
+ mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
mPriority(priority), mHasControl(false), mEnabled(false)
{
LOGV("constructor %p", this);
+ if (client == 0) {
+ return;
+ }
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
if (mCblkMemory != 0) {
@@ -6556,7 +6694,7 @@
AudioFlinger::EffectHandle::~EffectHandle()
{
LOGV("Destructor %p", this);
- disconnect();
+ disconnect(false);
LOGV("Destructor DONE %p", this);
}
@@ -6605,12 +6743,16 @@
void AudioFlinger::EffectHandle::disconnect()
{
- LOGV("disconnect %p", this);
+ disconnect(true);
+}
+
+void AudioFlinger::EffectHandle::disconnect(bool unpiniflast)
+{
+ LOGV("disconnect(%s)", unpiniflast ? "true" : "false");
if (mEffect == 0) {
return;
}
-
- mEffect->disconnect(this);
+ mEffect->disconnect(this, unpiniflast);
sp<ThreadBase> thread = mEffect->thread().promote();
if (thread != 0) {
@@ -6619,11 +6761,11 @@
// release sp on module => module destructor can be called now
mEffect.clear();
- if (mCblk) {
- mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
- }
- mCblkMemory.clear(); // and free the shared memory
if (mClient != 0) {
+ if (mCblk) {
+ mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
+ }
+ mCblkMemory.clear(); // and free the shared memory
Mutex::Autolock _l(mClient->audioFlinger()->mLock);
mClient.clear();
}
@@ -6643,6 +6785,7 @@
return INVALID_OPERATION;
}
if (mEffect == 0) return DEAD_OBJECT;
+ if (mClient == 0) return INVALID_OPERATION;
// handle commands that are not forwarded transparently to effect engine
if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) {
@@ -6749,15 +6892,15 @@
void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
{
- bool locked = tryLock(mCblk->lock);
+ bool locked = mCblk ? tryLock(mCblk->lock) : false;
snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
(mClient == NULL) ? getpid() : mClient->pid(),
mPriority,
mHasControl,
!locked,
- mCblk->clientIndex,
- mCblk->serverIndex
+ mCblk ? mCblk->clientIndex : 0,
+ mCblk ? mCblk->serverIndex : 0
);
if (locked) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 4fa70a2..3a0aac9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -149,6 +149,10 @@
virtual int newAudioSessionId();
+ virtual void acquireAudioSessionId(int audioSession);
+
+ virtual void releaseAudioSessionId(int audioSession);
+
virtual status_t queryNumberEffects(uint32_t *numEffects);
virtual status_t queryEffect(uint32_t index, effect_descriptor_t *descriptor);
@@ -215,6 +219,7 @@
status_t initCheck() const;
virtual void onFirstRef();
audio_hw_device_t* findSuitableHwDev_l(uint32_t devices);
+ void purgeStaleEffects_l();
// Internal dump utilites.
status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
@@ -436,7 +441,8 @@
int *enabled,
status_t *status);
void disconnectEffect(const sp< EffectModule>& effect,
- const wp<EffectHandle>& handle);
+ const wp<EffectHandle>& handle,
+ bool unpiniflast);
// return values for hasAudioSession (bit field)
enum effect_state {
@@ -519,6 +525,7 @@
// updated mSuspendedSessions when an effect chain is removed
void updateSuspendedSessionsOnRemoveEffectChain_l(const sp<EffectChain>& chain);
+ friend class AudioFlinger;
friend class Track;
friend class TrackBase;
friend class PlaybackThread;
@@ -607,7 +614,6 @@
protected:
friend class ThreadBase;
- friend class AudioFlinger;
friend class TrackHandle;
friend class PlaybackThread;
friend class MixerThread;
@@ -1100,7 +1106,7 @@
wp<ThreadBase>& thread() { return mThread; }
status_t addHandle(sp<EffectHandle>& handle);
- void disconnect(const wp<EffectHandle>& handle);
+ void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
size_t removeHandle (const wp<EffectHandle>& handle);
effect_descriptor_t& desc() { return mDescriptor; }
@@ -1115,9 +1121,15 @@
sp<EffectHandle> controlHandle();
+ bool isPinned() { return mPinned; }
+ void unPin() { mPinned = false; }
+
status_t dump(int fd, const Vector<String16>& args);
protected:
+ friend class EffectHandle;
+ friend class AudioFlinger;
+ bool mPinned;
// Maximum time allocated to effect engines to complete the turn off sequence
static const uint32_t MAX_DISABLE_TIME_MS = 10000;
@@ -1169,6 +1181,7 @@
uint32_t *replySize,
void *pReplyData);
virtual void disconnect();
+ virtual void disconnect(bool unpiniflast);
virtual sp<IMemory> getCblk() const;
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -1196,7 +1209,8 @@
void dump(char* buffer, size_t size);
protected:
-
+ friend class AudioFlinger;
+ friend class EffectModule;
EffectHandle(const EffectHandle&);
EffectHandle& operator =(const EffectHandle&);
@@ -1288,7 +1302,7 @@
status_t dump(int fd, const Vector<String16>& args);
protected:
-
+ friend class AudioFlinger;
EffectChain(const EffectChain&);
EffectChain& operator =(const EffectChain&);
@@ -1344,6 +1358,12 @@
hwDev(dev), stream(in) {}
};
+ struct AudioSessionRef {
+ int sessionid;
+ pid_t pid;
+ int cnt;
+ };
+
friend class RecordThread;
friend class PlaybackThread;
@@ -1369,6 +1389,7 @@
uint32_t mMode;
bool mBtNrec;
+ Vector<AudioSessionRef*> mAudioSessionRefs;
};