Merge "Transcoding: Log transcoder level metrics." into sc-dev
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index f33f5f1..ba801e7 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -338,7 +338,7 @@
 }
 
 std::vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
-    std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
+    static std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
     if (factories.size() == 0) {
         // must be in passthrough mode, load the default passthrough service
         auto passthrough = IDrmFactory::getService();
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
index 9b125af..ed3848d 100644
--- a/drm/libmediadrm/DrmUtils.cpp
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -43,6 +43,9 @@
 #include <mediadrm/ICrypto.h>
 #include <mediadrm/IDrm.h>
 
+#include <map>
+#include <string>
+
 using HServiceManager = ::android::hidl::manager::V1_2::IServiceManager;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -66,8 +69,8 @@
     return obj;
 }
 
-template <typename Hal, typename V>
-void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+template <typename Hal, typename V, typename M>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories, M& instances) {
     sp<HServiceManager> serviceManager = HServiceManager::getService();
     if (serviceManager == nullptr) {
         LOG2BE("Failed to get service manager");
@@ -78,7 +81,7 @@
         for (const auto &instance : registered) {
             auto factory = Hal::getService(instance);
             if (factory != nullptr) {
-                LOG2BI("found %s %s", Hal::descriptor, instance.c_str());
+                instances[instance.c_str()] = Hal::descriptor;
                 if (!uuid || factory->isCryptoSchemeSupported(uuid)) {
                     factories.push_back(factory);
                 }
@@ -87,6 +90,12 @@
     });
 }
 
+template <typename Hal, typename V>
+void MakeHidlFactories(const uint8_t uuid[16], V &factories) {
+    std::map<std::string, std::string> instances;
+    MakeHidlFactories<Hal>(uuid, factories, instances);
+}
+
 hidl_vec<uint8_t> toHidlVec(const void *ptr, size_t size) {
     hidl_vec<uint8_t> vec(size);
     if (ptr != nullptr) {
@@ -147,11 +156,15 @@
 
 std::vector<sp<::V1_0::IDrmFactory>> MakeDrmFactories(const uint8_t uuid[16]) {
     std::vector<sp<::V1_0::IDrmFactory>> drmFactories;
-    MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories);
-    MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories);
+    std::map<std::string, std::string> instances;
+    MakeHidlFactories<::V1_0::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_1::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_2::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_3::IDrmFactory>(uuid, drmFactories, instances);
+    MakeHidlFactories<::V1_4::IDrmFactory>(uuid, drmFactories, instances);
+    for (auto const& entry : instances) {
+        LOG2BI("found instance=%s version=%s", entry.first.c_str(), entry.second.c_str());
+    }
     return drmFactories;
 }
 
@@ -255,6 +268,8 @@
         return ERROR_DRM_PROVISIONING_CONFIG;
     case ::V1_4::Status::PROVISIONING_PARSE_ERROR:
         return ERROR_DRM_PROVISIONING_PARSE;
+    case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED:
+        return ERROR_DRM_PROVISIONING_REQUEST_REJECTED;
     case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR:
         return ERROR_DRM_PROVISIONING_RETRY;
     case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR:
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index cfaeb66..43b2c14 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -186,6 +186,23 @@
                 .build());
 
         addParameter(
+                DefineParam(mQuantization, C2_PARAMKEY_QUANTIZATION)
+                .withDefault(new C2StreamQuantizationInfo::output(0u,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN,
+                                      DEFAULT_QP_MAX, DEFAULT_QP_MIN))
+                .withFields({
+                        C2F(mQuantization, iMax).inRange(1, 51),
+                        C2F(mQuantization, iMin).inRange(1, 51),
+                        C2F(mQuantization, pMax).inRange(1, 51),
+                        C2F(mQuantization, pMin).inRange(1, 51),
+                        C2F(mQuantization, bMax).inRange(1, 51),
+                        C2F(mQuantization, bMin).inRange(1, 51),
+                 })
+                .withSetter(QuantizationSetter)
+                .build());
+
+        addParameter(
                 DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
                 .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
                 .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
@@ -220,6 +237,71 @@
         return res;
     }
 
+    static C2R QuantizationSetter(bool mayBlock, C2P<C2StreamQuantizationInfo::output> &me) {
+        (void)mayBlock;
+        (void)me;
+        C2R res = C2R::Ok();
+
+        ALOGV("QuantizationSetter enters max/min i %d/%d p %d/%d b %d/%d",
+              me.v.iMax, me.v.iMin, me.v.pMax, me.v.pMin, me.v.bMax, me.v.bMin);
+
+        // bounds checking
+        constexpr int qp_lowest = 1;
+        constexpr int qp_highest = 51;
+
+        if (me.v.iMax < qp_lowest) {
+            me.set().iMax = qp_lowest;
+        } else if (me.v.iMax > qp_highest) {
+            me.set().iMax = qp_highest;
+        }
+
+        if (me.v.iMin < qp_lowest) {
+            me.set().iMin = qp_lowest;
+        } else if (me.v.iMin > qp_highest) {
+            me.set().iMin = qp_highest;
+        }
+
+        if (me.v.pMax < qp_lowest) {
+            me.set().pMax = qp_lowest;
+        } else if (me.v.pMax > qp_highest) {
+            me.set().pMax = qp_highest;
+        }
+
+        if (me.v.pMin < qp_lowest) {
+            me.set().pMin = qp_lowest;
+        } else if (me.v.pMin > qp_highest) {
+            me.set().pMin = qp_highest;
+        }
+
+        if (me.v.bMax < qp_lowest) {
+            me.set().bMax = qp_lowest;
+        } else if (me.v.bMax > qp_highest) {
+            me.set().bMax = qp_highest;
+        }
+
+        if (me.v.bMin < qp_lowest) {
+            me.set().bMin = qp_lowest;
+        } else if (me.v.bMin > qp_highest) {
+            me.set().bMin = qp_highest;
+        }
+
+        // consistency checking, e.g. min<max
+        //
+        if (me.v.iMax < me.v.iMin) {
+            me.set().iMax = me.v.iMin;
+        }
+        if (me.v.pMax < me.v.pMin) {
+            me.set().pMax = me.v.pMin;
+        }
+        if (me.v.bMax < me.v.bMin) {
+            me.set().bMax = me.v.bMin;
+        }
+
+        // TODO: enforce any sort of i_max < p_max < b_max?
+
+        return res;
+    }
+
     static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
                           C2P<C2StreamPictureSizeInfo::input> &me) {
         (void)mayBlock;
@@ -393,6 +475,7 @@
     std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
     std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; }
+    std::shared_ptr<C2StreamQuantizationInfo::output> getQuantization_l() const { return mQuantization; }
 
 private:
     std::shared_ptr<C2StreamUsageTuning::input> mUsage;
@@ -404,6 +487,7 @@
     std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
     std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
     std::shared_ptr<C2StreamGopTuning::output> mGop;
+    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
 };
 
 #define ive_api_function  ih264e_api_function
@@ -664,6 +748,7 @@
     ive_ctl_set_qp_op_t s_qp_op;
     IV_STATUS_T status;
 
+    // set the defaults
     s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
     s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
 
@@ -679,6 +764,21 @@
     s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
     s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
 
+    // parameter parsing ensured proper range 1..51, so only worry about ordering
+    bool valid = true;
+    if (mQuantization->iMax < mQuantization->iMin) valid = false;
+    if (mQuantization->pMax < mQuantization->pMin) valid = false;
+    if (mQuantization->bMax < mQuantization->bMin) valid = false;
+
+    if (valid) {
+        s_qp_ip.u4_i_qp_max = mQuantization->iMax;
+        s_qp_ip.u4_i_qp_min = mQuantization->iMin;
+        s_qp_ip.u4_p_qp_max = mQuantization->pMax;
+        s_qp_ip.u4_p_qp_min = mQuantization->pMin;
+        s_qp_ip.u4_b_qp_max = mQuantization->bMax;
+        s_qp_ip.u4_b_qp_min = mQuantization->bMin;
+    }
+
     s_qp_ip.u4_timestamp_high = -1;
     s_qp_ip.u4_timestamp_low = -1;
 
@@ -926,6 +1026,7 @@
         mIInterval = mIntf->getSyncFramePeriod_l();
         mIDRInterval = mIntf->getSyncFramePeriod_l();
         gop = mIntf->getGop_l();
