Add Spatializer support in libAudioHal

Bug: 273373363
Test: atest EffectsFactoryHalInterfaceTest
Change-Id: I68bfc8388dac0f070a58351bc1d8836f8c50fc6c
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index c4841c5..0c0184e 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -69,9 +69,6 @@
                                 void* pReplyData);
 
   private:
-    const aidl::android::media::audio::common::AudioFormatDescription kDefaultFormatDescription = {
-            .type = aidl::android::media::audio::common::AudioFormatType::PCM,
-            .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT};
     const bool mIsProxyEffect;
 
     static constexpr int kDefaultframeCount = 0x100;
@@ -81,13 +78,16 @@
         return pt ? std::to_string(*pt) : "nullptr";
     }
 
-    using AudioChannelLayout = aidl::android::media::audio::common::AudioChannelLayout;
     const aidl::android::media::audio::common::AudioConfig kDefaultAudioConfig = {
             .base = {.sampleRate = 44100,
-                     .channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
-                             AudioChannelLayout::LAYOUT_STEREO),
-                     .format = kDefaultFormatDescription},
+                     .channelMask = aidl::android::media::audio::common::AudioChannelLayout::make<
+                             aidl::android::media::audio::common::AudioChannelLayout::layoutMask>(
+                             aidl::android::media::audio::common::AudioChannelLayout::
+                                     LAYOUT_STEREO),
+                     .format = {.type = aidl::android::media::audio::common::AudioFormatType::PCM,
+                                .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT}},
             .frameCount = kDefaultframeCount};
+
     // command handler map
     typedef status_t (EffectConversionHelperAidl::*CommandHandler)(uint32_t /* cmdSize */,
                                                                    const void* /* pCmdData */,
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
index 49e6827..d1794f0 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
@@ -16,17 +16,17 @@
 
 #include <cstdint>
 #include <cstring>
-#include <optional>
 #define LOG_TAG "AidlConversionSpatializer"
 //#define LOG_NDEBUG 0
 
 #include <aidl/android/hardware/audio/effect/DefaultExtension.h>
 #include <aidl/android/hardware/audio/effect/VendorExtension.h>
 #include <error/expected_utils.h>
-#include <media/AidlConversionNdk.h>
+#include <media/AidlConversionCppNdk.h>
 #include <media/AidlConversionEffect.h>
+#include <media/AidlConversionNdk.h>
+#include <system/audio_effects/aidl_effects_utils.h>
 #include <system/audio_effects/effect_spatializer.h>
-
 #include <utils/Log.h>
 
 #include "AidlConversionSpatializer.h"
@@ -34,38 +34,321 @@
 namespace android {
 namespace effect {
 
-using ::aidl::android::aidl_utils::statusTFromBinderStatus;
-using ::aidl::android::hardware::audio::effect::DefaultExtension;
-using ::aidl::android::hardware::audio::effect::Parameter;
-using ::aidl::android::hardware::audio::effect::VendorExtension;
-using ::android::status_t;
+using aidl::android::getParameterSpecificField;
+using aidl::android::aidl_utils::statusTFromBinderStatus;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::effect::DefaultExtension;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
+using aidl::android::hardware::audio::effect::Spatializer;
+using aidl::android::hardware::audio::effect::VendorExtension;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::HeadTracking;
+using aidl::android::media::audio::common::Spatialization;
+using aidl::android::media::audio::common::toString;
+using android::status_t;
 using utils::EffectParamReader;
 using utils::EffectParamWriter;
 
+bool AidlConversionSpatializer::isSpatializerParameterSupported() {
+    return mIsSpatializerAidlParamSupported.value_or(
+            (mIsSpatializerAidlParamSupported =
+                     [&]() {
+                         ::aidl::android::hardware::audio::effect::Parameter aidlParam;
+                         auto id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+                                                              Spatializer::vendor);
+                         // No range defined in descriptor capability means no Spatializer AIDL
+                         // implementation BAD_VALUE return from getParameter indicates the
+                         // parameter is not supported by HAL
+                         return mDesc.capability.range.getTag() == Range::spatializer &&
+                                mEffect->getParameter(id, &aidlParam).getStatus() !=
+                                        android::BAD_VALUE;
+                     }())
+                    .value());
+}
+
 status_t AidlConversionSpatializer::setParameter(EffectParamReader& param) {
-    Parameter aidlParam = VALUE_OR_RETURN_STATUS(
-            ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+    Parameter aidlParam;
+    if (isSpatializerParameterSupported()) {
+        uint32_t command = 0;
+        if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+            OK != param.readFromParameter(&command)) {
+            ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+            return BAD_VALUE;
+        }
+
+        switch (command) {
+            case SPATIALIZER_PARAM_LEVEL: {
+                Spatialization::Level level = Spatialization::Level::NONE;
+                if (OK != param.readFromValue(&level)) {
+                    ALOGE("%s invalid level value %s", __func__, param.toString().c_str());
+                    return BAD_VALUE;
+                }
+                aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+                                                    spatializationLevel, level);
+                break;
+            }
+            case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+                HeadTracking::Mode mode = HeadTracking::Mode::DISABLED;
+                if (OK != param.readFromValue(&mode)) {
+                    ALOGE("%s invalid mode value %s", __func__, param.toString().c_str());
+                    return BAD_VALUE;
+                }
+                aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingMode,
+                                                    mode);
+                break;
+            }
+            case SPATIALIZER_PARAM_HEAD_TO_STAGE: {
+                const size_t valueSize = param.getValueSize();
+                if (valueSize / sizeof(float) > 6 || valueSize % sizeof(float) != 0) {
+                    ALOGE("%s invalid parameter value size %zu", __func__, valueSize);
+                    return BAD_VALUE;
+                }
+                std::array<float, 6> headToStage = {};
+                for (size_t i = 0; i < valueSize / sizeof(float); i++) {
+                    if (OK != param.readFromValue(&headToStage[i])) {
+                        ALOGE("%s failed to read headToStage from %s", __func__,
+                              param.toString().c_str());
+                        return BAD_VALUE;
+                    }
+                }
+                HeadTracking::SensorData sensorData =
+                        HeadTracking::SensorData::make<HeadTracking::SensorData::headToStage>(
+                                headToStage);
+                aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+                                                    headTrackingSensorData, sensorData);
+                break;
+            }
+            case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+                int32_t modeInt32 = 0;
+                int32_t sensorId = -1;
+                if (OK != param.readFromValue(&modeInt32) || OK != param.readFromValue(&sensorId)) {
+                    ALOGE("%s %d invalid parameter value %s", __func__, __LINE__,
+                          param.toString().c_str());
+                    return BAD_VALUE;
+                }
+
+                const auto mode = static_cast<HeadTracking::ConnectionMode>(modeInt32);
+                if (mode < *ndk::enum_range<HeadTracking::ConnectionMode>().begin() ||
+                    mode > *ndk::enum_range<HeadTracking::ConnectionMode>().end()) {
+                    ALOGE("%s %d invalid mode %d", __func__, __LINE__, modeInt32);
+                    return BAD_VALUE;
+                }
+                aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+                                                    headTrackingConnectionMode, mode);
+                if (status_t status = statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+                    status != OK) {
+                    ALOGE("%s failed to set headTrackingConnectionMode %s", __func__,
+                          toString(mode).c_str());
+                    return status;
+                }
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingSensorId,
+                                                    sensorId);
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+            }
+            default: {
+                ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+                return BAD_VALUE;
+            }
+        }
+    } else {
+        aidlParam = VALUE_OR_RETURN_STATUS(
+                ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+    }
+
+    ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
     return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
 }
 
 status_t AidlConversionSpatializer::getParameter(EffectParamWriter& param) {
-    DefaultExtension defaultExt;
-    // read parameters into DefaultExtension vector<uint8_t>
-    defaultExt.bytes.resize(param.getParameterSize());
-    if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
-        ALOGE("%s invalid param %s", __func__, param.toString().c_str());
-        param.setStatus(BAD_VALUE);
-        return BAD_VALUE;
-    }
+    if (isSpatializerParameterSupported()) {
+        uint32_t command = 0;
+        if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+            OK != param.readFromParameter(&command)) {
+            ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+            return BAD_VALUE;
+        }
 
