Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad67b138 authored by Eric Laurent's avatar Eric Laurent Committed by android-build-merger
Browse files

audioflinger: add check for effect vs thread compatibility

am: 4c415062

Change-Id: Idbdb7591b291c09ced94d4eabae0edc3581e1bcd
parents 07303a00 4c415062
Loading
Loading
Loading
Loading
+6 −9
Original line number Diff line number Diff line
@@ -660,7 +660,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
                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 @@ audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId
    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 @@ status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId,
        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;
    }

+23 −0
Original line number Diff line number Diff line
@@ -1960,4 +1960,27 @@ void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
    }
}

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
+27 −20
Original line number Diff line number Diff line
@@ -117,6 +117,8 @@ public:
    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 @@ public:

    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:
@@ -362,7 +369,7 @@ protected:
    void setThread(const sp<ThreadBase>& thread);

             wp<ThreadBase> mThread;     // parent mixer thread
    Mutex mLock;                // mutex protecting effect list
    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
+199 −52
Original line number Diff line number Diff line
@@ -1256,6 +1256,135 @@ void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModu
    }
}

// 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 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
        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,7 +1895,43 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
            }
            frameCount = max(frameCount, mFrameCount * sFastTrackMultiplier); // incl framecount 0
        }
        ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",

        // 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 "
@@ -2400,9 +2527,9 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui
    }
}

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 @@ uint32_t AudioFlinger::PlaybackThread::hasAudioSession(audio_session_t sessionId
        sp<Track> track = mTracks[i];
        if (sessionId == track->sessionId() && !track->isInvalid()) {
            result |= TRACK_SESSION;
            if (track->isFastTrack()) {
                result |= FAST_SESSION;
            }
            break;
        }
    }
@@ -6333,7 +6463,21 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe
            // there are sufficient fast track slots available
            mFastTrackAvail
        ) {
        ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%zu mFrameCount=%zu",
          // 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 "
@@ -7224,9 +7368,9 @@ uint32_t AudioFlinger::RecordThread::getInputFramesLost()
    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 @@ uint32_t AudioFlinger::RecordThread::hasAudioSession(audio_session_t sessionId)
    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;
        }
    }
+25 −6
Original line number Diff line number Diff line
@@ -301,8 +301,10 @@ public:
                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 @@ public:
                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 @@ public:

                        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 @@ public:
    // 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 @@ public:

                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 @@ public:

    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 @@ public:
            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();