+        mQuantization = mIntf->getQuantization_l();
     }
     if (gop && gop->flexCount() > 0) {
         uint32_t syncInterval = 1;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 555055b..e4bf0b0 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -192,6 +192,7 @@
     std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
     std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
     std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+    std::shared_ptr<C2StreamQuantizationInfo::output> mQuantization;
 
     uint32_t mOutBufferSize;
     UWORD32 mHeaderGenerated;
diff --git a/media/codec2/components/mpeg4_h263/TEST_MAPPING b/media/codec2/components/mpeg4_h263/TEST_MAPPING
new file mode 100644
index 0000000..93fba22
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/TEST_MAPPING
@@ -0,0 +1,6 @@
+// mappings for frameworks/av/media/codec2/components/mpeg4_h263
+{
+  "presubmit": [
+    { "name": "C2SoftMpeg4DecTest" }
+  ]
+}
diff --git a/media/codec2/components/tests/Android.bp b/media/codec2/components/tests/Android.bp
new file mode 100644
index 0000000..3c68eee
--- /dev/null
+++ b/media/codec2/components/tests/Android.bp
@@ -0,0 +1,68 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_av_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+    name: "C2SoftCodecTest-defaults",
+    gtest: true,
+    host_supported: false,
+    srcs: [
+        "C2SoftCodecTest.cpp",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libion",
+        "libfmq",
+        "libbase",
+        "libutils",
+        "libcutils",
+        "libcodec2",
+        "libhidlbase",
+        "libdmabufheap",
+        "libcodec2_vndk",
+        "libnativewindow",
+        "libcodec2_soft_common",
+        "libsfplugin_ccodec_utils",
+        "libstagefright_foundation",
+        "libstagefright_bufferpool@2.0.1",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.media.bufferpool@2.0",
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+    ],
+
+    shared_libs: [
+        "libui",
+        "libdl",
+        "libhardware",
+        "libvndksupport",
+        "libprocessgroup",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "C2SoftMpeg4DecTest",
+    defaults: ["C2SoftCodecTest-defaults"],
+
+    static_libs: [
+        "libstagefright_m4vh263dec",
+        "libcodec2_soft_mpeg4dec",
+    ],
+
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/media/codec2/components/tests/C2SoftCodecTest.cpp b/media/codec2/components/tests/C2SoftCodecTest.cpp
new file mode 100644
index 0000000..84c2562
--- /dev/null
+++ b/media/codec2/components/tests/C2SoftCodecTest.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2021 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <C2Config.h>
+#include <C2ComponentFactory.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android;
+extern "C" ::C2ComponentFactory* CreateCodec2Factory();
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory);
+
+class C2SoftCodecTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    mFactory = CreateCodec2Factory();
+  }
+
+  void TearDown() override {
+    if (mFactory) {
+      DestroyCodec2Factory(mFactory);
+    }
+  }
+
+  c2_status_t createComponent(
+        std::shared_ptr<C2Component>* const comp) {
+    if (!mFactory) {
+      return C2_NO_INIT;
+    }
+    return mFactory->createComponent(
+        kPlaceholderId, comp, std::default_delete<C2Component>());
+  }
+
+  c2_status_t createInterface(
+      std::shared_ptr<C2ComponentInterface>* const intf) {
+    if (!mFactory) {
+      return C2_NO_INIT;
+    }
+    return mFactory->createInterface(
+        kPlaceholderId, intf, std::default_delete<C2ComponentInterface>());
+  }
+
+  ::C2ComponentFactory *getFactory() { return mFactory; }
+
+private:
+  static constexpr ::c2_node_id_t kPlaceholderId = 0;
+
+  ::C2ComponentFactory *mFactory;
+};
+
+TEST_F(C2SoftCodecTest, PictureSizeInfoTest) {
+  std::shared_ptr<C2ComponentInterface> interface;
+  c2_status_t status = createInterface(&interface);
+  ASSERT_EQ(status, C2_OK) << "Error in createInterface";
+  ASSERT_NE(interface, nullptr) << "interface is null";
+
+  std::unique_ptr<C2StreamPictureSizeInfo::output> param =
+      std::make_unique<C2StreamPictureSizeInfo::output>();
+  std::vector<C2FieldSupportedValuesQuery> validValueInfos = {
+      C2FieldSupportedValuesQuery::Current(
+          C2ParamField(param.get(), &C2StreamPictureSizeInfo::width)),
+      C2FieldSupportedValuesQuery::Current(
+          C2ParamField(param.get(), &C2StreamPictureSizeInfo::height))};
+  status = interface->querySupportedValues_vb(validValueInfos, C2_MAY_BLOCK);
+  ASSERT_EQ(status, C2_OK) << "Error in querySupportedValues_vb";
+  ASSERT_EQ(validValueInfos.size(), 2) << "querySupportedValues_vb didn't return 2 values";
+
+  ASSERT_EQ(validValueInfos[0].values.range.max.ref<uint32_t>(), 1920)
+      << "Incorrect maximum value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.max.ref<uint32_t>(), 1920)
+      << "Incorrect maximum value for height";
+  ASSERT_EQ(validValueInfos[0].values.range.min.ref<uint32_t>(), 2)
+      << "Incorrect minimum value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.min.ref<uint32_t>(), 2)
+      << "Incorrect minimum value for height";
+  ASSERT_EQ(validValueInfos[0].values.range.step.ref<uint32_t>(), 2)
+      << "Incorrect alignment value for width";
+  ASSERT_EQ(validValueInfos[1].values.range.step.ref<uint32_t>(), 2)
+      << "Incorrect alignment value for height";
+
+  return;
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  ALOGV("Test result = %d\n", status);
+  return status;
+}
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 752140a..8e8a08b 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -187,6 +187,8 @@
     kParamIndexPictureType,
     kParamIndexHdr10PlusMetadata,
 
+    kParamIndexQuantization,
+
     /* ------------------------------------ video components ------------------------------------ */
 
     kParamIndexFrameRate = C2_PARAM_INDEX_VIDEO_PARAM_START,
@@ -708,6 +710,38 @@
         C2StreamProfileLevelInfo;
 constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl";
 
+struct C2QuantizationStruct {
+    int32_t iMax;  ///< max/min for I frames
+    int32_t iMin;
+    int32_t pMax;  ///< max/min for P frames
+    int32_t pMin;
+    int32_t bMax;  ///< max/min for B frames
+    int32_t bMin;
+
+    C2QuantizationStruct(
+            int32_t iMax_ = INT32_MAX,
+            int32_t iMin_ = INT32_MIN,
+            int32_t pMax_ = INT32_MAX,
+            int32_t pMin_ = INT32_MIN,
+            int32_t bMax_ = INT32_MAX,
+            int32_t bMin_ = INT32_MIN)
+        : iMax(iMax_), iMin(iMin_),
+          pMax(pMax_), pMin(pMin_),
+          bMax(bMax_), bMin(bMin_) { }
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(Quantization)          // reference?
+    C2FIELD(iMax, "i-max")
+    C2FIELD(iMin, "i-min")
+    C2FIELD(pMax, "p-max")
+    C2FIELD(pMin, "p-min")
+    C2FIELD(bMax, "b-max")
+    C2FIELD(bMin, "b-min")
+};
+
+typedef C2StreamParam<C2Info, C2QuantizationStruct, kParamIndexQuantization>
+        C2StreamQuantizationInfo;
+constexpr char C2_PARAMKEY_QUANTIZATION[] = "coded.qp";
+
 /**
  * Codec-specific initialization data.
  *
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index d3814fb..2190f46 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -727,6 +727,19 @@
             return C2Value();
         }));
 
+    add(ConfigMapper(KEY_VIDEO_QP_I_MAX, C2_PARAMKEY_QUANTIZATION, "i-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_I_MIN, C2_PARAMKEY_QUANTIZATION, "i-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_P_MAX, C2_PARAMKEY_QUANTIZATION, "p-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_P_MIN, C2_PARAMKEY_QUANTIZATION, "p-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_B_MAX, C2_PARAMKEY_QUANTIZATION, "b-max")
+        .limitTo(D::VIDEO & D::ENCODER));
+    add(ConfigMapper(KEY_VIDEO_QP_B_MIN, C2_PARAMKEY_QUANTIZATION, "b-min")
+        .limitTo(D::VIDEO & D::ENCODER));
+
     // convert to dBFS and add default
     add(ConfigMapper(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL, "value")
         .limitTo(D::AUDIO & D::DECODER & (D::CONFIG | D::PARAM | D::READ))
diff --git a/media/janitors/OWNERS-codecs b/media/janitors/OWNERS-codecs
deleted file mode 100644
index e201399..0000000
--- a/media/janitors/OWNERS-codecs
+++ /dev/null
@@ -1,5 +0,0 @@
-# gerrit owner/approvers for the actual software codec libraries
-# differentiated from plugins connecting those codecs to either omx or codec2 infrastructure
-essick@google.com
-lajos@google.com
-marcone@google.com
diff --git a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
index 761c6ce..bbe7de0 100644
--- a/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
+++ b/media/libeffects/lvm/lib/Bass/src/LVDBE_Init.cpp
@@ -57,7 +57,7 @@
      * Create the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVDBE_Instance_t;
+        *phInstance = new LVDBE_Instance_t{};
     }
     pInstance = (LVDBE_Instance_t*)*phInstance;
 
diff --git a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
index b092970..6ea08bc 100644
--- a/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
+++ b/media/libeffects/lvm/lib/Bundle/src/LVM_Init.cpp
@@ -93,7 +93,7 @@
     /*
      * Create the instance handle
      */
-    *phInstance = new LVM_Instance_t;
+    *phInstance = new LVM_Instance_t{};
     pInstance = (LVM_Instance_t*)*phInstance;
 
     pInstance->InstParams = *pInstParams;
diff --git a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
index 37e6d4d..8cb7013 100644
--- a/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
+++ b/media/libeffects/lvm/lib/Eq/src/LVEQNB_Init.cpp
@@ -52,7 +52,7 @@
                                    LVEQNB_Capabilities_t* pCapabilities, void* pScratch) {
     LVEQNB_Instance_t* pInstance;
 
-    *phInstance = new LVEQNB_Instance_t;
+    *phInstance = new LVEQNB_Instance_t{};
     pInstance = (LVEQNB_Instance_t*)*phInstance;
 
     pInstance->Capabilities = *pCapabilities;
diff --git a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
index bf71634..298655b 100644
--- a/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
+++ b/media/libeffects/lvm/lib/Reverb/src/LVREV_GetInstanceHandle.cpp
@@ -114,7 +114,7 @@
      * Set the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVREV_Instance_st;
+        *phInstance = new LVREV_Instance_st{};
     }
     pLVREV_Private = (LVREV_Instance_st*)*phInstance;
     pLVREV_Private->MemoryTable = *pMemoryTable;
diff --git a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
index 5ca8543..5550b9c 100644
--- a/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
+++ b/media/libeffects/lvm/lib/SpectrumAnalyzer/src/LVPSA_Init.cpp
@@ -49,7 +49,7 @@
     LVM_UINT32 BufferLength = 0;
 
     /* Set the instance handle if not already initialised */
-    *phInstance = new LVPSA_InstancePr_t;
+    *phInstance = new LVPSA_InstancePr_t{};
     pLVPSA_Inst = (LVPSA_InstancePr_t*)*phInstance;
 
     pLVPSA_Inst->pScratch = pScratch;
