audioflinger: add check for effect vs thread compatibility am: 4c415062ad
am: ad67b138fc

Change-Id: I38015a85b7df2d620daa2c9e5d7435de510a8fbe
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a757aac..35a120a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -660,7 +660,7 @@
                 sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
                 if (mPlaybackThreads.keyAt(i) != output) {
                     uint32_t sessions = t->hasAudioSession(lSessionId);
-                    if (sessions & PlaybackThread::EFFECT_SESSION) {
+                    if (sessions & ThreadBase::EFFECT_SESSION) {
                         effectThread = t.get();
                         break;
                     }
@@ -1766,7 +1766,7 @@
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
         sp<PlaybackThread> thread = mPlaybackThreads.valueAt(i);
         uint32_t sessions = thread->hasAudioSession(sessionId);
-        if (sessions & PlaybackThread::TRACK_SESSION) {
+        if (sessions & ThreadBase::TRACK_SESSION) {
             AudioParameter param = AudioParameter();
             param.addInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), value);
             thread->setParameters(param.toString());
@@ -2824,14 +2824,11 @@
         return INVALID_OPERATION;
     }
 
-    // Check whether the destination thread has a channel count of FCC_2, which is
-    // currently required for (most) effects. Prevent moving the effect chain here rather
-    // than disabling the addEffect_l() call in dstThread below.
-    if ((dstThread->type() == ThreadBase::MIXER || dstThread->isDuplicating()) &&
-            dstThread->mChannelCount != FCC_2) {
+    // Check whether the destination thread and all effects in the chain are compatible
+    if (!chain->isCompatibleWithThread_l(dstThread)) {
         ALOGW("moveEffectChain_l() effect chain failed because"
-                " destination thread %p channel count(%u) != %u",
-                dstThread, dstThread->mChannelCount, FCC_2);
+                " destination thread %p is not compatible with effects in the chain",
+                dstThread);
         return INVALID_OPERATION;
     }
 
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 00304b2..a9aafd8 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1960,4 +1960,27 @@
     }
 }
 
+bool AudioFlinger::EffectChain::hasSoftwareEffect() const
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mEffects.size(); i++) {
+        if (mEffects[i]->isImplementationSoftware()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// isCompatibleWithThread_l() must be called with thread->mLock held
+bool AudioFlinger::EffectChain::isCompatibleWithThread_l(const sp<ThreadBase>& thread) const
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mEffects.size(); i++) {
+        if (thread->checkEffectCompatibility_l(&(mEffects[i]->desc()), mSessionId) != NO_ERROR) {
+            return false;
+        }
+    }
+    return true;
+}
+
 } // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index bc9bc94..3b62652 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -117,6 +117,8 @@
     void             unlock() { mLock.unlock(); }
     bool             isOffloadable() const
                         { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
+    bool             isImplementationSoftware() const
+                        { return (mDescriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0; }
     status_t         setOffloaded(bool offloaded, audio_io_handle_t io);
     bool             isOffloaded() const;
     void             addEffectToHal_l();
@@ -330,6 +332,11 @@
 
     void syncHalEffectsState();
 
+    bool hasSoftwareEffect() const;
+
+    // isCompatibleWithThread_l() must be called with thread->mLock held
+    bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+
     void dump(int fd, const Vector<String16>& args);
 
 protected:
@@ -361,30 +368,30 @@
 
     void setThread(const sp<ThreadBase>& thread);
 
-    wp<ThreadBase> mThread;     // parent mixer thread
-    Mutex mLock;                // mutex protecting effect list
-    Vector< sp<EffectModule> > mEffects; // list of effect modules
-    audio_session_t mSessionId; // audio session ID
-    int16_t *mInBuffer;         // chain input buffer
-    int16_t *mOutBuffer;        // chain output buffer
+             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
+             int16_t *mInBuffer;         // chain input buffer
+             int16_t *mOutBuffer;        // chain output buffer
 
     // 'volatile' here means these are accessed with atomic operations instead of mutex
     volatile int32_t mActiveTrackCnt;    // number of active tracks connected
     volatile int32_t mTrackCnt;          // number of tracks connected
 
-    int32_t mTailBufferCount;   // current effect tail buffer count
-    int32_t mMaxTailBuffers;    // maximum effect tail buffers
-    bool mOwnInBuffer;          // true if the chain owns its input buffer
-    int mVolumeCtrlIdx;         // index of insert effect having control over volume
-    uint32_t mLeftVolume;       // previous volume on left channel
-    uint32_t mRightVolume;      // previous volume on right channel
-    uint32_t mNewLeftVolume;       // new volume on left channel
-    uint32_t mNewRightVolume;      // new volume on right channel
-    uint32_t mStrategy; // strategy for this effect chain
-    // mSuspendedEffects lists all effects currently suspended in the chain.
-    // Use effect type UUID timelow field as key. There is no real risk of identical
-    // timeLow fields among effect type UUIDs.
-    // Updated by updateSuspendedSessions_l() only.
-    KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+             int32_t mTailBufferCount;   // current effect tail buffer count
+             int32_t mMaxTailBuffers;    // maximum effect tail buffers
+             bool mOwnInBuffer;          // true if the chain owns its input buffer
+             int mVolumeCtrlIdx;         // index of insert effect having control over volume
+             uint32_t mLeftVolume;       // previous volume on left channel
+             uint32_t mRightVolume;      // previous volume on right channel
+             uint32_t mNewLeftVolume;       // new volume on left channel
+             uint32_t mNewRightVolume;      // new volume on right channel
+             uint32_t mStrategy; // strategy for this effect chain
+             // mSuspendedEffects lists all effects currently suspended in the chain.
+             // Use effect type UUID timelow field as key. There is no real risk of identical
+             // timeLow fields among effect type UUIDs.
+             // Updated by updateSuspendedSessions_l() only.
+             KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
     volatile int32_t mForceVolume; // force next volume command because a new effect was enabled
 };
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index fb1d2b0..d721270 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1256,6 +1256,135 @@
     }
 }
 
+// checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+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) {
+        ALOGW("checkEffectCompatibility_l(): global effect %s on record thread %s",
+                desc->name, mThreadName);
+        return BAD_VALUE;
+    }
+    // only pre processing effects on record thread
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC) {
+        ALOGW("checkEffectCompatibility_l(): non pre processing effect %s on record thread %s",
+                desc->name, mThreadName);
+        return BAD_VALUE;
+    }
+    audio_input_flags_t flags = mInput->flags;
+    if (hasFastCapture() || (flags & AUDIO_INPUT_FLAG_FAST)) {
+        if (flags & AUDIO_INPUT_FLAG_RAW) {
+            ALOGW("checkEffectCompatibility_l(): effect %s on record thread %s in raw mode",
+                  desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+        if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+            ALOGW("checkEffectCompatibility_l(): non HW effect %s on record thread %s in fast mode",
+                  desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+    }
+    return NO_ERROR;
+}
+
+// checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::checkEffectCompatibility_l(
+        const effect_descriptor_t *desc, audio_session_t sessionId)
+{
+    // no preprocessing on playback threads
+    if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
+        ALOGW("checkEffectCompatibility_l(): pre processing effect %s created on playback"
+                " thread %s", desc->name, mThreadName);
+        return BAD_VALUE;
+    }
+
+    switch (mType) {
+    case MIXER: {
+        // Reject any effect on mixer multichannel sinks.
+        // TODO: fix both format and multichannel issues with effects.
+        if (mChannelCount != FCC_2) {
+            ALOGW("checkEffectCompatibility_l(): effect %s for multichannel(%d) on MIXER"
+                    " thread %s", desc->name, mChannelCount, mThreadName);
+            return BAD_VALUE;
+        }
+        audio_output_flags_t flags = mOutput->flags;
+        if (hasFastMixer() || (flags & AUDIO_OUTPUT_FLAG_FAST)) {
+            if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+                // global effects are applied only to non fast tracks if they are SW
+                if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+                    break;
+                }
+            } else if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+                // 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 output stage session", desc->name);
+                    return BAD_VALUE;
+                }
+            } else {
+                // no restriction on effects applied on non fast tracks
+                if ((hasAudioSession_l(sessionId) & ThreadBase::FAST_SESSION) == 0) {
+                    break;
+                }
+            }
+            if (flags & AUDIO_OUTPUT_FLAG_RAW) {
+                ALOGW("checkEffectCompatibility_l(): effect %s on playback thread in raw mode",
+                      desc->name);
+                return BAD_VALUE;
+            }
+            if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) {
+                ALOGW("checkEffectCompatibility_l(): non HW effect %s on playback thread"
+                        " in fast mode", desc->name);
+                return BAD_VALUE;
+            }
+        }
+    } break;
+    case OFFLOAD:
+        // only offloadable effects on offload thread
+        if ((desc->flags & EFFECT_FLAG_OFFLOAD_MASK) != EFFECT_FLAG_OFFLOAD_SUPPORTED) {
+            ALOGW("checkEffectCompatibility_l(): non offloadable effect %s created on"
+                    " OFFLOAD thread %s", desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+        break;
+    case DIRECT:
+        // Reject any effect on Direct output threads for now, since the format of
+        // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo).
+        ALOGW("checkEffectCompatibility_l(): effect %s on DIRECT output thread %s",
+                desc->name, mThreadName);
+        return BAD_VALUE;
+    case DUPLICATING:
+        // Reject any effect on mixer multichannel sinks.
+        // TODO: fix both format and multichannel issues with effects.
+        if (mChannelCount != FCC_2) {
+            ALOGW("checkEffectCompatibility_l(): effect %s for multichannel(%d)"
+                    " on DUPLICATING thread %s", desc->name, mChannelCount, mThreadName);
+            return BAD_VALUE;
+        }
+        if ((sessionId == AUDIO_SESSION_OUTPUT_STAGE) || (sessionId == AUDIO_SESSION_OUTPUT_MIX)) {
+            ALOGW("checkEffectCompatibility_l(): global effect %s on DUPLICATING"
+                    " thread %s", desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+        if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
+            ALOGW("checkEffectCompatibility_l(): post processing effect %s on"
+                    " DUPLICATING thread %s", desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+        if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) != 0) {
+            ALOGW("checkEffectCompatibility_l(): HW tunneled effect %s on"
+                    " DUPLICATING thread %s", desc->name, mThreadName);
+            return BAD_VALUE;
+        }
+        break;
+    default:
+        LOG_ALWAYS_FATAL("checkEffectCompatibility_l(): wrong thread type %d", mType);
+    }
+
+    return NO_ERROR;
+}
+
 // ThreadBase::createEffect_l() must be called with AudioFlinger::mLock held
 sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
         const sp<AudioFlinger::Client>& client,
@@ -1280,54 +1409,16 @@
         goto Exit;
     }
 
