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

Commit 41709559 authored by Eric Laurent's avatar Eric Laurent
Browse files

audioflinger: refactor EffectModule class

Introduce a class EffecBase as a parent class of
EffectModule with default implementation for handles management,
basic properties storage and state management.

Bug: 146177259
Test: CTS: AudioEffectTest, EqualizerTest, VisualizerTest,
AudioPreProcessingTest
Test: manual test: Play Music EQ settings, Duo EAC and NS
Change-Id: Id91cef9db6b4f6be0d2e77a8b4afcbdc7234e519
parent c43423a6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3529,7 +3529,7 @@ sp<IEffect> AudioFlinger::createEffect(

    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) {
+1 −0
Original line number Diff line number Diff line
@@ -533,6 +533,7 @@ private:
    class AsyncCallbackThread;
    class Track;
    class RecordTrack;
    class EffectBase;
    class EffectModule;
    class EffectHandle;
    class EffectChain;
+372 −327

File changed.

Preview size limit exceeded, changes collapsed.

+132 −88
Original line number Diff line number Diff line
@@ -45,17 +45,17 @@ public:
    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<EffectModule>& effect,
    virtual void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect,
                                             bool enabled,
                                             bool threadLocked) = 0;
    virtual void onEffectEnable(const sp<EffectModule>& effect) = 0;
    virtual void onEffectDisable(const sp<EffectModule>& effect) = 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<EffectModule>& effect) = 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;
@@ -65,11 +65,11 @@ public:
    virtual wp<EffectChain> chain() const = 0;
};

// EffectModule and EffectChain classes both have their own mutex to protect
// 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
@@ -80,23 +80,19 @@ public:
// 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(const sp<EffectCallbackInterface>& chain,
    EffectBase(const sp<EffectCallbackInterface>& callback,
               effect_descriptor_t *desc,
               int id,
               audio_session_t sessionId,
               bool pinned);
    virtual ~EffectModule();

    ~EffectBase() override = default;

    enum effect_state {
        IDLE,
@@ -109,29 +105,125 @@ public:
    };

    int id() const { return mId; }
    effect_state state() const {
        return mState;
    }
    audio_session_t sessionId() const {
        return mSessionId;
    }
    const effect_descriptor_t& desc() const { return mDescriptor; }
    bool             isOffloadable() const
                        { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
    bool             isImplementationSoftware() const
                        { return (mDescriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0; }
    bool             isProcessImplemented() const
                        { return (mDescriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0; }
    bool             isVolumeControl() const
                        { return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK)
                            == EFFECT_FLAG_VOLUME_CTRL; }
    bool             isVolumeMonitor() const
                        { return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK)
                            == EFFECT_FLAG_VOLUME_MONITOR; }

    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 = 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
    bool                      mSuspended; // effect is suspended: temporarily disabled by framework

    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);
                     void *pReplyData) override;

    void reset_l();
    status_t configure();
    status_t init();
    effect_state state() const {
        return mState;
    }

    uint32_t status() {
        return mStatus;
    }
    audio_session_t sessionId() const {
        return mSessionId;
    }
    status_t    setEnabled(bool enabled, bool fromHandle);
    status_t    setEnabled_l(bool enabled);
    bool isEnabled() const;

    bool isProcessEnabled() const;
    bool isOffloadedOrDirect() const;
    bool isVolumeControlEnabled() const;
@@ -144,15 +236,8 @@ public:
    int16_t     *outBuffer() const {
        return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
    }
    void        setCallback(const sp<EffectCallbackInterface>& callback) { mCallback = callback; }

    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; }
    sp<EffectCallbackInterface>&     callback() { return mCallback; }
    ssize_t removeHandle_l(EffectHandle *handle) override;

    status_t         setDevices(const AudioDeviceTypeAddrVector &devices);
    status_t         setInputDevice(const AudioDeviceTypeAddr &device);
@@ -161,41 +246,18 @@ public:
    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
                        { return (mDescriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0; }
    bool             isProcessImplemented() const
                        { return (mDescriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0; }
    bool             isVolumeControl() const
                        { return (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK)
                            == EFFECT_FLAG_VOLUME_CTRL; }
    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();

    status_t         updatePolicyState();
    void             checkSuspendOnEffectEnabled(bool enabled, bool threadLocked);
    sp<EffectModule> asEffectModule() override { return this; }

    void             dump(int fd, const Vector<String16>& args);

private:
    friend class AudioFlinger;      // for mHandles
    bool                mPinned;

    // Maximum time allocated to effect engines to complete the turn off sequence
    static const uint32_t MAX_DISABLE_TIME_MS = 10000;
@@ -207,23 +269,15 @@ private:
    status_t removeEffectFromHal_l();
    status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);

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_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

#ifdef FLOAT_EFFECT_CHAIN
@@ -251,16 +305,6 @@ mutable Mutex mLock; // mutex for process, commands and handl
    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
@@ -272,7 +316,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
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);
@@ -310,9 +354,9 @@ public:
    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;
        }
@@ -329,7 +373,7 @@ private:
    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
@@ -472,7 +516,7 @@ private:
        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<EffectModule>& effect) override;
        bool updateOrphanEffectChains(const sp<EffectBase>& effect) override;

        audio_io_handle_t io() const override;
        bool isOutput() const override;
@@ -492,13 +536,13 @@ private:
        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<EffectModule>& effect,
        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<EffectModule>& effect) override;
        void onEffectDisable(const sp<EffectModule>& effect) override;
        void onEffectEnable(const sp<EffectBase>& effect) override;
        void onEffectDisable(const sp<EffectBase>& effect) override;

        wp<EffectChain> chain() const override { return mChain; }

+3 −3
Original line number Diff line number Diff line
@@ -1419,11 +1419,11 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
    sp<EffectModule> effect;
    {
        Mutex::Autolock _l(mLock);

        effect = handle->effect().promote();
        if (effect == nullptr) {
        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) {