diff --git a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
index d60b360..dd1baf3 100644
--- a/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
+++ b/media/libeffects/lvm/lib/StereoWidening/src/LVCS_Init.cpp
@@ -55,7 +55,7 @@
      * Create the instance handle if not already initialised
      */
     if (*phInstance == LVM_NULL) {
-        *phInstance = new LVCS_Instance_t;
+        *phInstance = new LVCS_Instance_t{};
     }
     pInstance = (LVCS_Instance_t*)*phInstance;
 
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 639af4d..9939ed1 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -10,12 +10,42 @@
 }
 
 cc_test {
+    name: "EffectReverbTest",
+    vendor: true,
+    gtest: true,
+    host_supported: true,
+    srcs: [
+        "EffectReverbTest.cpp",
+        "EffectTestHelper.cpp",
+    ],
+    include_dirs: [
+        "frameworks/av/media/libeffects/lvm/lib/Common/lib",
+        "frameworks/av/media/libeffects/lvm/wrapper/Reverb",
+    ],
+    static_libs: [
+        "libaudioutils",
+        "libreverb",
+        "libreverbwrapper",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    header_libs: [
+        "libaudioeffects",
+        "libhardware_headers",
+    ],
+}
+
+cc_test {
     name: "EffectBundleTest",
     vendor: true,
     gtest: true,
     host_supported: true,
     test_suites: ["device-tests"],
-    srcs: ["EffectBundleTest.cpp"],
+    srcs: [
+        "EffectBundleTest.cpp",
+        "EffectTestHelper.cpp",
+    ],
     static_libs: [
         "libaudioutils",
         "libbundlewrapper",
diff --git a/media/libeffects/lvm/tests/EffectBundleTest.cpp b/media/libeffects/lvm/tests/EffectBundleTest.cpp
index aae09de..881ffb1 100644
--- a/media/libeffects/lvm/tests/EffectBundleTest.cpp
+++ b/media/libeffects/lvm/tests/EffectBundleTest.cpp
@@ -14,22 +14,8 @@
  * limitations under the License.
  */
 
-#include <array>
-#include <audio_utils/channels.h>
-#include <audio_utils/primitives.h>
-#include <climits>
-#include <cstdlib>
-#include <gtest/gtest.h>
-#include <hardware/audio_effect.h>
-#include <log/log.h>
-#include <random>
-#include <system/audio.h>
-#include <vector>
-
-extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
-
-// Corresponds to SNR for 1 bit difference between two int16_t signals
-constexpr float kSNRThreshold = 90.308998;
+#include "EffectTestHelper.h"
+using namespace android;
 
 // Update isBassBoost, if the order of effects is updated
 constexpr effect_uuid_t kEffectUuids[] = {
@@ -50,120 +36,15 @@
 
 constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
 
-constexpr audio_channel_mask_t kChMasks[] = {
-        AUDIO_CHANNEL_OUT_MONO,          AUDIO_CHANNEL_OUT_STEREO,
-        AUDIO_CHANNEL_OUT_2POINT1,       AUDIO_CHANNEL_OUT_2POINT0POINT2,
-        AUDIO_CHANNEL_OUT_QUAD,          AUDIO_CHANNEL_OUT_QUAD_BACK,
-        AUDIO_CHANNEL_OUT_QUAD_SIDE,     AUDIO_CHANNEL_OUT_SURROUND,
-        AUDIO_CHANNEL_INDEX_MASK_4,      AUDIO_CHANNEL_OUT_2POINT1POINT2,
-        AUDIO_CHANNEL_OUT_3POINT0POINT2, AUDIO_CHANNEL_OUT_PENTA,
-        AUDIO_CHANNEL_INDEX_MASK_5,      AUDIO_CHANNEL_OUT_3POINT1POINT2,
-        AUDIO_CHANNEL_OUT_5POINT1,       AUDIO_CHANNEL_OUT_5POINT1_BACK,
-        AUDIO_CHANNEL_OUT_5POINT1_SIDE,  AUDIO_CHANNEL_INDEX_MASK_6,
-        AUDIO_CHANNEL_OUT_6POINT1,       AUDIO_CHANNEL_INDEX_MASK_7,
-        AUDIO_CHANNEL_OUT_5POINT1POINT2, AUDIO_CHANNEL_OUT_7POINT1,
-        AUDIO_CHANNEL_INDEX_MASK_8,      AUDIO_CHANNEL_INDEX_MASK_9,
-        AUDIO_CHANNEL_INDEX_MASK_10,     AUDIO_CHANNEL_INDEX_MASK_11,
-        AUDIO_CHANNEL_INDEX_MASK_12,     AUDIO_CHANNEL_INDEX_MASK_13,
-        AUDIO_CHANNEL_INDEX_MASK_14,     AUDIO_CHANNEL_INDEX_MASK_15,
-        AUDIO_CHANNEL_INDEX_MASK_16,     AUDIO_CHANNEL_INDEX_MASK_17,
-        AUDIO_CHANNEL_INDEX_MASK_18,     AUDIO_CHANNEL_INDEX_MASK_19,
-        AUDIO_CHANNEL_INDEX_MASK_20,     AUDIO_CHANNEL_INDEX_MASK_21,
-        AUDIO_CHANNEL_INDEX_MASK_22,     AUDIO_CHANNEL_INDEX_MASK_23,
-        AUDIO_CHANNEL_INDEX_MASK_24,
-};
-
-constexpr size_t kNumChMasks = std::size(kChMasks);
-
-constexpr size_t kSampleRates[] = {8000,  11025, 12000, 16000, 22050,  24000, 32000,
-                                   44100, 48000, 88200, 96000, 176400, 192000};
-
-constexpr size_t kNumSampleRates = std::size(kSampleRates);
-
-constexpr size_t kFrameCounts[] = {4, 2048};
-
-constexpr size_t kNumFrameCounts = std::size(kFrameCounts);
-
-constexpr size_t kLoopCounts[] = {1, 4};
-
-constexpr size_t kNumLoopCounts = std::size(kLoopCounts);
-
-class EffectBundleHelper {
-  public:
-    EffectBundleHelper(const effect_uuid_t* uuid, size_t chMask, size_t sampleRate,
-                       size_t frameCount, size_t loopCount)
-        : mUuid(uuid),
-          mChMask(chMask),
-          mChannelCount(audio_channel_count_from_out_mask(mChMask)),
-          mSampleRate(sampleRate),
-          mFrameCount(frameCount),
-          mLoopCount(loopCount) {}
-    void createEffect();
-    void releaseEffect();
-    void configEffect();
-    void process(float* input, float* output);
-
-  private:
-    const effect_uuid_t* mUuid;
-    const size_t mChMask;
-    const size_t mChannelCount;
-    const size_t mSampleRate;
-    const size_t mFrameCount;
-    const size_t mLoopCount;
-    effect_handle_t mEffectHandle{};
-};
-
-void EffectBundleHelper::createEffect() {
-    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(mUuid, 1, 1, &mEffectHandle);
-    ASSERT_EQ(status, 0) << "create_effect returned an error " << status << "\n";
-}
-
-void EffectBundleHelper::releaseEffect() {
-    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(mEffectHandle);
-    ASSERT_EQ(status, 0) << "release_effect returned an error " << status << "\n";
-}
-
-void EffectBundleHelper::configEffect() {
-    effect_config_t config{};
-    config.inputCfg.samplingRate = config.outputCfg.samplingRate = mSampleRate;
-    config.inputCfg.channels = config.outputCfg.channels = mChMask;
-    config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
-
-    int reply = 0;
-    uint32_t replySize = sizeof(reply);
-    int status = (*mEffectHandle)
-                         ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
-                                   &config, &replySize, &reply);
-    ASSERT_EQ(status, 0) << "command returned an error " << status << "\n";
-    ASSERT_EQ(reply, 0) << "command reply non zero " << reply << "\n";
-
-    status = (*mEffectHandle)
-                     ->command(mEffectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
-    ASSERT_EQ(status, 0) << "command enable returned an error " << status << "\n";
-    ASSERT_EQ(reply, 0) << "command reply non zero " << reply << "\n";
-}
-
-void EffectBundleHelper::process(float* input, float* output) {
-    audio_buffer_t inBuffer = {.frameCount = mFrameCount, .f32 = input};
-    audio_buffer_t outBuffer = {.frameCount = mFrameCount, .f32 = output};
-    for (size_t i = 0; i < mLoopCount; i++) {
-        int status = (*mEffectHandle)->process(mEffectHandle, &inBuffer, &outBuffer);
-        ASSERT_EQ(status, 0) << "process returned an error " << status << "\n";
-
-        inBuffer.f32 += mFrameCount * mChannelCount;
-        outBuffer.f32 += mFrameCount * mChannelCount;
-    }
-}
-
 typedef std::tuple<int, int, int, int, int> SingleEffectTestParam;
 class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
   public:
     SingleEffectTest()
-        : mChMask(kChMasks[std::get<0>(GetParam())]),
+        : mChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
           mChannelCount(audio_channel_count_from_out_mask(mChMask)),
-          mSampleRate(kSampleRates[std::get<1>(GetParam())]),
-          mFrameCount(kFrameCounts[std::get<2>(GetParam())]),
-          mLoopCount(kLoopCounts[std::get<3>(GetParam())]),
+          mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
           mTotalFrameCount(mFrameCount * mLoopCount),
           mUuid(&kEffectUuids[std::get<4>(GetParam())]) {}
 
@@ -182,10 +63,10 @@
                  << "chMask: " << mChMask << " sampleRate: " << mSampleRate
                  << " frameCount: " << mFrameCount << " loopCount: " << mLoopCount);
 
-    EffectBundleHelper effect(mUuid, mChMask, mSampleRate, mFrameCount, mLoopCount);
+    EffectTestHelper effect(mUuid, mChMask, mChMask, mSampleRate, mFrameCount, mLoopCount);
 
     ASSERT_NO_FATAL_FAILURE(effect.createEffect());
-    ASSERT_NO_FATAL_FAILURE(effect.configEffect());
+    ASSERT_NO_FATAL_FAILURE(effect.setConfig());
 
     // Initialize input buffer with deterministic pseudo-random values
     std::vector<float> input(mTotalFrameCount * mChannelCount);
@@ -199,21 +80,22 @@
     ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
 }
 
-INSTANTIATE_TEST_SUITE_P(EffectBundleTestAll, SingleEffectTest,
-                         ::testing::Combine(::testing::Range(0, (int)kNumChMasks),
-                                            ::testing::Range(0, (int)kNumSampleRates),
-                                            ::testing::Range(0, (int)kNumFrameCounts),
-                                            ::testing::Range(0, (int)kNumLoopCounts),
-                                            ::testing::Range(0, (int)kNumEffectUuids)));
+INSTANTIATE_TEST_SUITE_P(
+        EffectBundleTestAll, SingleEffectTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids)));
 
 typedef std::tuple<int, int, int, int> SingleEffectComparisonTestParam;
 class SingleEffectComparisonTest
     : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
   public:
     SingleEffectComparisonTest()
-        : mSampleRate(kSampleRates[std::get<0>(GetParam())]),
-          mFrameCount(kFrameCounts[std::get<1>(GetParam())]),
-          mLoopCount(kLoopCounts[std::get<2>(GetParam())]),
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
           mTotalFrameCount(mFrameCount * mLoopCount),
           mUuid(&kEffectUuids[std::get<3>(GetParam())]) {}
 
@@ -224,26 +106,6 @@
     const effect_uuid_t* mUuid;
 };
 