-    // Reject any effect on Direct output threads for now, since the format of
-    // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo).
-    if (mType == DIRECT) {
-        ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s",
-                desc->name, mThreadName);
-        lStatus = BAD_VALUE;
-        goto Exit;
-    }
-
-    // Reject any effect on mixer or duplicating multichannel sinks.
-    // TODO: fix both format and multichannel issues with effects.
-    if ((mType == MIXER || mType == DUPLICATING) && mChannelCount != FCC_2) {
-        ALOGW("createEffect_l() Cannot add effect %s for multichannel(%d) %s threads",
-                desc->name, mChannelCount, mType == MIXER ? "MIXER" : "DUPLICATING");
-        lStatus = BAD_VALUE;
-        goto Exit;
-    }
-
-    // Allow global effects only on offloaded and mixer threads
-    if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
-        switch (mType) {
-        case MIXER:
-        case OFFLOAD:
-            break;
-        case DIRECT:
-        case DUPLICATING:
-        case RECORD:
-        default:
-            ALOGW("createEffect_l() Cannot add global effect %s on thread %s",
-                    desc->name, mThreadName);
-            lStatus = BAD_VALUE;
-            goto Exit;
-        }
-    }
-
-    // Only Pre processor effects are allowed on input threads and only on input threads
-    if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
-        ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
-                desc->name, desc->flags, mType);
-        lStatus = BAD_VALUE;
-        goto Exit;
-    }
-
     ALOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
 
     { // scope for mLock
         Mutex::Autolock _l(mLock);
 
+        lStatus = checkEffectCompatibility_l(desc, sessionId);
+        if (lStatus != NO_ERROR) {
+            goto Exit;
+        }
+
         // check for existing effect chain with the requested audio session
         chain = getEffectChain_l(sessionId);
         if (chain == 0) {
@@ -1804,8 +1895,44 @@
             }
             frameCount = max(frameCount, mFrameCount * sFastTrackMultiplier); // incl framecount 0
         }
