AIDL effect: Add Equalizer parameters implementation and vts

Bug: 238913361
Test: atest VtsHalAudioEffectTargetTest
atest VtsHalAudioEffectFactoryTargetTest
atest VtsHalEqualizerTargetTest

Change-Id: I94b2283ca2aa0e45715e1c9ac3ea6ad809ec2a2c
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
new file mode 100644
index 0000000..36492ec
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <cstdint>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <fmq/AidlMessageQueue.h>
+
+namespace aidl::android::hardware::audio::effect {
+
+class EffectContext {
+  public:
+    typedef ::android::AidlMessageQueue<
+            IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            StatusMQ;
+    typedef ::android::AidlMessageQueue<
+            int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
+            DataMQ;
+
+    EffectContext(size_t statusDepth, size_t inBufferSize, size_t outBufferSize) {
+        mStatusMQ = std::make_shared<StatusMQ>(statusDepth, true /*configureEventFlagWord*/);
+        mInputMQ = std::make_shared<DataMQ>(inBufferSize);
+        mOutputMQ = std::make_shared<DataMQ>(outBufferSize);
+
+        if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) {
+            LOG(ERROR) << __func__ << " created invalid FMQ";
+        }
+        mWorkBuffer.reserve(std::max(inBufferSize, outBufferSize));
+    };
+
+    std::shared_ptr<StatusMQ> getStatusFmq() { return mStatusMQ; };
+    std::shared_ptr<DataMQ> getInputDataFmq() { return mInputMQ; };
+    std::shared_ptr<DataMQ> getOutputDataFmq() { return mOutputMQ; };
+
+    int8_t* getWorkBuffer() { return static_cast<int8_t*>(mWorkBuffer.data()); };
+    // TODO: update with actual available size
+    size_t availableToRead() { return mWorkBuffer.capacity(); };
+    size_t availableToWrite() { return mWorkBuffer.capacity(); };
+
+  private:
+    std::shared_ptr<StatusMQ> mStatusMQ;
+    std::shared_ptr<DataMQ> mInputMQ;
+    std::shared_ptr<DataMQ> mOutputMQ;
+    // TODO handle effect process input and output
+    // work buffer set by effect instances, the access and update are in same thread
+    std::vector<int8_t> mWorkBuffer;
+};
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h
index e831cea..09a0000 100644
--- a/audio/aidl/default/include/effect-impl/EffectThread.h
+++ b/audio/aidl/default/include/effect-impl/EffectThread.h
@@ -22,12 +22,10 @@
 #include <android-base/thread_annotations.h>
 #include <system/thread_defs.h>
 