-template <typename T>
-float computeSnr(const T* ref, const T* tst, size_t count) {
-    double signal{};
-    double noise{};
-
-    for (size_t i = 0; i < count; ++i) {
-        const double value(ref[i]);
-        const double diff(tst[i] - value);
-        signal += value * value;
-        noise += diff * diff;
-    }
-    // Initialized to a value greater than kSNRThreshold to handle
-    // cases where ref and tst match exactly
-    float snr = kSNRThreshold + 1.0f;
-    if (signal > 0.0f && noise > 0.0f) {
-        snr = 10.f * log(signal / noise);
-    }
-    return snr;
-}
-
 // Compares first two channels in multi-channel output to stereo output when same effect is applied
 TEST_P(SingleEffectComparisonTest, SimpleProcess) {
     SCOPED_TRACE(testing::Message() << " sampleRate: " << mSampleRate << " frameCount: "
@@ -264,11 +126,11 @@
                     mTotalFrameCount * sizeof(float) * FCC_1);
 
     // Apply effect on stereo channels
-    EffectBundleHelper stereoEffect(mUuid, AUDIO_CHANNEL_OUT_STEREO, mSampleRate, mFrameCount,
-                                    mLoopCount);
+    EffectTestHelper stereoEffect(mUuid, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_STEREO,
+                                  mSampleRate, mFrameCount, mLoopCount);
 
     ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
-    ASSERT_NO_FATAL_FAILURE(stereoEffect.configEffect());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
 
     std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
     ASSERT_NO_FATAL_FAILURE(stereoEffect.process(stereoInput.data(), stereoOutput.data()));
@@ -278,12 +140,12 @@
     std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
     memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
 
-    for (size_t chMask : kChMasks) {
+    for (size_t chMask : EffectTestHelper::kChMasks) {
         size_t channelCount = audio_channel_count_from_out_mask(chMask);
-        EffectBundleHelper testEffect(mUuid, chMask, mSampleRate, mFrameCount, mLoopCount);
+        EffectTestHelper testEffect(mUuid, chMask, chMask, mSampleRate, mFrameCount, mLoopCount);
 
         ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
-        ASSERT_NO_FATAL_FAILURE(testEffect.configEffect());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
 
         std::vector<float> testInput(mTotalFrameCount * channelCount);
 
@@ -312,7 +174,8 @@
             // SNR must be above the threshold
             float snr = computeSnr<int16_t>(stereoRefI16.data(), stereoTestI16.data(),
                                             mTotalFrameCount * FCC_2);
-            ASSERT_GT(snr, kSNRThreshold) << "SNR " << snr << "is lower than " << kSNRThreshold;
+            ASSERT_GT(snr, EffectTestHelper::kSNRThreshold)
+                    << "SNR " << snr << "is lower than " << EffectTestHelper::kSNRThreshold;
         } else {
             ASSERT_EQ(0,
                       memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
@@ -321,11 +184,12 @@
     }
 }
 
-INSTANTIATE_TEST_SUITE_P(EffectBundleTestAll, SingleEffectComparisonTest,
-                         ::testing::Combine(::testing::Range(0, (int)kNumSampleRates),
-                                            ::testing::Range(0, (int)kNumFrameCounts),
-                                            ::testing::Range(0, (int)kNumLoopCounts),
-                                            ::testing::Range(0, (int)kNumEffectUuids)));
+INSTANTIATE_TEST_SUITE_P(
+        EffectBundleTestAll, SingleEffectComparisonTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids)));
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/media/libeffects/lvm/tests/EffectReverbTest.cpp b/media/libeffects/lvm/tests/EffectReverbTest.cpp
new file mode 100644
index 0000000..59453eb
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectReverbTest.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include <audio_effects/effect_presetreverb.h>
+#include <VectorArithmetic.h>
+
+#include "EffectTestHelper.h"
+using namespace android;
+
+constexpr effect_uuid_t kEffectUuids[] = {
+        // NXP SW insert environmental reverb
+        {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW insert preset reverb
+        {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW auxiliary environmental reverb
+        {0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        // NXP SW auxiliary preset reverb
+        {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+};
+
+constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
+
+static bool isAuxMode(const effect_uuid_t* uuid) {
+    // Update this, if the order of effects in kEffectUuids is updated
+    return (uuid == &kEffectUuids[2] || uuid == &kEffectUuids[3]);
+}
+
+constexpr int kPresets[] = {
+        REVERB_PRESET_NONE,      REVERB_PRESET_SMALLROOM,  REVERB_PRESET_MEDIUMROOM,
+        REVERB_PRESET_LARGEROOM, REVERB_PRESET_MEDIUMHALL, REVERB_PRESET_LARGEHALL,
+        REVERB_PRESET_PLATE,
+};
+
+constexpr size_t kNumPresets = std::size(kPresets);
+
+typedef std::tuple<int, int, int, int, int, int> SingleEffectTestParam;
+class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
+  public:
+    SingleEffectTest()
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
+          mTotalFrameCount(mFrameCount * mLoopCount),
+          mUuid(&kEffectUuids[std::get<4>(GetParam())]),
+          mInChMask(isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO
+                                     : EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
+          mInChannelCount(audio_channel_count_from_out_mask(mInChMask)),
+          mOutChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
+          mOutChannelCount(audio_channel_count_from_out_mask(mOutChMask)),
+          mPreset(kPresets[std::get<5>(GetParam())]) {}
+
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    const size_t mTotalFrameCount;
+    const effect_uuid_t* mUuid;
+    const size_t mInChMask;
+    const size_t mInChannelCount;
+    const size_t mOutChMask;
+    const size_t mOutChannelCount;
+    const size_t mPreset;
+};
+
+// Tests applying a single effect
+TEST_P(SingleEffectTest, SimpleProcess) {
+    SCOPED_TRACE(testing::Message() << "outChMask: " << mOutChMask << " sampleRate: " << mSampleRate
+                                    << " frameCount: " << mFrameCount
+                                    << " loopCount: " << mLoopCount << " preset: " << mPreset);
+
+    EffectTestHelper effect(mUuid, mInChMask, mOutChMask, mSampleRate, mFrameCount, mLoopCount);
+
+    ASSERT_NO_FATAL_FAILURE(effect.createEffect());
+    ASSERT_NO_FATAL_FAILURE(effect.setConfig());
+    ASSERT_NO_FATAL_FAILURE(effect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+    // Initialize input buffer with deterministic pseudo-random values
+    std::vector<float> input(mTotalFrameCount * mInChannelCount);
+    std::vector<float> output(mTotalFrameCount * mOutChannelCount);
+    std::minstd_rand gen(mOutChMask);
+    std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+    for (auto& in : input) {
+        in = dis(gen);
+    }
+    ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
+    ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EffectReverbTestAll, SingleEffectTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids),
+                           ::testing::Range(0, (int)kNumPresets)));
+
+typedef std::tuple<int, int, int, int, int> SingleEffectComparisonTestParam;
+class SingleEffectComparisonTest
+    : public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
+  public:
+    SingleEffectComparisonTest()
+        : mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
+          mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
+          mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
+          mTotalFrameCount(mFrameCount * mLoopCount),
+          mUuid(&kEffectUuids[std::get<3>(GetParam())]),
+          mPreset(kPresets[std::get<4>(GetParam())]) {}
+
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    const size_t mTotalFrameCount;
+    const effect_uuid_t* mUuid;
+    const size_t mPreset;
+};
+
+// Compares first two channels in multi-channel output to stereo output when same effect is applied
+TEST_P(SingleEffectComparisonTest, SimpleProcess) {
+    SCOPED_TRACE(testing::Message()
+                 << " sampleRate: " << mSampleRate << " frameCount: " << mFrameCount
+                 << " loopCount: " << mLoopCount << " preset: " << mPreset);
+
+    // Initialize mono input buffer with deterministic pseudo-random values
+    std::vector<float> monoInput(mTotalFrameCount);
+
+    std::minstd_rand gen(mSampleRate);
+    std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+    for (auto& in : monoInput) {
+        in = dis(gen);
+    }
+
+    // Generate stereo by repeating mono channel data
+    std::vector<float> stereoInput(mTotalFrameCount * FCC_2);
+    adjust_channels(monoInput.data(), FCC_1, stereoInput.data(), FCC_2, sizeof(float),
+                    mTotalFrameCount * sizeof(float) * FCC_1);
+
+    // Apply effect on stereo channels
+    EffectTestHelper stereoEffect(
+            mUuid, isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO,
+            AUDIO_CHANNEL_OUT_STEREO, mSampleRate, mFrameCount, mLoopCount);
+
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+    std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.process(
+            (isAuxMode(mUuid) ? monoInput.data() : stereoInput.data()), stereoOutput.data()));
+    ASSERT_NO_FATAL_FAILURE(stereoEffect.releaseEffect());
+
+    // Average of both channels data is stored for mono comparison
+    std::vector<float> monoOutput(mTotalFrameCount);
+    From2iToMono_Float((const float*)stereoOutput.data(), monoOutput.data(), mTotalFrameCount);
+
+    // Convert stereo float data to stereo int16_t to be used as reference
+    std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
+    memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
+
+    // mono int16_t to be used as refernece for mono comparison
+    std::vector<int16_t> monoRefI16(mTotalFrameCount);
+    memcpy_to_i16_from_float(monoRefI16.data(), monoOutput.data(), mTotalFrameCount);
+
+    for (size_t outChMask : EffectTestHelper::kChMasks) {
+        size_t outChannelCount = audio_channel_count_from_out_mask(outChMask);
+        size_t inChMask = isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : outChMask;
+
+        EffectTestHelper testEffect(mUuid, inChMask, outChMask, mSampleRate, mFrameCount,
+                                    mLoopCount);
+
+        ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
+        ASSERT_NO_FATAL_FAILURE(testEffect.setParam(REVERB_PARAM_PRESET, mPreset));
+
+        std::vector<float> testInput(mTotalFrameCount * outChannelCount);
+
+        // Repeat mono channel data to all the channels
+        // adjust_channels() zero fills channels > 2, hence can't be used here
+        for (size_t i = 0; i < mTotalFrameCount; ++i) {
+            auto* fp = &testInput[i * outChannelCount];
+            std::fill(fp, fp + outChannelCount, monoInput[i]);
+        }
+
+        std::vector<float> testOutput(mTotalFrameCount * outChannelCount);
+        ASSERT_NO_FATAL_FAILURE(testEffect.process(
+                (isAuxMode(mUuid) ? monoInput.data() : testInput.data()), testOutput.data()));
+        ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
+
+        if (outChannelCount == FCC_1) {
+            // Convert the test data to int16_t
+            std::vector<int16_t> monoTestI16(mTotalFrameCount);
+            memcpy_to_i16_from_float(monoTestI16.data(), testOutput.data(), mTotalFrameCount);
+
+            ASSERT_EQ(0, memcmp(monoRefI16.data(), monoTestI16.data(), mTotalFrameCount * FCC_2))
+                    << "Mono channel do not match with reference output \n";
+        } else {
+            // Extract first two channels
+            std::vector<float> stereoTestOutput(mTotalFrameCount * FCC_2);
+            adjust_channels(testOutput.data(), outChannelCount, stereoTestOutput.data(), FCC_2,
+                            sizeof(float), mTotalFrameCount * sizeof(float) * outChannelCount);
+
+            // Convert the test data to int16_t
+            std::vector<int16_t> stereoTestI16(mTotalFrameCount * FCC_2);
+            memcpy_to_i16_from_float(stereoTestI16.data(), stereoTestOutput.data(),
+                                     mTotalFrameCount * FCC_2);
+
+            ASSERT_EQ(0,
+                      memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
+                    << "First two channels do not match with stereo output \n";
+        }
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        EffectReverbTestAll, SingleEffectComparisonTest,
+        ::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
+                           ::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
+                           ::testing::Range(0, (int)kNumEffectUuids),
+                           ::testing::Range(0, (int)kNumPresets)));
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGV("Test result = %d\n", status);
+    return status;
+}
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.cpp b/media/libeffects/lvm/tests/EffectTestHelper.cpp
new file mode 100644
index 0000000..625c15a
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectTestHelper.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include "EffectTestHelper.h"
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+namespace android {
+
+void EffectTestHelper::createEffect() {
+    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(mUuid, 1, 1, &mEffectHandle);
+    ASSERT_EQ(status, 0) << "create_effect returned an error " << status;
+}
+
+void EffectTestHelper::releaseEffect() {
+    int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(mEffectHandle);
+    ASSERT_EQ(status, 0) << "release_effect returned an error " << status;
+}
+
+void EffectTestHelper::setConfig() {
+    effect_config_t config{};
+    config.inputCfg.samplingRate = config.outputCfg.samplingRate = mSampleRate;
+    config.inputCfg.channels = mInChMask;
+    config.outputCfg.channels = mOutChMask;
+    config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    int status = (*mEffectHandle)
+                         ->command(mEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
+                                   &config, &replySize, &reply);
+    ASSERT_EQ(status, 0) << "set_config returned an error " << status;
+    ASSERT_EQ(reply, 0) << "set_config reply non zero " << reply;
+
+    status = (*mEffectHandle)
+                     ->command(mEffectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+    ASSERT_EQ(status, 0) << "cmd_enable returned an error " << status;
+    ASSERT_EQ(reply, 0) << "cmd_enable reply non zero " << reply;
+}
+
+void EffectTestHelper::setParam(uint32_t type, uint32_t value) {
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    uint32_t paramData[2] = {type, value};
+    auto effectParam = new effect_param_t[sizeof(effect_param_t) + sizeof(paramData)];
+    memcpy(&effectParam->data[0], &paramData[0], sizeof(paramData));
+    effectParam->psize = sizeof(paramData[0]);
+    effectParam->vsize = sizeof(paramData[1]);
+    int status = (*mEffectHandle)
+                         ->command(mEffectHandle, EFFECT_CMD_SET_PARAM,
+                                   sizeof(effect_param_t) + sizeof(paramData), effectParam,
+                                   &replySize, &reply);
+    delete[] effectParam;
+    ASSERT_EQ(status, 0) << "set_param returned an error " << status;
+    ASSERT_EQ(reply, 0) << "set_param reply non zero " << reply;
+}
+
+void EffectTestHelper::process(float* input, float* output) {
+    audio_buffer_t inBuffer = {.frameCount = mFrameCount, .f32 = input};
+    audio_buffer_t outBuffer = {.frameCount = mFrameCount, .f32 = output};
+    for (size_t i = 0; i < mLoopCount; i++) {
+        int status = (*mEffectHandle)->process(mEffectHandle, &inBuffer, &outBuffer);
+        ASSERT_EQ(status, 0) << "process returned an error " << status;
+
+        inBuffer.f32 += mFrameCount * mInChannelCount;
+        outBuffer.f32 += mFrameCount * mOutChannelCount;
+    }
+}
+}  // namespace android
diff --git a/media/libeffects/lvm/tests/EffectTestHelper.h b/media/libeffects/lvm/tests/EffectTestHelper.h
new file mode 100644
index 0000000..3854d46
--- /dev/null
+++ b/media/libeffects/lvm/tests/EffectTestHelper.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2021 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 <array>
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <climits>
+#include <cstdlib>
+#include <gtest/gtest.h>
+#include <hardware/audio_effect.h>
+#include <log/log.h>
+#include <random>
+#include <stdint.h>
+#include <system/audio.h>
+#include <vector>
+
+namespace android {
+template <typename T>
+static float computeSnr(const T* ref, const T* tst, size_t count) {
+    double signal{};
+    double noise{};
+
+    for (size_t i = 0; i < count; ++i) {
+        const double value(ref[i]);
+        const double diff(tst[i] - value);
+        signal += value * value;
+        noise += diff * diff;
+    }
+    // Initialized to large value to handle
+    // cases where ref and tst match exactly
+    float snr = FLT_MAX;
+    if (signal > 0.0f && noise > 0.0f) {
+        snr = 10.f * log(signal / noise);
+    }
+    return snr;
+}
+
+class EffectTestHelper {
+  public:
+    EffectTestHelper(const effect_uuid_t* uuid, size_t inChMask, size_t outChMask,
+                     size_t sampleRate, size_t frameCount, size_t loopCount)
+        : mUuid(uuid),
+          mInChMask(inChMask),
+          mInChannelCount(audio_channel_count_from_out_mask(mInChMask)),
+          mOutChMask(outChMask),
+          mOutChannelCount(audio_channel_count_from_out_mask(mOutChMask)),
+          mSampleRate(sampleRate),
+          mFrameCount(frameCount),
+          mLoopCount(loopCount) {}
+    void createEffect();
+    void releaseEffect();
+    void setConfig();
+    void setParam(uint32_t type, uint32_t val);
+    void process(float* input, float* output);
+
+    // Corresponds to SNR for 1 bit difference between two int16_t signals
+    static constexpr float kSNRThreshold = 90.308998;
+
+    static constexpr audio_channel_mask_t kChMasks[] = {
+            AUDIO_CHANNEL_OUT_MONO,          AUDIO_CHANNEL_OUT_STEREO,
+            AUDIO_CHANNEL_OUT_2POINT1,       AUDIO_CHANNEL_OUT_2POINT0POINT2,
+            AUDIO_CHANNEL_OUT_QUAD,          AUDIO_CHANNEL_OUT_QUAD_BACK,
+            AUDIO_CHANNEL_OUT_QUAD_SIDE,     AUDIO_CHANNEL_OUT_SURROUND,
+            AUDIO_CHANNEL_INDEX_MASK_4,      AUDIO_CHANNEL_OUT_2POINT1POINT2,
+            AUDIO_CHANNEL_OUT_3POINT0POINT2, AUDIO_CHANNEL_OUT_PENTA,
+            AUDIO_CHANNEL_INDEX_MASK_5,      AUDIO_CHANNEL_OUT_3POINT1POINT2,
+            AUDIO_CHANNEL_OUT_5POINT1,       AUDIO_CHANNEL_OUT_5POINT1_BACK,
+            AUDIO_CHANNEL_OUT_5POINT1_SIDE,  AUDIO_CHANNEL_INDEX_MASK_6,
+            AUDIO_CHANNEL_OUT_6POINT1,       AUDIO_CHANNEL_INDEX_MASK_7,
+            AUDIO_CHANNEL_OUT_5POINT1POINT2, AUDIO_CHANNEL_OUT_7POINT1,
+            AUDIO_CHANNEL_INDEX_MASK_8,      AUDIO_CHANNEL_INDEX_MASK_9,
+            AUDIO_CHANNEL_INDEX_MASK_10,     AUDIO_CHANNEL_INDEX_MASK_11,
+            AUDIO_CHANNEL_INDEX_MASK_12,     AUDIO_CHANNEL_INDEX_MASK_13,
+            AUDIO_CHANNEL_INDEX_MASK_14,     AUDIO_CHANNEL_INDEX_MASK_15,
+            AUDIO_CHANNEL_INDEX_MASK_16,     AUDIO_CHANNEL_INDEX_MASK_17,
+            AUDIO_CHANNEL_INDEX_MASK_18,     AUDIO_CHANNEL_INDEX_MASK_19,
+            AUDIO_CHANNEL_INDEX_MASK_20,     AUDIO_CHANNEL_INDEX_MASK_21,
+            AUDIO_CHANNEL_INDEX_MASK_22,     AUDIO_CHANNEL_INDEX_MASK_23,
+            AUDIO_CHANNEL_INDEX_MASK_24,
+    };
+
+    static constexpr size_t kNumChMasks = std::size(kChMasks);
+
+    static constexpr size_t kSampleRates[] = {8000,  11025, 12000, 16000, 22050,  24000, 32000,
+                                              44100, 48000, 88200, 96000, 176400, 192000};
+
+    static constexpr size_t kNumSampleRates = std::size(kSampleRates);
+
+    static constexpr size_t kFrameCounts[] = {4, 2048};
+
+    static constexpr size_t kNumFrameCounts = std::size(kFrameCounts);
+
+    static constexpr size_t kLoopCounts[] = {1, 4};
+
+    static constexpr size_t kNumLoopCounts = std::size(kLoopCounts);
+
+  private:
+    const effect_uuid_t* mUuid;
+    const size_t mInChMask;
+    const size_t mInChannelCount;
+    const size_t mOutChMask;
+    const size_t mOutChannelCount;
+    const size_t mSampleRate;
+    const size_t mFrameCount;
+    const size_t mLoopCount;
+    effect_handle_t mEffectHandle{};
+};
+}  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MediaErrors.h b/media/libstagefright/include/media/stagefright/MediaErrors.h
index cfd5608..d1df2ca 100644
--- a/media/libstagefright/include/media/stagefright/MediaErrors.h
+++ b/media/libstagefright/include/media/stagefright/MediaErrors.h
@@ -94,12 +94,13 @@
     ERROR_DRM_PROVISIONING_CERTIFICATE       = DRM_ERROR_BASE - 31,
     ERROR_DRM_PROVISIONING_CONFIG            = DRM_ERROR_BASE - 32,
     ERROR_DRM_PROVISIONING_PARSE             = DRM_ERROR_BASE - 33,
-    ERROR_DRM_PROVISIONING_RETRY             = DRM_ERROR_BASE - 34,
-    ERROR_DRM_SECURE_STOP_RELEASE            = DRM_ERROR_BASE - 35,
-    ERROR_DRM_STORAGE_READ                   = DRM_ERROR_BASE - 36,
-    ERROR_DRM_STORAGE_WRITE                  = DRM_ERROR_BASE - 37,
-    ERROR_DRM_ZERO_SUBSAMPLES                = DRM_ERROR_BASE - 38,
-    ERROR_DRM_LAST_USED_ERRORCODE            = DRM_ERROR_BASE - 38,
+    ERROR_DRM_PROVISIONING_REQUEST_REJECTED  = DRM_ERROR_BASE - 34,
+    ERROR_DRM_PROVISIONING_RETRY             = DRM_ERROR_BASE - 35,
+    ERROR_DRM_SECURE_STOP_RELEASE            = DRM_ERROR_BASE - 36,
+    ERROR_DRM_STORAGE_READ                   = DRM_ERROR_BASE - 37,
+    ERROR_DRM_STORAGE_WRITE                  = DRM_ERROR_BASE - 38,
+    ERROR_DRM_ZERO_SUBSAMPLES                = DRM_ERROR_BASE - 39,
+    ERROR_DRM_LAST_USED_ERRORCODE            = ERROR_DRM_ZERO_SUBSAMPLES,
 
     ERROR_DRM_VENDOR_MAX                     = DRM_ERROR_BASE - 500,
     ERROR_DRM_VENDOR_MIN                     = DRM_ERROR_BASE - 999,
@@ -202,6 +203,7 @@
         STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE);
         STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG);
         STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE);