-        ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
-                frameCount, mFrameCount);
+
+        // check compatibility with audio effects.
+        { // scope for mLock
+            Mutex::Autolock _l(mLock);
+            // do not accept RAW flag if post processing are present. Note that post processing on
+            // a fast mixer are necessarily hardware
+            sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_STAGE);
+            if (chain != 0) {
+                ALOGV_IF((flags & AUDIO_OUTPUT_FLAG_RAW) != 0,
+                        "AUDIO_OUTPUT_FLAG_RAW denied: post processing effect present");
+                *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_RAW);
+            }
+            // Do not accept FAST flag if software global effects are present
+            chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+            if (chain != 0) {
+                ALOGV_IF((flags & AUDIO_OUTPUT_FLAG_RAW) != 0,
+                        "AUDIO_OUTPUT_FLAG_RAW denied: global effect present");
+                *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_RAW);
+                if (chain->hasSoftwareEffect()) {
+                    ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: software global effect present");
+                    *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
+                }
+            }
+            // Do not accept FAST flag if the session has software effects
+            chain = getEffectChain_l(sessionId);
+            if (chain != 0) {
+                ALOGV_IF((flags & AUDIO_OUTPUT_FLAG_RAW) != 0,
+                        "AUDIO_OUTPUT_FLAG_RAW denied: effect present on session");
+                *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_RAW);
+                if (chain->hasSoftwareEffect()) {
+                    ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: software effect present on session");
+                    *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
+                }
+            }
+        }
+        ALOGV_IF((flags & AUDIO_OUTPUT_FLAG_FAST) != 0,
+                 "AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+                 frameCount, mFrameCount);
       } else {
         ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: sharedBuffer=%p frameCount=%zu "
                 "mFrameCount=%zu format=%#x mFormat=%#x isLinear=%d channelMask=%#x "
@@ -1814,7 +1941,7 @@
                 sharedBuffer.get(), frameCount, mFrameCount, format, mFormat,
                 audio_is_linear_pcm(format),
                 channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
-        *flags = (audio_output_flags_t)(*flags &~AUDIO_OUTPUT_FLAG_FAST);
+        *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
       }
     }
     // For normal PCM streaming tracks, update minimum frame count.
@@ -2400,9 +2527,9 @@
     }
 }
 
-uint32_t AudioFlinger::PlaybackThread::hasAudioSession(audio_session_t sessionId) const
+// hasAudioSession_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession_l(audio_session_t sessionId) const
 {
-    Mutex::Autolock _l(mLock);
     uint32_t result = 0;
     if (getEffectChain_l(sessionId) != 0) {
         result = EFFECT_SESSION;
@@ -2412,6 +2539,9 @@
         sp<Track> track = mTracks[i];
         if (sessionId == track->sessionId() && !track->isInvalid()) {
             result |= TRACK_SESSION;
+            if (track->isFastTrack()) {
+                result |= FAST_SESSION;
+            }
             break;
         }
     }
@@ -6333,8 +6463,22 @@
             // there are sufficient fast track slots available
             mFastTrackAvail
         ) {
-        ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
-                frameCount, mFrameCount);
+          // check compatibility with audio effects.
+          Mutex::Autolock _l(mLock);
+          // Do not accept FAST flag if the session has software effects
+          sp<EffectChain> chain = getEffectChain_l(sessionId);
+          if (chain != 0) {
+              ALOGV_IF((flags & AUDIO_INPUT_FLAG_RAW) != 0,
+                      "AUDIO_INPUT_FLAG_RAW denied: effect present on session");
+              *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_RAW);
+              if (chain->hasSoftwareEffect()) {
+                  ALOGV("AUDIO_INPUT_FLAG_FAST denied: software effect present on session");
+                  *flags = (audio_input_flags_t)(*flags & ~AUDIO_INPUT_FLAG_FAST);
+              }
+          }
+          ALOGV_IF((flags & AUDIO_INPUT_FLAG_FAST) != 0,
+                   "AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
+                   frameCount, mFrameCount);
       } else {
         ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%zu mFrameCount=%zu mPipeFramesP2=%zu "
                 "format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
@@ -7224,9 +7368,9 @@
     return mInput->stream->get_input_frames_lost(mInput->stream);
 }
 
