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], ¶mData[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;
+}