+        STATUS_CASE(ERROR_DRM_PROVISIONING_REQUEST_REJECTED);
         STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY);
         STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE);
         STATUS_CASE(ERROR_DRM_STORAGE_READ);
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index d038ce5..380bf6b 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -142,47 +142,19 @@
     return EngineBase::setForceUse(usage, config);
 }
 
-DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
-                                              DeviceVector availableOutputDevices,
-                                              DeviceVector availableInputDevices,
-                                              const SwAudioOutputCollection &outputs) const
+void Engine::filterOutputDevicesForStrategy(legacy_strategy strategy,
+                                            DeviceVector& availableOutputDevices,
+                                            const DeviceVector availableInputDevices,
+                                            const SwAudioOutputCollection &outputs) const
 {
-    DeviceVector devices;
-
     switch (strategy) {
-
-    case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
-        devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
-        break;
-
-    case STRATEGY_SONIFICATION_RESPECTFUL:
-        if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
-            devices = getDevicesForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
-        } else {
-            bool media_active_locally =
-                    outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
-                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
-                    || outputs.isActiveLocally(
-                        toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
-                        SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+    case STRATEGY_SONIFICATION_RESPECTFUL: {
+        if (!(isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL)))) {
             // routing is same as media without the "remote" device
             availableOutputDevices.remove(availableOutputDevices.getDevicesFromType(
                     AUDIO_DEVICE_OUT_REMOTE_SUBMIX));
-            devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
-                    availableOutputDevices,
-                    availableInputDevices, outputs);
-            // if no media is playing on the device, check for mandatory use of "safe" speaker
-            // when media would have played on speaker, and the safe speaker path is available
-            if (!media_active_locally) {
-                devices.replaceDevicesByType(
-                        AUDIO_DEVICE_OUT_SPEAKER,
-                        availableOutputDevices.getDevicesFromType(
-                                AUDIO_DEVICE_OUT_SPEAKER_SAFE));
-            }
         }
-        break;
-
+        } break;
     case STRATEGY_DTMF:
     case STRATEGY_PHONE: {
         // Force use of only devices on primary output if:
@@ -214,6 +186,64 @@
                 availableOutputDevices = availPrimaryOutputDevices;
             }
         }