-uint32_t AudioFlinger::RecordThread::hasAudioSession(audio_session_t sessionId) const
+// hasAudioSession_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::RecordThread::hasAudioSession_l(audio_session_t sessionId) const
 {
-    Mutex::Autolock _l(mLock);
     uint32_t result = 0;
     if (getEffectChain_l(sessionId) != 0) {
         result = EFFECT_SESSION;
@@ -7235,6 +7379,9 @@
     for (size_t i = 0; i < mTracks.size(); ++i) {
         if (sessionId == mTracks[i]->sessionId()) {
             result |= TRACK_SESSION;
+            if (mTracks[i]->isFastTrack()) {
+                result |= FAST_SESSION;
+            }
             break;
         }
     }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 0b4fbb9..1bfbca9 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -301,8 +301,10 @@
                 enum effect_state {
                     EFFECT_SESSION = 0x1,   // the audio session corresponds to at least one
                                             // effect
-                    TRACK_SESSION = 0x2     // the audio session corresponds to at least one
+                    TRACK_SESSION = 0x2,    // the audio session corresponds to at least one
                                             // track
+                    FAST_SESSION = 0x4      // the audio session corresponds to at least one
+                                            // fast track
                 };
 
                 // get effect chain corresponding to session Id.
@@ -335,9 +337,16 @@
                 void removeEffect_l(const sp< EffectModule>& effect);
                 // detach all tracks connected to an auxiliary effect
     virtual     void detachAuxEffect_l(int effectId __unused) {}
-                // returns either EFFECT_SESSION if effects on this audio session exist in one
-                // chain, or TRACK_SESSION if tracks on this audio session exist, or both
-                virtual uint32_t hasAudioSession(audio_session_t sessionId) const = 0;
+                // returns a combination of:
+                // - EFFECT_SESSION if effects on this audio session exist in one chain
+                // - TRACK_SESSION if tracks on this audio session exist
+                // - FAST_SESSION if fast tracks on this audio session exist
+    virtual     uint32_t hasAudioSession_l(audio_session_t sessionId) const = 0;
+                uint32_t hasAudioSession(audio_session_t sessionId) const {
+                    Mutex::Autolock _l(mLock);
+                    return hasAudioSession_l(sessionId);
+                }
+
                 // the value returned by default implementation is not important as the
                 // strategy is only meaningful for PlaybackThread which implements this method
                 virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
@@ -374,6 +383,10 @@
 
                         void systemReady();
 
+                // checkEffectCompatibility_l() must be called with ThreadBase::mLock held
+                virtual status_t    checkEffectCompatibility_l(const effect_descriptor_t *desc,
+                                                               audio_session_t sessionId) = 0;
+
     mutable     Mutex                   mLock;
 
 protected:
@@ -506,6 +519,9 @@
     // RefBase
     virtual     void        onFirstRef();
 
+    virtual     status_t    checkEffectCompatibility_l(const effect_descriptor_t *desc,
+                                                       audio_session_t sessionId);
+
 protected:
     // Code snippets that were lifted up out of threadLoop()
     virtual     void        threadLoop_mix() = 0;
@@ -605,7 +621,7 @@
 
                 virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
                 virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
-                virtual uint32_t hasAudioSession(audio_session_t sessionId) const;
+                virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const;
                 virtual uint32_t getStrategyForSession_l(audio_session_t sessionId);
 
 
@@ -1292,7 +1308,7 @@
 
     virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
     virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
-    virtual uint32_t hasAudioSession(audio_session_t sessionId) const;
+    virtual uint32_t hasAudioSession_l(audio_session_t sessionId) const;
 
             // Return the set of unique session IDs across all tracks.
             // The keys are the session IDs, and the associated values are meaningless.
@@ -1308,6 +1324,9 @@
             bool        hasFastCapture() const { return mFastCapture != 0; }
     virtual void        getAudioPortConfig(struct audio_port_config *config);
 
+    virtual status_t    checkEffectCompatibility_l(const effect_descriptor_t *desc,
+                                                   audio_session_t sessionId);
+
 private:
             // Enter standby if not already in standby, and set mStandby flag
             void    standbyIfNotAlreadyInStandby();