+#include "effect-impl/EffectTypes.h"
+
 namespace aidl::android::hardware::audio::effect {
 
-enum class RetCode { SUCCESS, ERROR };
-
-std::string toString(RetCode& code);
-
 class EffectThread {
   public:
     // default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO
@@ -35,10 +33,11 @@
     virtual ~EffectThread();
 
     // called by effect implementation.
-    RetCode create(const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
-    RetCode destroy();
-    RetCode start();
-    RetCode stop();
+    RetCode createThread(const std::string& name,
+                         const int priority = ANDROID_PRIORITY_URGENT_AUDIO);
+    RetCode destroyThread();
+    RetCode startThread();
+    RetCode stopThread();
 
     // Will call process() in a loop if the thread is running.
     void threadLoop();
diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h
new file mode 100644
index 0000000..46cfc0c
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectTypes.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <ostream>
+#include <string>
+
+namespace aidl::android::hardware::audio::effect {
+
+enum class RetCode {
+    SUCCESS,
+    ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */
+    ERROR_THREAD,            /* Effect thread error */
+    ERROR_NULL_POINTER,      /* NULL pointer */
+    ERROR_ALIGNMENT_ERROR,   /* Memory alignment error */
+    ERROR_BLOCK_SIZE_EXCEED  /* Maximum block size exceeded */
+};
+
+inline std::ostream& operator<<(std::ostream& out, const RetCode& code) {
+    switch (code) {
+        case RetCode::SUCCESS:
+            return out << "SUCCESS";
+        case RetCode::ERROR_ILLEGAL_PARAMETER:
+            return out << "ERROR_ILLEGAL_PARAMETER";
+        case RetCode::ERROR_THREAD:
+            return out << "ERROR_THREAD";
+        case RetCode::ERROR_NULL_POINTER:
+            return out << "ERROR_NULL_POINTER";
+        case RetCode::ERROR_ALIGNMENT_ERROR:
+            return out << "ERROR_ALIGNMENT_ERROR";
+        case RetCode::ERROR_BLOCK_SIZE_EXCEED:
+            return out << "ERROR_BLOCK_SIZE_EXCEED";
+    }
+
+    return out << "EnumError: " << code;
+}
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 99f6c24..48b7137 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -21,6 +21,17 @@
 
 using ::aidl::android::media::audio::common::AudioUuid;
 
+// Null UUID
+static const AudioUuid EffectNullUuid = {static_cast<int32_t>(0xec7178ec),
+                                         0xe5e1,
+                                         0x4432,
+                                         0xa3f4,
+                                         {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}};
+
+// Zero UUID
+static const AudioUuid EffectZeroUuid = {
+        static_cast<int32_t>(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
+
 // Equalizer type UUID.
 static const AudioUuid EqualizerTypeUUID = {static_cast<int32_t>(0x0bed4300),
                                             0xddd6,
diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h
new file mode 100644
index 0000000..0fe69ff
--- /dev/null
+++ b/audio/aidl/default/include/effect-impl/EffectWorker.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "EffectContext.h"
+#include "EffectThread.h"
+
+namespace aidl::android::hardware::audio::effect {
+
+std::string toString(RetCode& code);
+
+class EffectWorker : public EffectThread {
+  public:
+    // set effect context for worker, suppose to only happen once here
+    void setContext(std::shared_ptr<EffectContext> context) {
+        std::call_once(mOnceFlag, [&]() { mContext = context; });
+    };
+
+    // handle FMQ and call effect implemented virtual function
+    void process() override {
+        if (!mContext) {
+            LOG(ERROR) << __func__ << " invalid context!";
+            return;
+        }
+        std::shared_ptr<EffectContext::StatusMQ> statusMQ = mContext->getStatusFmq();
+        std::shared_ptr<EffectContext::DataMQ> inputMQ = mContext->getInputDataFmq();
+        std::shared_ptr<EffectContext::DataMQ> outputMQ = mContext->getOutputDataFmq();
+
+        // Only this worker will read from input data MQ and write to output data MQ.
+        auto readSize = inputMQ->availableToRead(), writeSize = outputMQ->availableToWrite();
+        if (readSize && writeSize) {
+            LOG(DEBUG) << __func__ << " available to read " << readSize << " available to write "
+                       << writeSize;
+            auto buffer = mContext->getWorkBuffer();
+            inputMQ->read(buffer, readSize);
+            IEffect::Status status = effectProcessImpl();
+            writeSize = std::min((int32_t)writeSize, status.fmqByteProduced);
+            outputMQ->write(buffer, writeSize);
+            statusMQ->writeBlocking(&status, 1);
+            LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqByteConsumed
+                       << " produced " << status.fmqByteProduced;
+        } else {
+            // TODO: maybe add some sleep here to avoid busy waiting
+        }
+    }
+
+    // must implement by each effect implementation
+    virtual IEffect::Status effectProcessImpl() = 0;
+
+  private:
+    // make sure the context only set once.
+    std::once_flag mOnceFlag;
+    std::shared_ptr<EffectContext> mContext;
+};
+
+}  // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
index 58ad1de..aa3a727 100644
--- a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
+++ b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h
@@ -21,20 +21,29 @@
 #include <cstdlib>
 #include <memory>
 
-#include "effect-impl/EffectThread.h"
+#include "effect-impl/EffectContext.h"
+#include "effect-impl/EffectTypes.h"
+#include "effect-impl/EffectUUID.h"
+#include "effect-impl/EffectWorker.h"
 
 namespace aidl::android::hardware::audio::effect {
 
-class EqualizerSwWorker : public EffectThread {
-    // EqualizerSwWorker(const std::string name){EffectThread(name)};
-    void process() override;
+class EqualizerSwContext : public EffectContext {
+  public:
+    EqualizerSwContext(int statusDepth, int inBufferSize, int outBufferSize)
+        : EffectContext(statusDepth, inBufferSize, outBufferSize) {
+        LOG(DEBUG) << __func__;
+    }
+
+  private:
+    // Add equalizer specific context for processing here
 };
 
-class EqualizerSw : public BnEffect {
+class EqualizerSw : public BnEffect, EffectWorker {
   public:
     EqualizerSw() {
-        // create the worker
-        mWorker = std::make_unique<EqualizerSwWorker>();
+        Equalizer::Capability eqCap = {.bandFrequencies = mBandFrequency, .presets = mPresets};
+        mDesc.capability.set<Capability::equalizer>(eqCap);
         LOG(DEBUG) << __func__;
     };
     ~EqualizerSw() {
@@ -52,12 +61,11 @@
     ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId,
                                     Parameter* _aidl_return) override;
 
+    IEffect::Status effectProcessImpl() override;
+
   private:
-    // effect processing thread.
-    std::unique_ptr<EqualizerSwWorker> mWorker;
     // Effect descriptor.
-    const Descriptor mDesc = {
-            .common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
+    Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}};
 
     // Parameters.
     Parameter::Common mCommonParam;
@@ -66,21 +74,32 @@
     // Instance state INIT by default.
     State mState = State::INIT;
 
-    typedef ::android::AidlMessageQueue<
-            Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            StatusMQ;
-    typedef ::android::AidlMessageQueue<
-            int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            DataMQ;
+    int mPreset = PRESET_CUSTOM;  // the current preset
+    const std::vector<Equalizer::BandFrequency> mBandFrequency = {{0, 30000, 120000},
+                                                                  {1, 120001, 460000},
+                                                                  {2, 460001, 1800000},
+                                                                  {3, 1800001, 7000000},
+                                                                  {4, 7000001, 20000000}};
+    // preset band level
+    std::vector<Equalizer::BandLevel> mBandLevels = {{0, 3}, {1, 0}, {2, 0}, {3, 0}, {4, 3}};
+    // presets supported by the device
+    const std::vector<Equalizer::Preset> mPresets = {
+            {0, "Normal"},      {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"},
+            {5, "Heavy Metal"}, {6, "Hip Hop"},   {7, "Jazz"},  {8, "Pop"},  {9, "Rock"}};
+    static const int NUM_OF_BANDS = 5;
+    static const int NUM_OF_PRESETS = 10;
+    static const int PRESET_CUSTOM = -1;
 
-    std::unique_ptr<StatusMQ> mStatusMQ;
-    std::unique_ptr<DataMQ> mInputMQ;
-    std::unique_ptr<DataMQ> mOutputMQ;
+    // Equalizer worker context
+    std::shared_ptr<EqualizerSwContext> mContext;
 
     ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param);
     ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific);
-    bool createFmq(int statusDepth, int inBufferSize, int outBufferSize, OpenEffectReturn* ret);
-    void destroyFmq();
+    ndk::ScopedAStatus getSpecificParameter(Parameter::Specific::Id id,
+                                            Parameter::Specific* specific);
+
     void cleanUp();
+
+    IEffect::Status status(binder_status_t status, size_t consumed, size_t produced);
 };
 }  // namespace aidl::android::hardware::audio::effect