+        } break;
+    case STRATEGY_ACCESSIBILITY: {
+        // do not route accessibility prompts to a digital output currently configured with a
+        // compressed format as they would likely not be mixed and dropped.
+        for (size_t i = 0; i < outputs.size(); i++) {
+            sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
+            if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
+                availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
+                        AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
+                        AUDIO_DEVICE_OUT_HDMI_ARC}));
+            }
+        }
+        } break;
+    default:
+        break;
+    }
+}
+
+DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy,
+                                              DeviceVector availableOutputDevices,
+                                              DeviceVector availableInputDevices,
+                                              const SwAudioOutputCollection &outputs) const
+{
+    DeviceVector devices;
+
+    switch (strategy) {
+
+    case STRATEGY_TRANSMITTED_THROUGH_SPEAKER:
+        devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);
+        break;
+
+    case STRATEGY_SONIFICATION_RESPECTFUL:
+        if (isInCall() || outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) {
+            devices = getDevicesForStrategyInt(
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
+        } else {
+            bool media_active_locally =
+                    outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC),
+                                            SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+                    || outputs.isActiveLocally(
+                        toVolumeSource(AUDIO_STREAM_ACCESSIBILITY),
+                        SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+            devices = getDevicesForStrategyInt(STRATEGY_MEDIA,
+                    availableOutputDevices,
+                    availableInputDevices, outputs);
+            // if no media is playing on the device, check for mandatory use of "safe" speaker
+            // when media would have played on speaker, and the safe speaker path is available
+            if (!media_active_locally) {
+                devices.replaceDevicesByType(
+                        AUDIO_DEVICE_OUT_SPEAKER,
+                        availableOutputDevices.getDevicesFromType(
+                                AUDIO_DEVICE_OUT_SPEAKER_SAFE));
+            }
+        }
+        break;
+
+    case STRATEGY_DTMF:
+    case STRATEGY_PHONE: {
         devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);
         if (!devices.isEmpty()) break;
         devices = availableOutputDevices.getFirstDevicesFromTypes({
@@ -286,16 +316,6 @@
 
     case STRATEGY_ACCESSIBILITY:
         if (strategy == STRATEGY_ACCESSIBILITY) {
-            // do not route accessibility prompts to a digital output currently configured with a
-            // compressed format as they would likely not be mixed and dropped.
-            for (size_t i = 0; i < outputs.size(); i++) {
-                sp<AudioOutputDescriptor> desc = outputs.valueAt(i);
-                if (desc->isActive() && !audio_is_linear_pcm(desc->getFormat())) {
-                    availableOutputDevices.remove(desc->devices().getDevicesFromTypes({
-                            AUDIO_DEVICE_OUT_HDMI, AUDIO_DEVICE_OUT_SPDIF,
-                            AUDIO_DEVICE_OUT_HDMI_ARC}));
-                }
-            }
             if (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
                     outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM))) {
                 return getDevicesForStrategyInt(
@@ -634,11 +654,18 @@
     auto legacyStrategy = mLegacyStrategyMap.find(strategy) != end(mLegacyStrategyMap) ?
                           mLegacyStrategyMap.at(strategy) : STRATEGY_NONE;
 
-    // When not in call, STRATEGY_PHONE and STRATEGY_DTMF follow STRATEGY_MEDIA
-    if (!isInCall() && (legacyStrategy == STRATEGY_PHONE || legacyStrategy == STRATEGY_DTMF)) {
+    // When not in call, STRATEGY_DTMF follows STRATEGY_MEDIA
+    if (!isInCall() && legacyStrategy == STRATEGY_DTMF) {
         legacyStrategy = STRATEGY_MEDIA;
         strategy = getProductStrategyFromLegacy(STRATEGY_MEDIA);
     }
+
+    DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
+    const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
+
+    filterOutputDevicesForStrategy(legacyStrategy, availableOutputDevices,
+                                   availableInputDevices, outputs);
+
     // check if this strategy has a preferred device that is available,
     // if yes, give priority to it.
     DeviceVector preferredAvailableDevVec =
@@ -647,9 +674,6 @@
         return preferredAvailableDevVec;
     }
 
-    DeviceVector availableInputDevices = getApmObserver()->getAvailableInputDevices();
-    const SwAudioOutputCollection& outputs = getApmObserver()->getOutputs();
-
     return getDevicesForStrategyInt(legacyStrategy,
                                     availableOutputDevices,
                                     availableInputDevices, outputs);
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 6214fe7..6dc6cd0 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -74,6 +74,11 @@
 
     status_t setDefaultDevice(audio_devices_t device);
 
+    void filterOutputDevicesForStrategy(legacy_strategy strategy,
+                                            DeviceVector& availableOutputDevices,
+                                            const DeviceVector availableInputDevices,
+                                            const SwAudioOutputCollection &outputs) const;
+
     DeviceVector getDevicesForStrategyInt(legacy_strategy strategy,
                                           DeviceVector availableOutputDevices,
                                           DeviceVector availableInputDevices,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 75f0c1b..6b664dd 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -261,11 +261,7 @@
         } else {
             checkCloseOutputs();
         }
-
-        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-            DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-            updateCallRouting(newDevices);
-        }
+        (void)updateCallRouting(false /*fromCache*/);
         std::vector<audio_io_handle_t> outputsToReopen;
         const DeviceVector msdOutDevices = getMsdAudioOutDevices();
         const DeviceVector activeMediaDevices =
@@ -382,10 +378,7 @@
         // getDeviceForStrategy() cache
         updateDevicesAndOutputs();
 
-        if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-            DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-            updateCallRouting(newDevices);
-        }
+        (void)updateCallRouting(false /*fromCache*/);
         // Reconnect Audio Source
         for (const auto &strategy : mEngine->getOrderedProductStrategies()) {
             auto attributes = mEngine->getAllAttributesForProductStrategy(strategy).front();
@@ -550,23 +543,58 @@
     return status;
 }
 
-uint32_t AudioPolicyManager::updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs)
+DeviceVector AudioPolicyManager::selectBestRxSinkDevicesForCall(bool fromCache)
+{
+    DeviceVector rxSinkdevices{};
+    rxSinkdevices = mEngine->getOutputDevicesForAttributes(
+                attributes_initializer(AUDIO_USAGE_VOICE_COMMUNICATION), nullptr, fromCache);
+    if (!rxSinkdevices.isEmpty() && mAvailableOutputDevices.contains(rxSinkdevices.itemAt(0))) {
+        auto rxSinkDevice = rxSinkdevices.itemAt(0);
+        auto telephonyRxModule = mHwModules.getModuleForDeviceType(
+                    AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_FORMAT_DEFAULT);
+        // retrieve Rx Source device descriptor
+        sp<DeviceDescriptor> rxSourceDevice = mAvailableInputDevices.getDevice(
+                    AUDIO_DEVICE_IN_TELEPHONY_RX, String8(), AUDIO_FORMAT_DEFAULT);
+
+        // RX Telephony and Rx sink devices are declared by Primary Audio HAL
+        if (isPrimaryModule(telephonyRxModule) && (telephonyRxModule->getHalVersionMajor() >= 3) &&
+                telephonyRxModule->supportsPatch(rxSourceDevice, rxSinkDevice)) {
+            ALOGW("%s() device %s using HW Bridge", __func__, rxSinkDevice->toString().c_str());
+            return DeviceVector(rxSinkDevice);
+        }
+    }
+    // Note that despite the fact that getNewOutputDevices() is called on the primary output,
+    // the device returned is not necessarily reachable via this output
+    // (filter later by setOutputDevices())
+    return getNewOutputDevices(mPrimaryOutput, fromCache);
+}
+
+status_t AudioPolicyManager::updateCallRouting(bool fromCache, uint32_t delayMs, uint32_t *waitMs)
+{
+    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
+        DeviceVector rxDevices = selectBestRxSinkDevicesForCall(fromCache);
+        return updateCallRoutingInternal(rxDevices, delayMs, waitMs);
+    }
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyManager::updateCallRoutingInternal(
+        const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs)
 {
     bool createTxPatch = false;
     bool createRxPatch = false;
     uint32_t muteWaitMs = 0;
-
     if(!hasPrimaryOutput() ||
             mPrimaryOutput->devices().onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_STUB)) {
-        return muteWaitMs;
+        return INVALID_OPERATION;
     }
-    ALOG_ASSERT(!rxDevices.isEmpty(), "updateCallRouting() no selected output device");
+    ALOG_ASSERT(!rxDevices.isEmpty(), "%s() no selected output device", __func__);
 
     audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
     auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
-    ALOG_ASSERT(txSourceDevice != 0, "updateCallRouting() input selected device not available");
+    ALOG_ASSERT(txSourceDevice != 0, "%s() input selected device not available", __func__);
 
-    ALOGV("updateCallRouting device rxDevice %s txDevice %s",
+    ALOGV("%s device rxDevice %s txDevice %s", __func__,
           rxDevices.itemAt(0)->toString().c_str(), txSourceDevice->toString().c_str());
 
     disconnectTelephonyRxAudioSource();
@@ -595,8 +623,8 @@
             (telephonyRxModule->getHalVersionMajor() >= 3)) {
         if (rxSourceDevice == 0 || txSinkDevice == 0) {
             // RX / TX Telephony device(s) is(are) not currently available
-            ALOGE("updateCallRouting() no telephony Tx and/or RX device");
-            return muteWaitMs;
+            ALOGE("%s() no telephony Tx and/or RX device", __func__);
+            return INVALID_OPERATION;
         }
         // createAudioPatchInternal now supports both HW / SW bridging
         createRxPatch = true;
@@ -634,8 +662,10 @@
         }
         mCallTxPatch = createTelephonyPatch(false /*isRx*/, txSourceDevice, delayMs);
     }
-
-    return muteWaitMs;
+    if (waitMs != nullptr) {
+        *waitMs = muteWaitMs;
+    }
+    return NO_ERROR;
 }
 
 sp<AudioPatch> AudioPolicyManager::createTelephonyPatch(
@@ -753,25 +783,22 @@
     }
 
     if (hasPrimaryOutput()) {
-        // Note that despite the fact that getNewOutputDevices() is called on the primary output,
-        // the device returned is not necessarily reachable via this output
-        DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
-        // force routing command to audio hardware when ending call
-        // even if no device change is needed
-        if (isStateInCall(oldState) && rxDevices.isEmpty()) {
-            rxDevices = mPrimaryOutput->devices();
-        }
-
         if (state == AUDIO_MODE_IN_CALL) {
-            updateCallRouting(rxDevices, delayMs);
-        } else if (oldState == AUDIO_MODE_IN_CALL) {
-            disconnectTelephonyRxAudioSource();
-            if (mCallTxPatch != 0) {
-                releaseAudioPatchInternal(mCallTxPatch->getHandle());
-                mCallTxPatch.clear();
-            }
-            setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
+            (void)updateCallRouting(false /*fromCache*/, delayMs);
         } else {
+            DeviceVector rxDevices = getNewOutputDevices(mPrimaryOutput, false /*fromCache*/);
+            // force routing command to audio hardware when ending call
+            // even if no device change is needed
+            if (isStateInCall(oldState) && rxDevices.isEmpty()) {
+                rxDevices = mPrimaryOutput->devices();
+            }
+            if (oldState == AUDIO_MODE_IN_CALL) {
+                disconnectTelephonyRxAudioSource();
+                if (mCallTxPatch != 0) {
+                    releaseAudioPatchInternal(mCallTxPatch->getHandle());
+                    mCallTxPatch.clear();
+                }
+            }
             setOutputDevices(mPrimaryOutput, rxDevices, force, 0);
         }
     }