-    VendorExtension idTag;
-    idTag.extension.setParcelable(defaultExt);
-    Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
-    Parameter aidlParam;
-    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
-    // copy the AIDL extension data back to effect_param_t
-    return VALUE_OR_RETURN_STATUS(
-            ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+        switch (command) {
+            case SPATIALIZER_PARAM_SUPPORTED_LEVELS: {
+                const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+                        mDesc.capability, Spatializer::spatializationLevel);
+                if (!range) {
+                    return BAD_VALUE;
+                }
+                for (const auto level : ::ndk::enum_range<Spatialization::Level>()) {
+                    const auto spatializer =
+                            Spatializer::make<Spatializer::spatializationLevel>(level);
+                    if (spatializer >= range->min && spatializer <= range->max) {
+                        if (status_t status = param.writeToValue(&level); status != OK) {
+                            ALOGI("%s %d: write level %s to value failed %d", __func__, __LINE__,
+                                  toString(level).c_str(), status);
+                            return status;
+                        }
+                    }
+                }
+                return OK;
+            }
+            case SPATIALIZER_PARAM_LEVEL: {
+                Parameter aidlParam;
+                Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+                                                              Spatializer::spatializationLevel);
+                RETURN_STATUS_IF_ERROR(
+                        statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+                const auto level = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+                        aidlParam, Spatializer, spatializer, Spatializer::spatializationLevel,
+                        Spatialization::Level));
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                return param.writeToValue(&level);
+            }
+            case SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED: {
+                const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+                        mDesc.capability, Spatializer::spatializationLevel);
+                if (!range) {
+                    ALOGE("%s %d: range not defined for spatializationLevel", __func__, __LINE__);
+                    return BAD_VALUE;
+                }
+                const auto& nonSupport = Spatializer::make<Spatializer::spatializationLevel>(
+                        Spatialization::Level::NONE);
+                const bool support = (range->min > range->max ||
+                                         (range->min == nonSupport && range->max == nonSupport))
+                                                ? false
+                                                : true;
+                return param.writeToValue(&support);
+            }
+            case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+                Parameter aidlParam;
+                Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+                                                Spatializer::headTrackingMode);
+                RETURN_STATUS_IF_ERROR(
+                        statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+                const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+                        aidlParam, Spatializer, spatializer, Spatializer::headTrackingMode,
+                        HeadTracking::Mode));
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                return param.writeToValue(&mode);
+            }
+            case SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS: {
+                Parameter aidlParam;
+                Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+                                                              Spatializer::supportedChannelLayout);
+                RETURN_STATUS_IF_ERROR(
+                        statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+                const auto& supportedLayouts = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+                        aidlParam, Spatializer, spatializer, Spatializer::supportedChannelLayout,
+                        std::vector<AudioChannelLayout>));
+                for (const auto& layout : supportedLayouts) {
+                    audio_channel_mask_t mask = VALUE_OR_RETURN_STATUS(
+                            ::aidl::android::aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+                                    layout, false /* isInput */));
+                    if (status_t status = param.writeToValue(&mask); status != OK) {
+                        ALOGI("%s %d: write mask %s to value failed %d", __func__, __LINE__,
+                              layout.toString().c_str(), status);
+                        return status;
+                    }
+                }
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                return OK;
+            }
+            case SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES: {
+                const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+                        mDesc.capability, Spatializer::spatializationMode);
+                if (!range) {
+                    return BAD_VALUE;
+                }
+                for (const auto mode : ::ndk::enum_range<Spatialization::Mode>()) {
+                    if (const auto spatializer =
+                                Spatializer::make<Spatializer::spatializationMode>(mode);
+                        spatializer >= range->min && spatializer <= range->max) {
+                        if (status_t status = param.writeToValue(&mode); status != OK) {
+                            ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+                                  toString(mode).c_str(), status);
+                            return status;
+                        }
+                    }
+                }
+                return OK;
+            }
+            case SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION: {
+                const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+                        mDesc.capability, Spatializer::headTrackingConnectionMode);
+                if (!range) {
+                    return BAD_VALUE;
+                }
+                for (const auto mode : ::ndk::enum_range<HeadTracking::ConnectionMode>()) {
+                    if (const auto spatializer =
+                                Spatializer::make<Spatializer::headTrackingConnectionMode>(mode);
+                        spatializer < range->min || spatializer > range->max) {
+                        continue;
+                    }
+                    if (status_t status = param.writeToValue(&mode); status != OK) {
+                        ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+                                toString(mode).c_str(), status);
+                        return status;
+                    }
+                }
+                return OK;
+            }
+            case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+                status_t status = OK;
+                Parameter aidlParam;
+                Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(
+                        Spatializer, spatializerTag, Spatializer::headTrackingConnectionMode);
+                RETURN_STATUS_IF_ERROR(
+                        statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+                const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+                        aidlParam, Spatializer, spatializer,
+                        Spatializer::headTrackingConnectionMode, HeadTracking::ConnectionMode));
+
+                id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+                                                Spatializer::headTrackingSensorId);
+                RETURN_STATUS_IF_ERROR(
+                        statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+                const auto sensorId = VALUE_OR_RETURN_STATUS(
+                        GET_PARAMETER_SPECIFIC_FIELD(aidlParam, Spatializer, spatializer,
+                                                     Spatializer::headTrackingSensorId, int32_t));
+                uint32_t modeInt32 = static_cast<int32_t>(mode);
+                if (status = param.writeToValue(&modeInt32); status != OK) {
+                    ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+                          toString(mode).c_str(), status);
+                    return status;
+                }
+                if (status = param.writeToValue(&sensorId); status != OK) {
+                    ALOGI("%s %d: write sensorId %d to value failed %d", __func__, __LINE__,
+                          sensorId, status);
+                    return status;
+                }
+                ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+                return OK;
+            }
+            default: {
+                ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+                return BAD_VALUE;
+            }
+        }
+    } else {
+        Parameter aidlParam;
+        DefaultExtension defaultExt;
+        // read parameters into DefaultExtension vector<uint8_t>
+        defaultExt.bytes.resize(param.getParameterSize());
+        if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
+            ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+            param.setStatus(BAD_VALUE);
+            return BAD_VALUE;
+        }
+
+        VendorExtension idTag;
+        idTag.extension.setParcelable(defaultExt);
+        Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
+        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+        ALOGI("%s %d: %s", __func__, __LINE__,
+              aidlParam.get<Parameter::specific>().toString().c_str());
+        // copy the AIDL extension data back to effect_param_t
+        return VALUE_OR_RETURN_STATUS(
+                ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+    }
 }
 
 } // namespace effect
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
index 7c60b14..444e5a7 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
@@ -32,6 +32,8 @@
     ~AidlConversionSpatializer() {}
 
   private:
+    std::optional<bool> mIsSpatializerAidlParamSupported;
+    bool isSpatializerParameterSupported();
     status_t setParameter(utils::EffectParamReader& param) override;
     status_t getParameter(utils::EffectParamWriter& param) override;
 };