@@ -3300,9 +3327,7 @@
 void AudioPolicyManager::updateCallAndOutputRouting(bool forceVolumeReeval, uint32_t delayMs)
 {
     uint32_t waitMs = 0;
-    if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
-        DeviceVector newDevices = getNewOutputDevices(mPrimaryOutput, true /*fromCache*/);
-        waitMs = updateCallRouting(newDevices, delayMs);
+    if (updateCallRouting(true /*fromCache*/, delayMs, &waitMs) == NO_ERROR) {
         // Only apply special touch sound delay once
         delayMs = 0;
     }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index bf73f75..bdf82ef 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -735,9 +735,22 @@
                     String8(devices.itemAt(0)->address().c_str()) : String8("");
         }
 
-        uint32_t updateCallRouting(const DeviceVector &rxDevices, uint32_t delayMs = 0);
+        status_t updateCallRouting(
+                bool fromCache, uint32_t delayMs = 0, uint32_t *waitMs = nullptr);
+        status_t updateCallRoutingInternal(
+                const DeviceVector &rxDevices, uint32_t delayMs, uint32_t *waitMs);
         sp<AudioPatch> createTelephonyPatch(bool isRx, const sp<DeviceDescriptor> &device,
                                             uint32_t delayMs);
+        /**
+         * @brief selectBestRxSinkDevicesForCall: if the primary module host both Telephony Rx/Tx
+         * devices, and it declares also supporting a HW bridge between the Telephony Rx and the
+         * given sink device for Voice Call audio attributes, select this device in prio.
+         * Otherwise, getNewOutputDevices() is called on the primary output to select sink device.
+         * @param fromCache true to prevent engine reconsidering all product strategies and retrieve
+         * from engine cache.
+         * @return vector of devices, empty if none is found.
+         */
+        DeviceVector selectBestRxSinkDevicesForCall(bool fromCache);
         bool isDeviceOfModule(const sp<DeviceDescriptor>& devDesc, const char *moduleId) const;
 
         status_t startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
diff --git a/services/oboeservice/fuzzer/Android.bp b/services/oboeservice/fuzzer/Android.bp
new file mode 100644
index 0000000..78ef3fc
--- /dev/null
+++ b/services/oboeservice/fuzzer/Android.bp
@@ -0,0 +1,70 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_av_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_fuzz {
+    name: "oboeservice_fuzzer",
+    srcs: [
+        "oboeservice_fuzzer.cpp",
+    ],
+    shared_libs: [
+        "libaaudio_internal",
+        "libaudioclient",
+        "libaudioflinger",
+        "libaudioutils",
+        "libmedia_helper",
+        "libmediametrics",
+        "libmediautils",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "aaudio-aidl-cpp",
+    ],
+    static_libs: [
+        "libaaudioservice",
+    ],
+    include_dirs: [
+        "frameworks/av/services/oboeservice",
+    ],
+    header_libs: [
+        "libaudiohal_headers",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/services/oboeservice/fuzzer/README.md b/services/oboeservice/fuzzer/README.md
new file mode 100644
index 0000000..00b85df
--- /dev/null
+++ b/services/oboeservice/fuzzer/README.md
@@ -0,0 +1,65 @@
+# Fuzzer for libaaudioservice
+
+## Plugin Design Considerations
+The fuzzer plugin for libaaudioservice is designed based on the
+understanding of the service and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+AAudio Service request contains the following parameters:
+1. AAudioFormat
+2. UserId
+3. ProcessId
+4. InService
+5. DeviceId
+6. SampleRate
+7. SamplesPerFrame
+8. Direction
+9. SharingMode
+10. Usage
+11. ContentType
+12. InputPreset
+13. BufferCapacity
+
+| Parameter| Valid Input Values| Configured Value|
+|------------- |-------------| ----- |
+| `AAudioFormat` | `AAUDIO_FORMAT_UNSPECIFIED`, `AAUDIO_FORMAT_PCM_I16`, `AAUDIO_FORMAT_PCM_FLOAT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `UserId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from getuid() |
+| `ProcessId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from getpid() |
+| `InService`   | `bool` | Value obtained from FuzzedDataProvider |
+| `DeviceId`   | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SampleRate`   | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `SamplesPerFrame` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `Direction` | `AAUDIO_DIRECTION_OUTPUT`, `AAUDIO_DIRECTION_INPUT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `SharingMode` | `AAUDIO_SHARING_MODE_EXCLUSIVE`, `AAUDIO_SHARING_MODE_SHARED` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `Usage` | `AAUDIO_USAGE_MEDIA`, `AAUDIO_USAGE_VOICE_COMMUNICATION`, `AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING`, `AAUDIO_USAGE_ALARM`, `AAUDIO_USAGE_NOTIFICATION`, `AAUDIO_USAGE_NOTIFICATION_RINGTONE`, `AAUDIO_USAGE_NOTIFICATION_EVENT`, `AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY`, `AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE`, `AAUDIO_USAGE_ASSISTANCE_SONIFICATION`, `AAUDIO_USAGE_GAME`, `AAUDIO_USAGE_ASSISTANT`, `AAUDIO_SYSTEM_USAGE_EMERGENCY`, `AAUDIO_SYSTEM_USAGE_SAFETY`, `AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS`, `AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `ContentType` | `AAUDIO_CONTENT_TYPE_SPEECH`, `AAUDIO_CONTENT_TYPE_MUSIC`, `AAUDIO_CONTENT_TYPE_MOVIE`, `AAUDIO_CONTENT_TYPE_SONIFICATION` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `InputPreset` | `AAUDIO_INPUT_PRESET_GENERIC`, `AAUDIO_INPUT_PRESET_CAMCORDER`, `AAUDIO_INPUT_PRESET_VOICE_RECOGNITION`, `AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION`, `AAUDIO_INPUT_PRESET_UNPROCESSED`, `AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE` | Value chosen from valid values by obtaining index from FuzzedDataProvider |
+| `BufferCapacity` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+## Build
+
+This describes steps to build oboeservice_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) oboeservice_fuzzer
+```
+
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/oboeservice_fuzzer/oboeservice_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
new file mode 100644
index 0000000..163eae8
--- /dev/null
+++ b/services/oboeservice/fuzzer/oboeservice_fuzzer.cpp
@@ -0,0 +1,365 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <fuzzer/FuzzedDataProvider.h>
+#include <stdio.h>
+
+#include <AAudioService.h>
+#include <aaudio/AAudio.h>
+#include "aaudio/BnAAudioClient.h"
+
+#define UNUSED_PARAM __attribute__((unused))
+
+using namespace android;
+using namespace aaudio;
+
+aaudio_format_t kAAudioFormats[] = {
+    AAUDIO_FORMAT_UNSPECIFIED,
+    AAUDIO_FORMAT_PCM_I16,
+    AAUDIO_FORMAT_PCM_FLOAT,
+};
+
+aaudio_usage_t kAAudioUsages[] = {
+    AAUDIO_USAGE_MEDIA,
+    AAUDIO_USAGE_VOICE_COMMUNICATION,
+    AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+    AAUDIO_USAGE_ALARM,
+    AAUDIO_USAGE_NOTIFICATION,
+    AAUDIO_USAGE_NOTIFICATION_RINGTONE,
+    AAUDIO_USAGE_NOTIFICATION_EVENT,
+    AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+    AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+    AAUDIO_USAGE_ASSISTANCE_SONIFICATION,
+    AAUDIO_USAGE_GAME,
+    AAUDIO_USAGE_ASSISTANT,
+    AAUDIO_SYSTEM_USAGE_EMERGENCY,
+    AAUDIO_SYSTEM_USAGE_SAFETY,
+    AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS,
+    AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT,
+};
+
+aaudio_content_type_t kAAudioContentTypes[] = {
+    AAUDIO_CONTENT_TYPE_SPEECH,
+    AAUDIO_CONTENT_TYPE_MUSIC,
+    AAUDIO_CONTENT_TYPE_MOVIE,
+    AAUDIO_CONTENT_TYPE_SONIFICATION,
+};
+
+aaudio_input_preset_t kAAudioInputPresets[] = {
+    AAUDIO_INPUT_PRESET_GENERIC,           AAUDIO_INPUT_PRESET_CAMCORDER,
+    AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION,
+    AAUDIO_INPUT_PRESET_UNPROCESSED,       AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE,
+};
+
+const size_t kNumAAudioFormats = std::size(kAAudioFormats);
+const size_t kNumAAudioUsages = std::size(kAAudioUsages);
+const size_t kNumAAudioContentTypes = std::size(kAAudioContentTypes);
+const size_t kNumAAudioInputPresets = std::size(kAAudioInputPresets);
+
+class FuzzAAudioClient : public virtual RefBase, public AAudioServiceInterface {
+   public:
+    FuzzAAudioClient(sp<AAudioService> service);
+
+    virtual ~FuzzAAudioClient();
+
+    AAudioServiceInterface *getAAudioService();
+
+    void dropAAudioService();
+
+    void registerClient(const sp<IAAudioClient> &client UNUSED_PARAM) override {}
+
+    aaudio_handle_t openStream(const AAudioStreamRequest &request,
+                               AAudioStreamConfiguration &configurationOutput) override;
+
+    aaudio_result_t closeStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
+                                         AudioEndpointParcelable &parcelable) override;
+
+    aaudio_result_t startStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t flushStream(aaudio_handle_t streamHandle) override;
+
+    aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle, pid_t clientThreadId,
+                                        int64_t periodNanoseconds) override;
+
+    aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+                                          pid_t clientThreadId) override;
+
+    aaudio_result_t startClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+                                const AudioClient &client UNUSED_PARAM,
+                                const audio_attributes_t *attr UNUSED_PARAM,
+                                audio_port_handle_t *clientHandle UNUSED_PARAM) override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    aaudio_result_t stopClient(aaudio_handle_t streamHandle UNUSED_PARAM,
+                               audio_port_handle_t clientHandle UNUSED_PARAM) override {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {}
+
+    int getDeathCount() { return mDeathCount; }
+
+    void incDeathCount() { ++mDeathCount; }
+
+    class AAudioClient : public IBinder::DeathRecipient, public BnAAudioClient {
+       public:
+        AAudioClient(wp<FuzzAAudioClient> fuzzAAudioClient) : mBinderClient(fuzzAAudioClient) {}
+
+        virtual void binderDied(const wp<IBinder> &who UNUSED_PARAM) {
+            sp<FuzzAAudioClient> client = mBinderClient.promote();
+            if (client.get()) {
+                client->dropAAudioService();
+                client->incDeathCount();
+            }
+        }
+
+        android::binder::Status onStreamChange(int32_t handle, int32_t opcode, int32_t value) {
+            static_assert(std::is_same_v<aaudio_handle_t, int32_t>);
+            android::sp<FuzzAAudioClient> client = mBinderClient.promote();
+            if (client.get() != nullptr) {
+                client->onStreamChange(handle, opcode, value);
+            }
+            return android::binder::Status::ok();
+        }
+
+       private:
+        wp<FuzzAAudioClient> mBinderClient;
+    };
+
+   private:
+    sp<AAudioService> mAAudioService;
+    sp<AAudioClient> mAAudioClient;
+    AAudioServiceInterface *mAAudioServiceInterface;
+    int mDeathCount;
+};
+
+FuzzAAudioClient::FuzzAAudioClient(sp<AAudioService> service) : AAudioServiceInterface() {
+    mAAudioService = service;
+    mAAudioServiceInterface = &service->asAAudioServiceInterface();
+    mAAudioClient = new AAudioClient(this);
+    mDeathCount = 0;
+    if (mAAudioClient.get() && mAAudioService.get()) {
+        mAAudioService->linkToDeath(mAAudioClient);
+        mAAudioService->registerClient(mAAudioClient);
+    }
+}
+
+FuzzAAudioClient::~FuzzAAudioClient() { dropAAudioService(); }
+
+AAudioServiceInterface *FuzzAAudioClient::getAAudioService() {
+    if (!mAAudioServiceInterface && mAAudioService.get()) {
+        mAAudioServiceInterface = &mAAudioService->asAAudioServiceInterface();
+    }
+    return mAAudioServiceInterface;
+}
+
+void FuzzAAudioClient::dropAAudioService() {
+    mAAudioService.clear();
+}
+
+aaudio_handle_t FuzzAAudioClient::openStream(const AAudioStreamRequest &request,
+                                             AAudioStreamConfiguration &configurationOutput) {
+    aaudio_handle_t stream;
+    for (int i = 0; i < 2; ++i) {
+        AAudioServiceInterface *service = getAAudioService();
+        if (!service) {
+            return AAUDIO_ERROR_NO_SERVICE;
+        }
+
+        stream = service->openStream(request, configurationOutput);
+
+        if (stream == AAUDIO_ERROR_NO_SERVICE) {
+            dropAAudioService();
+        } else {
+            break;
+        }
+    }
+    return stream;
+}
+
+aaudio_result_t FuzzAAudioClient::closeStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->closeStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::getStreamDescription(aaudio_handle_t streamHandle,
+                                                       AudioEndpointParcelable &parcelable) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->getStreamDescription(streamHandle, parcelable);
+}
+
+aaudio_result_t FuzzAAudioClient::startStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->startStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::pauseStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->pauseStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::stopStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->stopStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::flushStream(aaudio_handle_t streamHandle) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->flushStream(streamHandle);
+}
+
+aaudio_result_t FuzzAAudioClient::registerAudioThread(aaudio_handle_t streamHandle,
+                                                      pid_t clientThreadId,
+                                                      int64_t periodNanoseconds) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds);
+}
+
+aaudio_result_t FuzzAAudioClient::unregisterAudioThread(aaudio_handle_t streamHandle,
+                                                        pid_t clientThreadId) {
+    AAudioServiceInterface *service = getAAudioService();
+    if (!service) {
+        return AAUDIO_ERROR_NO_SERVICE;
+    }
+    return service->unregisterAudioThread(streamHandle, clientThreadId);
+}
+
+class OboeserviceFuzzer {
+   public:
+    OboeserviceFuzzer();
+    ~OboeserviceFuzzer() = default;
+    void process(const uint8_t *data, size_t size);
+
+   private:
+    sp<FuzzAAudioClient> mClient;
+};
+
+OboeserviceFuzzer::OboeserviceFuzzer() {
+    sp<AAudioService> service = new AAudioService();
+    mClient = new FuzzAAudioClient(service);
+}
+
+void OboeserviceFuzzer::process(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    AAudioStreamRequest request;
+    AAudioStreamConfiguration configurationOutput;
+
+    // Initialize stream request
+    request.getConfiguration().setFormat((audio_format_t)(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : kAAudioFormats[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioFormats - 1)]));
+
+    request.setUserId(getuid());
+    request.setProcessId(getpid());
+    request.setInService(fdp.ConsumeBool());
+
+    request.getConfiguration().setDeviceId(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setSampleRate(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setSamplesPerFrame(fdp.ConsumeIntegral<int32_t>());
+    request.getConfiguration().setDirection(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : (fdp.ConsumeBool() ? AAUDIO_DIRECTION_OUTPUT : AAUDIO_DIRECTION_INPUT));
+    request.getConfiguration().setSharingMode(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : (fdp.ConsumeBool() ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED));
+
+    request.getConfiguration().setUsage(
+        fdp.ConsumeBool()
+            ? fdp.ConsumeIntegral<int32_t>()
+            : kAAudioUsages[fdp.ConsumeIntegralInRange<int32_t>(0, kNumAAudioUsages - 1)]);
+    request.getConfiguration().setContentType(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : kAAudioContentTypes[fdp.ConsumeIntegralInRange<int32_t>(
+                                0, kNumAAudioContentTypes - 1)]);
+    request.getConfiguration().setInputPreset(
+        fdp.ConsumeBool() ? fdp.ConsumeIntegral<int32_t>()
+                          : kAAudioInputPresets[fdp.ConsumeIntegralInRange<int32_t>(
+                                0, kNumAAudioInputPresets - 1)]);
+    request.getConfiguration().setPrivacySensitive(fdp.ConsumeBool());
+
+    request.getConfiguration().setBufferCapacity(fdp.ConsumeIntegral<int32_t>());
+
+    aaudio_handle_t stream = mClient->openStream(request, configurationOutput);
+    if (stream < 0) {
+        // invalid request, stream not opened.
+        return;
+    }
+    while (fdp.remaining_bytes()) {
+        AudioEndpointParcelable audioEndpointParcelable;
+        int action = fdp.ConsumeIntegralInRange<int32_t>(0, 4);
+        switch (action) {
+            case 0:
+                mClient->getStreamDescription(stream, audioEndpointParcelable);
+                break;
+            case 1:
+                mClient->startStream(stream);
+                break;
+            case 2:
+                mClient->pauseStream(stream);
+                break;
+            case 3:
+                mClient->stopStream(stream);
+                break;
+            case 4:
+                mClient->flushStream(stream);
+                break;
+        }
+    }
+    mClient->closeStream(stream);
+    assert(mClient->getDeathCount() == 0);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    OboeserviceFuzzer oboeserviceFuzzer;
+    oboeserviceFuzzer.process(data, size);
+    return 0;
+}