audio policy: add Spatializer effect control

Add APIs to discover support for audio spatialization function and control a
spatializer output mixer and effect creation to Audio Policy Service.

When supported, a system service can retrieve a ISpatializer
interface to query supported features and set a spatialization mode.

When spatialization is enabled, the corresponding specialized output mixer is
created and the effect applied.

The audio policy manager will attach clients which audio attributes and
format qualifying for spatialization to the specialized output mixer.

Bug: 188502620
Test: make

Change-Id: Ie2734fb9bb3ef9b945431c6c9bd110b1434a79cd
Merged-In: Ie2734fb9bb3ef9b945431c6c9bd110b1434a79cd
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 9c307ff..4ab9b28 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -388,7 +388,9 @@
         "aidl/android/media/AudioProductStrategy.aidl",
         "aidl/android/media/AudioVolumeGroup.aidl",
         "aidl/android/media/DeviceRole.aidl",
+        "aidl/android/media/HeadTrackingMode.aidl",
         "aidl/android/media/SoundTriggerSession.aidl",
+        "aidl/android/media/SpatializationLevel.aidl",
     ],
     imports: [
         "audio_common-aidl",
@@ -459,10 +461,13 @@
     srcs: [
         "aidl/android/media/GetInputForAttrResponse.aidl",
         "aidl/android/media/GetOutputForAttrResponse.aidl",
+        "aidl/android/media/GetSpatializerResponse.aidl",
         "aidl/android/media/Int.aidl",
         "aidl/android/media/RecordClientInfo.aidl",
         "aidl/android/media/IAudioPolicyService.aidl",
         "aidl/android/media/IAudioPolicyServiceClient.aidl",
+        "aidl/android/media/INativeSpatializerCallback.aidl",
+        "aidl/android/media/ISpatializer.aidl",
     ],
     imports: [
         "audio_common-aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 792be31..c7967e5 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -2240,6 +2240,47 @@
     return OK;
 }
 
+status_t AudioSystem::getSpatializer(const sp<media::INativeSpatializerCallback>& callback,
+                                          sp<media::ISpatializer>* spatializer) {
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (spatializer == nullptr) {
+        return BAD_VALUE;
+    }
+    if (aps == 0) {
+        return PERMISSION_DENIED;
+    }
+    media::GetSpatializerResponse response;
+    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+            aps->getSpatializer(callback, &response)));
+
+    *spatializer = response.spatializer;
+    return OK;
+}
+
+status_t AudioSystem::canBeSpatialized(const audio_attributes_t *attr,
+                                    const audio_config_t *config,
+                                    const AudioDeviceTypeAddrVector &devices,
+                                    bool *canBeSpatialized) {
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) {
+        return PERMISSION_DENIED;
+    }
+    audio_attributes_t attributes = attr != nullptr ? *attr : AUDIO_ATTRIBUTES_INITIALIZER;
+    audio_config_t configuration = config != nullptr ? *config : AUDIO_CONFIG_INITIALIZER;
+
+    std::optional<media::AudioAttributesInternal> attrAidl = VALUE_OR_RETURN_STATUS(
+            legacy2aidl_audio_attributes_t_AudioAttributesInternal(attributes));
+    std::optional<media::AudioConfig> configAidl = VALUE_OR_RETURN_STATUS(
+            legacy2aidl_audio_config_t_AudioConfig(configuration));
+    std::vector<media::AudioDevice> devicesAidl = VALUE_OR_RETURN_STATUS(
+            convertContainer<std::vector<media::AudioDevice>>(devices,
+                                                   legacy2aidl_AudioDeviceTypeAddress));
+    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+            aps->canBeSpatialized(attrAidl, configAidl, devicesAidl, canBeSpatialized)));
+    return OK;
+}
+
+
 class CaptureStateListenerImpl : public media::BnCaptureStateListener,
                                  public IBinder::DeathRecipient {
 public:
diff --git a/media/libaudioclient/aidl/android/media/GetSpatializerResponse.aidl b/media/libaudioclient/aidl/android/media/GetSpatializerResponse.aidl
new file mode 100644
index 0000000..25115ac
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/GetSpatializerResponse.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import android.media.ISpatializer;
+
+/**
+ * Used as a return value for IAudioPolicyService.getSpatializer() method
+ * {@hide}
+ */
+ parcelable GetSpatializerResponse {
+    /* The ISpatializer interface if successful, null if not */
+    @nullable ISpatializer spatializer;
+}
diff --git a/media/libaudioclient/aidl/android/media/HeadTrackingMode.aidl b/media/libaudioclient/aidl/android/media/HeadTrackingMode.aidl
new file mode 100644
index 0000000..d6cf410
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/HeadTrackingMode.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+
+/**
+ * The head tracking mode supported by the spatializer stage effect implementation.
+ * Used by methods of the ISpatializer interface.
+ * {@hide}
+ */
+@Backing(type="byte")
+enum HeadTrackingMode {
+    /** Head tracking is active in a mode not listed below (forward compatibility) */
+    OTHER = 0,
+    /** Head tracking is disabled */
+    DISABLED = 1,
+    /** Head tracking is performed relative to the real work environment */
+    RELATIVE_WORLD = 2,
+    /** Head tracking is performed relative to the device's screen */
+    RELATIVE_SCREEN = 3,
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 4c3955a..5f0a1de 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -47,8 +47,10 @@
 import android.media.EffectDescriptor;
 import android.media.GetInputForAttrResponse;
 import android.media.GetOutputForAttrResponse;
+import android.media.GetSpatializerResponse;
 import android.media.IAudioPolicyServiceClient;
 import android.media.ICaptureStateListener;
+import android.media.INativeSpatializerCallback;
 import android.media.Int;
 import android.media.SoundTriggerSession;
 
@@ -348,4 +350,29 @@
                                                     DeviceRole role);
 
     boolean registerSoundTriggerCaptureStateListener(ICaptureStateListener listener);
+
+    /** If a spatializer stage effect is present on the platform, this will return an
+     * ISpatializer interface (see GetSpatializerResponse,aidl) to control this
+     * feature.
+     * If no spatializer stage is present, a null interface is returned.
+     * The INativeSpatializerCallback passed must not be null.
+     * Only one ISpatializer interface can exist at a given time. The native audio policy
+     * service will reject the request if an interface was already acquired and previous owner
+     * did not die or call ISpatializer.release().
+     */
+    GetSpatializerResponse getSpatializer(INativeSpatializerCallback callback);
+
+    /** Queries if some kind of spatialization will be performed if the audio playback context
+     * described by the provided arguments is present.
+     * The context is made of:
+     * - The audio attributes describing the playback use case.
+     * - The audio configuration describing the audio format, channels, sampling rate...
+     * - The devices describing the sink audio device selected for playback.
+     * All arguments are optional and only the specified arguments are used to match against
+     * supported criteria. For instance, supplying no argument will tell if spatialization is
+     * supported or not in general.
+     */
+    boolean canBeSpatialized(in @nullable AudioAttributesInternal attr,
+                             in @nullable AudioConfig config,
+                             in AudioDevice[] devices);
 }
diff --git a/media/libaudioclient/aidl/android/media/INativeSpatializerCallback.aidl b/media/libaudioclient/aidl/android/media/INativeSpatializerCallback.aidl
new file mode 100644
index 0000000..f34df05
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/INativeSpatializerCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import android.media.SpatializationLevel;
+
+/**
+ * The INativeSpatializerCallback interface is a callback associated to the
+ * ISpatializer interface. The callback is used by the spatializer stage
+ * implementation in native audio server to communicate stage changes to the
+ * client controlling the spatializer with the ISpatializer interface.
+ * {@hide}
+ */
+interface INativeSpatializerCallback {
+    /** Called when the spatialization level applied by the vitualizer stage changes
+     * (e.g. when the spatializer is enabled or disabled)
+     */
+    oneway void onLevelChanged(SpatializationLevel level);
+}
diff --git a/media/libaudioclient/aidl/android/media/ISpatializer.aidl b/media/libaudioclient/aidl/android/media/ISpatializer.aidl
new file mode 100644
index 0000000..212d8fe
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/ISpatializer.aidl
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import android.media.HeadTrackingMode;
+import android.media.SpatializationLevel;
+
+/**
+ * The ISpatializer interface is used to control the native audio service implementation
+ * of the spatializer stage with headtracking when present on a platform.
+ * It is intended for exclusive use by the java AudioService running in system_server.
+ * It provides APIs to discover the feature availability and options as well as control and report
+ * the active state and modes of the spatializer and head tracking effect.
+ * {@hide}
+ */
+interface ISpatializer {
+    /** Releases a ISpatializer interface previously acquired. */
+    void release();
+
+    /** Reports the list of supported spatialization levels (see SpatializationLevel.aidl).
+     * The list should never be empty if an ISpatializer interface was successfully
+     * retrieved with IAudioPolicyService.getSpatializer().
+     */
+    SpatializationLevel[] getSupportedLevels();
+
+    /** Selects the desired spatialization level (see SpatializationLevel.aidl). Selecting a level
+     * different from SpatializationLevel.NONE with create the specialized multichannel output
+     * mixer, create and enable the spatializer effect and let the audio policy attach eligible
+     * AudioTrack to this output stream.
+     */
+    void setLevel(SpatializationLevel level);
+
+    /** Gets the selected spatialization level (see SpatializationLevel.aidl) */
+    SpatializationLevel getLevel();
+
+    /** Reports the list of supported head tracking modes (see HeadTrackingMode.aidl). The list can
+     * be empty if the spatializer implementation does not support head tracking or if no
+     * head tracking device is connected.
+     */
+    HeadTrackingMode[] getSupportedHeadTrackingModes();
+
+    /** Selects the desired head tracking mode (see HeadTrackingMode.aidl) */
+    void setDesiredHeadTrackingMode(HeadTrackingMode mode);
+
+    /** Gets the actual head tracking mode. Can be different from the desired mode if conditions to
+     * enable the desired mode are not met (e.g if the head tracking device was removed)
+     */
+    HeadTrackingMode getActualHeadTrackingMode();
+
+    /** Reset the head tracking algorithm to consider current head pose as neutral */
+    void recenterHeadtracker();
+    /** Set the screen to stage transform to use by the head tracking algorithm */
+    void setGlobalTransform(in float[] screenToStage);
+}
diff --git a/media/libaudioclient/aidl/android/media/SpatializationLevel.aidl b/media/libaudioclient/aidl/android/media/SpatializationLevel.aidl
new file mode 100644
index 0000000..cef42bb
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/SpatializationLevel.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+/**
+ * The spatialization level or mode supported by the spatializer stage effect implementation.
+ * Used by methods of the ISpatializer interface.
+ * {@hide}
+ */
+@Backing(type="byte")
+enum SpatializationLevel {
+    /** Spatialization is disabled. */
+    NONE = 0,
+    /** The spatializer accepts audio with positional multichannel masks (e.g 5.1). */
+    SPATIALIZER_MULTICHANNEL = 1,
+    /** The spatializer accepts audio made of a channel bed of positional multichannels (e.g 5.1)
+     * and audio objects positioned independently via meta data.
+     */
+    SPATIALIZER_MCHAN_BED_PLUS_OBJECTS = 2,
+}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 9187de1..869bd6e 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -22,6 +22,8 @@
 #include <android/media/AudioVibratorInfo.h>
 #include <android/media/BnAudioFlingerClient.h>
 #include <android/media/BnAudioPolicyServiceClient.h>
+#include <android/media/INativeSpatializerCallback.h>
+#include <android/media/ISpatializer.h>
 #include <android/content/AttributionSourceState.h>
 #include <media/AidlConversionUtil.h>
 #include <media/AudioDeviceTypeAddr.h>
@@ -488,6 +490,49 @@
     static status_t getDeviceForStrategy(product_strategy_t strategy,
             AudioDeviceTypeAddr &device);
 
+
+    /**
+     * If a spatializer stage effect is present on the platform, this will return an
+     * ISpatializer interface to control this feature.
+     * If no spatializer stage is present, a null interface is returned.
+     * The INativeSpatializerCallback passed must not be null.
+     * Only one ISpatializer interface can exist at a given time. The native audio policy
+     * service will reject the request if an interface was already acquired and previous owner
+     * did not die or call ISpatializer.release().
+     * @param callback in: the callback to receive state updates if the ISpatializer
+     *        interface is acquired.
+     * @param spatializer out: the ISpatializer interface made available to control the
+     *        platform spatializer
+     * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, PERMISSION_DENIED, BAD_VALUE
+     *         in case of error.
+     */
+    static status_t getSpatializer(const sp<media::INativeSpatializerCallback>& callback,
+                                        sp<media::ISpatializer>* spatializer);
+
+    /**
+     * Queries if some kind of spatialization will be performed if the audio playback context
+     * described by the provided arguments is present.
+     * The context is made of:
+     * - The audio attributes describing the playback use case.
+     * - The audio configuration describing the audio format, channels, sampling rate ...
+     * - The devices describing the sink audio device selected for playback.
+     * All arguments are optional and only the specified arguments are used to match against
+     * supported criteria. For instance, supplying no argument will tell if spatialization is
+     * supported or not in general.
+     * @param attr audio attributes describing the playback use case
+     * @param config audio configuration describing the audio format, channels, sampling rate...
+     * @param devices the sink audio device selected for playback
+     * @param canBeSpatialized out: true if spatialization is enabled for this context,
+     *        false otherwise
+     * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE
+     *         in case of error.
+     */
+    static status_t canBeSpatialized(const audio_attributes_t *attr,
+                                     const audio_config_t *config,
+                                     const AudioDeviceTypeAddrVector &devices,
+                                     bool *canBeSpatialized);
+
+
     // A listener for capture state changes.
     class CaptureStateListener : public RefBase {
     public:
@@ -500,11 +545,11 @@
         virtual ~CaptureStateListener() = default;
     };
 
-    // Regiseters a listener for sound trigger capture state changes.
+    // Registers a listener for sound trigger capture state changes.
     // There may only be one such listener registered at any point.
-    // The listener onStateChanged() method will be invoked sychronously from
+    // The listener onStateChanged() method will be invoked synchronously from
     // this call with the initial value.
-    // The listener onServiceDied() method will be invoked sychronously from
+    // The listener onServiceDied() method will be invoked synchronously from
     // this call if initial attempt to register failed.
     // If the audio policy service cannot be reached, this method will return
     // PERMISSION_DENIED and will not invoke the callback, otherwise, it will
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 6b11d9a..4078278 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -333,6 +333,50 @@
     virtual status_t getDevicesForRoleAndCapturePreset(audio_source_t audioSource,
                                                        device_role_t role,
                                                        AudioDeviceTypeAddrVector &devices) = 0;
+
+    /**
+     * Queries if some kind of spatialization will be performed if the audio playback context
+     * described by the provided arguments is present.
+     * The context is made of:
+     * - The audio attributes describing the playback use case.
+     * - The audio configuration describing the audio format, channels, sampling rate ...
+     * - The devices describing the sink audio device selected for playback.
+     * All arguments are optional and only the specified arguments are used to match against
+     * supported criteria. For instance, supplying no argument will tell if spatialization is
+     * supported or not in general.
+     * @param attr audio attributes describing the playback use case
+     * @param config audio configuration describing the audio format, channels, sampling rate...
+     * @param devices the sink audio device selected for playback
+     * @return true if spatialization is enabled for this context,
+     *        false otherwise
+     */
+     virtual bool canBeSpatialized(const audio_attributes_t *attr,
+                                  const audio_config_t *config,
+                                  const AudioDeviceTypeAddrVector &devices) const = 0;
+
+    /**
+     * Opens a specialized spatializer output if supported by the platform.
+     * If several spatializer output profiles exist, the one supporting the sink device
+     * corresponding to the provided audio attributes will be selected.
+     * Only one spatializer output stream can be opened at a time and an error is returned
+     * if one already exists.
+     * @param config audio format, channel mask and sampling rate to be used as the mixer
+     *        configuration for the spatializer mixer created.
+     * @param attr audio attributes describing the playback use case that will drive the
+     *        sink device selection
+     * @param output the IO handle of the output opened
+     * @return NO_ERROR if an output was opened, INVALID_OPERATION or BAD_VALUE otherwise
+     */
+    virtual status_t getSpatializerOutput(const audio_config_base_t *config,
+                                            const audio_attributes_t *attr,
+                                            audio_io_handle_t *output) = 0;
+
+    /**
+     * Closes a previously opened specialized spatializer output.
+     * @param output the IO handle of the output to close.
+     * @return NO_ERROR if an output was closed, INVALID_OPERATION or BAD_VALUE otherwise
+     */
+    virtual status_t releaseSpatializerOutput(audio_io_handle_t output) = 0;
 };
 
 
diff --git a/services/audiopolicy/service/Android.bp b/services/audiopolicy/service/Android.bp
index 454c020..5ffddc2 100644
--- a/services/audiopolicy/service/Android.bp
+++ b/services/audiopolicy/service/Android.bp
@@ -16,6 +16,7 @@
         "AudioPolicyInterfaceImpl.cpp",
         "AudioPolicyService.cpp",
         "CaptureStateNotifier.cpp",
+        "Spatializer.cpp",
     ],
 
     include_dirs: [
@@ -27,6 +28,7 @@
         "libaudioclient",
         "libaudioclient_aidl_conversion",
         "libaudiofoundation",
+        "libaudiohal",
         "libaudiopolicy",
         "libaudiopolicymanagerdefault",
         "libaudioutils",
@@ -40,6 +42,7 @@
         "libmediautils",
         "libpermission",
         "libsensorprivacy",
+        "libshmemcompat",
         "libutils",
         "audioclient-types-aidl-cpp",
         "audioflinger-aidl-cpp",
@@ -55,6 +58,7 @@
     ],
 
     header_libs: [
+        "libaudiohal_headers",
         "libaudiopolicycommon",
         "libaudiopolicyengine_interface_headers",
         "libaudiopolicymanager_interface_headers",
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 77223b6..58359be 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -2205,4 +2205,41 @@
     return Status::ok();
 }
 
+Status AudioPolicyService::getSpatializer(
+        const sp<media::INativeSpatializerCallback>& callback,
+        media::GetSpatializerResponse* _aidl_return) {
+    _aidl_return->spatializer = nullptr;
+    LOG_ALWAYS_FATAL_IF(callback == nullptr);
+    RETURN_IF_BINDER_ERROR(binderStatusFromStatusT(mSpatializer->registerCallback(callback)));
+    _aidl_return->spatializer = mSpatializer;
+    return Status::ok();
+}
+
+Status AudioPolicyService::canBeSpatialized(
+        const std::optional<media::AudioAttributesInternal>& attrAidl,
+        const std::optional<media::AudioConfig>& configAidl,
+        const std::vector<media::AudioDevice>& devicesAidl,
+        bool* _aidl_return) {
+    if (mAudioPolicyManager == nullptr) {
+        return binderStatusFromStatusT(NO_INIT);
+    }
+    audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+    if (attrAidl.has_value()) {
+        attr = VALUE_OR_RETURN_BINDER_STATUS(
+            aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl.value()));
+    }
+    audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+    if (configAidl.has_value()) {
+        config = VALUE_OR_RETURN_BINDER_STATUS(
+                                    aidl2legacy_AudioConfig_audio_config_t(configAidl.value()));
+    }
+    AudioDeviceTypeAddrVector devices = VALUE_OR_RETURN_BINDER_STATUS(
+            convertContainer<AudioDeviceTypeAddrVector>(devicesAidl,
+                                                        aidl2legacy_AudioDeviceTypeAddress));
+
+    Mutex::Autolock _l(mLock);
+    *_aidl_return = mAudioPolicyManager->canBeSpatialized(&attr, &config, devices);
+    return Status::ok();
+}
+
 } // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 3c757b3..56c472b 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -141,6 +141,13 @@
     uidPolicy->registerSelf();
     sensorPrivacyPolicy->registerSelf();
 
+    // Create spatializer if supported
+    const audio_attributes_t attr = attributes_initializer(AUDIO_USAGE_MEDIA);
+    AudioDeviceTypeAddrVector devices;
+    bool hasSpatializer = mAudioPolicyManager->canBeSpatialized(&attr, nullptr, devices);
+    if (hasSpatializer) {
+        mSpatializer = Spatializer::create(this);
+    }
     AudioSystem::audioPolicyReady();
 }
 
@@ -356,6 +363,49 @@
     }
 }
 
+void AudioPolicyService::onCheckSpatializer()
+{
+    Mutex::Autolock _l(mLock);
+    mOutputCommandThread->checkSpatializerCommand();
+}
+
+void AudioPolicyService::doOnCheckSpatializer()
+{
+    sp<Spatializer> spatializer;
+    {
+        Mutex::Autolock _l(mLock);
+        spatializer = mSpatializer;
+
+        if (spatializer != nullptr) {
+            audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
+            if (spatializer->getLevel() != media::SpatializationLevel::NONE
+                && spatializer->getOutput() == AUDIO_IO_HANDLE_NONE) {
+                const audio_attributes_t attr = attributes_initializer(AUDIO_USAGE_MEDIA);
+                audio_config_base_t config = spatializer->getAudioInConfig();
+                status_t status =
+                        mAudioPolicyManager->getSpatializerOutput(&config, &attr, &output);
+                if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
+                    return;
+                }
+                mLock.unlock();
+                status = spatializer->attachOutput(output);
+                mLock.lock();
+                if (status != NO_ERROR) {
+                    mAudioPolicyManager->releaseSpatializerOutput(output);
+                }
+            } else if (spatializer->getLevel() == media::SpatializationLevel::NONE
+                                   && spatializer->getOutput() != AUDIO_IO_HANDLE_NONE) {
+                mLock.unlock();
+                output = spatializer->detachOutput();
+                mLock.lock();
+                if (output != AUDIO_IO_HANDLE_NONE) {
+                    mAudioPolicyManager->releaseSpatializerOutput(output);
+                }
+            }
+        }
+    }
+}
+
 status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
                                                 audio_patch_handle_t *handle,
                                                 int delayMs)
@@ -993,7 +1043,8 @@
         case TRANSACTION_addDevicesRoleForCapturePreset:
         case TRANSACTION_removeDevicesRoleForCapturePreset:
         case TRANSACTION_clearDevicesRoleForCapturePreset:
-        case TRANSACTION_getDevicesForRoleAndCapturePreset: {
+        case TRANSACTION_getDevicesForRoleAndCapturePreset:
+        case TRANSACTION_getSpatializer: {
             if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
                 ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
                       __func__, code, IPCThreadState::self()->getCallingPid(),
@@ -1767,6 +1818,17 @@
                     mLock.lock();
                     } break;
 
+                case CHECK_SPATIALIZER: {
+                    ALOGV("AudioCommandThread() processing updateUID states");
+                    svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnCheckSpatializer();
+                    mLock.lock();
+                    } break;
+
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -2078,6 +2140,14 @@
     sendCommand(command);
 }
 
+void AudioPolicyService::AudioCommandThread::checkSpatializerCommand()
+{
+    sp<AudioCommand>command = new AudioCommand();
+    command->mCommand = CHECK_SPATIALIZER;
+    ALOGV("AudioCommandThread() adding check spatializer");
+    sendCommand(command);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index b03e35e..b897a44 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -19,6 +19,7 @@
 #define ANDROID_AUDIOPOLICYSERVICE_H
 
 #include <android/media/BnAudioPolicyService.h>
+#include <android/media/GetSpatializerResponse.h>
 #include <android-base/thread_annotations.h>
 #include <cutils/misc.h>
 #include <cutils/config_utils.h>
@@ -38,6 +39,7 @@
 #include <mediautils/ServiceUtilities.h>
 #include "AudioPolicyEffects.h"
 #include "CaptureStateNotifier.h"
+#include "Spatializer.h"
 #include <AudioPolicyInterface.h>
 #include <android/hardware/BnSensorPrivacyListener.h>
 #include <android/content/AttributionSourceState.h>
@@ -53,7 +55,8 @@
 class AudioPolicyService :
     public BinderService<AudioPolicyService>,
     public media::BnAudioPolicyService,
-    public IBinder::DeathRecipient
+    public IBinder::DeathRecipient,
+    public SpatializerPolicyCallback
 {
     friend class BinderService<AudioPolicyService>;
 
@@ -243,11 +246,15 @@
     binder::Status registerSoundTriggerCaptureStateListener(
             const sp<media::ICaptureStateListener>& listener, bool* _aidl_return) override;
 
-    virtual     status_t    onTransact(
-                                uint32_t code,
-                                const Parcel& data,
-                                Parcel* reply,
-                                uint32_t flags);
+    binder::Status getSpatializer(const sp<media::INativeSpatializerCallback>& callback,
+            media::GetSpatializerResponse* _aidl_return) override;
+    binder::Status canBeSpatialized(
+            const std::optional<media::AudioAttributesInternal>& attr,
+            const std::optional<media::AudioConfig>& config,
+            const std::vector<media::AudioDevice>& devices,
+            bool* _aidl_return) override;
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
 
     // IBinder::DeathRecipient
     virtual     void        binderDied(const wp<IBinder>& who);
@@ -313,6 +320,15 @@
     void onRoutingUpdated();
     void doOnRoutingUpdated();
 
+    /**
+     * Spatializer SpatializerPolicyCallback implementation.
+     * onCheckSpatializer() sends an event on mOutputCommandThread which executes
+     * doOnCheckSpatializer() to check if a Spatializer output must be opened or closed
+     * by audio policy manager and attach/detach the spatializer effect accordingly.
+     */
+    void onCheckSpatializer() override;
+    void doOnCheckSpatializer();
+
     void setEffectSuspended(int effectId,
                             audio_session_t sessionId,
                             bool suspended);
@@ -483,7 +499,8 @@
             SET_EFFECT_SUSPENDED,
             AUDIO_MODULES_UPDATE,
             ROUTING_UPDATED,
-            UPDATE_UID_STATES
+            UPDATE_UID_STATES,
+            CHECK_SPATIALIZER
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -532,6 +549,7 @@
                     void        audioModulesUpdateCommand();
                     void        routingChangedCommand();
                     void        updateUidStatesCommand();
+                    void        checkSpatializerCommand();
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
     private:
         class AudioCommandData;
@@ -986,6 +1004,8 @@
 
     CaptureStateNotifier mCaptureStateNotifier;
 
+    sp<Spatializer> mSpatializer;
+
     void *mLibraryHandle = nullptr;
     CreateAudioPolicyManagerInstance mCreateAudioPolicyManager;
     DestroyAudioPolicyManagerInstance mDestroyAudioPolicyManager;
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
new file mode 100644
index 0000000..3383515
--- /dev/null
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -0,0 +1,378 @@
+/*
+**
+** 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.
+*/
+
+
+#define LOG_TAG "Spatializer"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android/content/AttributionSourceState.h>
+#include <audio_utils/fixedfft.h>
+#include <cutils/bitops.h>
+#include <media/ShmemCompat.h>
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+#include <mediautils/ServiceUtilities.h>
+#include <utils/Thread.h>
+
+#include "Spatializer.h"
+
+namespace android {
+
+using aidl_utils::statusTFromBinderStatus;
+using aidl_utils::binderStatusFromStatusT;
+using android::content::AttributionSourceState;
+using binder::Status;
+using media::HeadTrackingMode;
+using media::SpatializationLevel;
+
+#define VALUE_OR_RETURN_BINDER_STATUS(x) \
+    ({ auto _tmp = (x); \
+       if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
+       std::move(_tmp.value()); })
+
+#define RETURN_IF_BINDER_ERROR(x)      \
+    {                                  \
+        binder::Status _tmp = (x);     \
+        if (!_tmp.isOk()) return _tmp; \
+    }
+
+// ---------------------------------------------------------------------------
+
+sp<Spatializer> Spatializer::create(SpatializerPolicyCallback *callback) {
+    sp<Spatializer> spatializer;
+
+    sp<EffectsFactoryHalInterface> effectsFactoryHal = EffectsFactoryHalInterface::create();
+    if (effectsFactoryHal == nullptr) {
+        ALOGW("%s failed to create effect factory interface", __func__);
+        return spatializer;
+    }
+
+    std::vector<effect_descriptor_t> descriptors;
+    status_t status =
+            effectsFactoryHal->getDescriptors(FX_IID_VIRTUALIZER_STAGE, &descriptors);
+    if (status != NO_ERROR) {
+        ALOGW("%s failed to get spatializer descriptor, error %d", __func__, status);
+        return spatializer;
+    }
+    ALOG_ASSERT(!descriptors.empty(),
+            "%s getDescriptors() returned no error but empty list", __func__);
+
+    //TODO: get supported spatialization modes from FX engine or descriptor
+
+    sp<EffectHalInterface> effect;
+    status = effectsFactoryHal->createEffect(&descriptors[0].uuid, AUDIO_SESSION_OUTPUT_STAGE,
+            AUDIO_IO_HANDLE_NONE, AUDIO_PORT_HANDLE_NONE, &effect);
+    ALOGI("%s FX create status %d effect %p", __func__, status, effect.get());
+
+    if (status == NO_ERROR && effect != nullptr) {
+        spatializer = new Spatializer(descriptors[0], callback);
+        // TODO: Read supported config from engine
+        audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
+        config.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+        spatializer->setAudioInConfig(config);
+    }
+
+    return spatializer;
+}
+
+Spatializer::Spatializer(effect_descriptor_t engineDescriptor,
+                                   SpatializerPolicyCallback *callback)
+    : mEngineDescriptor(engineDescriptor), mPolicyCallback(callback) {
+    ALOGV("%s", __func__);
+}
+
+Spatializer::~Spatializer() {
+    ALOGV("%s", __func__);
+}
+
+status_t Spatializer::registerCallback(
+        const sp<media::INativeSpatializerCallback>& callback) {
+    Mutex::Autolock _l(mLock);
+    if (callback == nullptr) {
+        return BAD_VALUE;
+    }
+
+    sp<IBinder> binder = IInterface::asBinder(callback);
+    status_t status = binder->linkToDeath(this);
+    if (status == NO_ERROR) {
+        mSpatializerCallback = callback;
+    }
+    ALOGV("%s status %d", __func__, status);
+    return status;
+}
+
+// IBinder::DeathRecipient
+void Spatializer::binderDied(__unused const wp<IBinder> &who) {
+    {
+        Mutex::Autolock _l(mLock);
+        mLevel = SpatializationLevel::NONE;
+        mSpatializerCallback.clear();
+    }
+    ALOGV("%s", __func__);
+    mPolicyCallback->onCheckSpatializer();
+}
+
+// ISpatializer
+Status Spatializer::getSupportedLevels(std::vector<SpatializationLevel> *levels) {
+    ALOGV("%s", __func__);
+    if (levels == nullptr) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    //TODO: get this from engine
+    levels->push_back(SpatializationLevel::NONE);
+    levels->push_back(SpatializationLevel::SPATIALIZER_MULTICHANNEL);
+    return Status::ok();
+}
+
+Status Spatializer::setLevel(media::SpatializationLevel level) {
+    ALOGV("%s level %d", __func__, (int)level);
+    if (level != SpatializationLevel::NONE
+            && level != SpatializationLevel::SPATIALIZER_MULTICHANNEL) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    sp<media::INativeSpatializerCallback> callback;
+    bool levelChanged = false;
+    {
+        Mutex::Autolock _l(mLock);
+        levelChanged = mLevel != level;
+        mLevel = level;
+        callback = mSpatializerCallback;
+    }
+
+    if (levelChanged) {
+        mPolicyCallback->onCheckSpatializer();
+        if (callback != nullptr) {
+            callback->onLevelChanged(level);
+        }
+    }
+    return Status::ok();
+}
+
+Status Spatializer::getLevel(media::SpatializationLevel *level) {
+    if (level == nullptr) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    Mutex::Autolock _l(mLock);
+    *level = mLevel;
+    ALOGV("%s level %d", __func__, (int)*level);
+    return Status::ok();
+}
+
+Status Spatializer::getSupportedHeadTrackingModes(
+        std::vector<media::HeadTrackingMode>* modes) {
+    ALOGV("%s", __func__);
+    if (modes == nullptr) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    //TODO: get this from:
+    // - The engine capabilities
+    // - If a head tracking sensor is registered and linked to a connected audio device
+    // - if we have indications on the screen orientation
+    modes->push_back(HeadTrackingMode::RELATIVE_WORLD);
+    return Status::ok();
+}
+
+Status Spatializer::setDesiredHeadTrackingMode(media::HeadTrackingMode mode) {
+    ALOGV("%s level %d", __func__, (int)mode);
+    if (mode != HeadTrackingMode::DISABLED
+            && mode != HeadTrackingMode::RELATIVE_WORLD) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    {
+        Mutex::Autolock _l(mLock);
+        mHeadTrackingMode = mode;
+    }
+    return Status::ok();
+}
+
+Status Spatializer::getActualHeadTrackingMode(media::HeadTrackingMode *mode) {
+    if (mode == nullptr) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    Mutex::Autolock _l(mLock);
+    *mode = mHeadTrackingMode;
+    ALOGV("%s mode %d", __func__, (int)*mode);
+    return Status::ok();
+}
+
+Status Spatializer::recenterHeadtracker() {
+    return Status::ok();
+}
+
+Status Spatializer::setGlobalTransform(const std::vector<float>& screenToStage) {
+    Mutex::Autolock _l(mLock);
+    mScreenToStageTransform = screenToStage;
+    ALOGV("%s", __func__);
+    return Status::ok();
+}
+
+Status Spatializer::release() {
+    ALOGV("%s", __func__);
+    bool levelChanged = false;
+    {
+        Mutex::Autolock _l(mLock);
+        if (mSpatializerCallback == nullptr) {
+            return binderStatusFromStatusT(INVALID_OPERATION);
+        }
+
+        sp<IBinder> binder = IInterface::asBinder(mSpatializerCallback);
+        binder->unlinkToDeath(this);
+        mSpatializerCallback.clear();
+
+        levelChanged = mLevel != SpatializationLevel::NONE;
+        mLevel = SpatializationLevel::NONE;
+    }
+
+    if (levelChanged) {
+        mPolicyCallback->onCheckSpatializer();
+    }
+    return Status::ok();
+}
+
+status_t Spatializer::attachOutput(audio_io_handle_t output) {
+    Mutex::Autolock _l(mLock);
+    ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
+    if (mOutput != AUDIO_IO_HANDLE_NONE) {
+        LOG_ALWAYS_FATAL_IF(mEngine != nullptr, "%s output set without FX engine", __func__);
+        // remove FX instance
+        mEngine->setEnabled(false);
+        mEngine.clear();
+    }
+    // create FX instance on output
+    AttributionSourceState attributionSource = AttributionSourceState();
+    mEngine = new AudioEffect(attributionSource);
+    mEngine->set(nullptr, &mEngineDescriptor.uuid, 0,
+                 Spatializer::engineCallback /* cbf */, this /* user */,
+                 AUDIO_SESSION_OUTPUT_STAGE, output,
+                 {} /* device */, false /* probe */, true /* notifyFramesProcessed */);
+    status_t status = mEngine->initCheck();
+    ALOGV("%s mEngine create status %d", __func__, (int)status);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    mEngine->setEnabled(true);
+    mOutput = output;
+    return NO_ERROR;
+}
+
+audio_io_handle_t Spatializer::detachOutput() {
+    Mutex::Autolock _l(mLock);
+    ALOGV("%s mOutput %d", __func__, (int)mOutput);
+    if (mOutput == AUDIO_IO_HANDLE_NONE) {
+        return AUDIO_IO_HANDLE_NONE;
+    }
+    // remove FX instance
+    mEngine->setEnabled(false);
+    mEngine.clear();
+    audio_io_handle_t output = mOutput;
+    mOutput = AUDIO_IO_HANDLE_NONE;
+    return output;
+}
+
+void Spatializer::engineCallback(int32_t event, void *user, void *info) {
+
+    if (user == nullptr) {
+        return;
+    }
+    const Spatializer * const me = reinterpret_cast<Spatializer *>(user);
+    switch (event) {
+        case AudioEffect::EVENT_FRAMES_PROCESSED: {
+            int frames = info == nullptr ? 0 : *(int *)info;
+            ALOGD("%s frames processed %d for me %p", __func__, frames, me);
+            } break;
+        default:
+            ALOGD("%s event %d", __func__, event);
+            break;
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+Spatializer::EffectClient::EffectClient(const sp<media::IEffectClient>& effectClient,
+             Spatializer& parent)
+             : BnEffect(),
+             mEffectClient(effectClient), mParent(parent) {
+}
+
+Spatializer::EffectClient::~EffectClient() {
+}
+
+// IEffect
+
+#define RETURN(code) \
+  *_aidl_return = (code); \
+  return Status::ok();
+
+// Write a POD value into a vector of bytes (clears the previous buffer
+// content).
+template<typename T>
+void writeToBuffer(const T& value, std::vector<uint8_t>* buffer) {
+    buffer->clear();
+    appendToBuffer(value, buffer);
+}
+
+Status Spatializer::EffectClient::enable(int32_t* _aidl_return) {
+    RETURN(OK);
+}
+
+Status Spatializer::EffectClient::disable(int32_t* _aidl_return) {
+    RETURN(OK);
+}
+
+Status Spatializer::EffectClient::command(int32_t cmdCode,
+                                const std::vector<uint8_t>& cmdData __unused,
+                                int32_t maxResponseSize __unused,
+                                std::vector<uint8_t>* response __unused,
+                                int32_t* _aidl_return) {
+
+    // reject commands reserved for internal use by audio framework if coming from outside
+    // of audioserver
+    switch(cmdCode) {
+        case EFFECT_CMD_ENABLE:
+        case EFFECT_CMD_DISABLE:
+        case EFFECT_CMD_SET_PARAM_DEFERRED:
+        case EFFECT_CMD_SET_PARAM_COMMIT:
+            RETURN(BAD_VALUE);
+        case EFFECT_CMD_SET_PARAM:
+        case EFFECT_CMD_GET_PARAM:
+            break;
+        default:
+            if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY) {
+                break;
+            }
+            android_errorWriteLog(0x534e4554, "62019992");
+            RETURN(BAD_VALUE);
+    }
+    (void)mParent;
+    RETURN(OK);
+}
+
+Status Spatializer::EffectClient::disconnect() {
+    mDisconnected = true;
+    return Status::ok();
+}
+
+Status Spatializer::EffectClient::getCblk(media::SharedFileRegion* _aidl_return) {
+    LOG_ALWAYS_FATAL_IF(!convertIMemoryToSharedFileRegion(mCblkMemory, _aidl_return));
+    return Status::ok();
+}
+
+} // namespace android
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
new file mode 100644
index 0000000..b177953
--- /dev/null
+++ b/services/audiopolicy/service/Spatializer.h
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_SPATIALIZER_H
+#define ANDROID_MEDIA_SPATIALIZER_H
+
+#include <android/media/BnEffect.h>
+#include <android/media/BnSpatializer.h>
+#include <android/media/HeadTrackingMode.h>
+#include <android/media/SpatializationLevel.h>
+
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_virtualizer_stage.h>
+
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+
+/**
+ * A callback interface from the Spatializer object or its parent AudioPolicyService.
+ * This is implemented by the audio policy service hosting the Spatializer to perform
+ * actions needed when a state change inside the Spatializer requires some audio system
+ * changes that cannot be performed by the Spatializer. For instance opening or closing a
+ * spatializer output stream when the spatializer is enabled or disabled
+ */
+class SpatializerPolicyCallback {
+public:
+    /** Called when a stage change occurs that requires the parent audio policy service to take
+     * some action.
+     */
+    virtual void onCheckSpatializer() = 0;
+
+    virtual ~SpatializerPolicyCallback() = default;
+};
+/**
+ * The Spatializer class implements all functional controlling the multichannel spatializer
+ * with head tracking implementation in the native audio service: audio policy and audio flinger.
+ * It presents an AIDL interface available to the java audio service to discover the availability
+ * of the feature and options, control its state and register an active head tracking sensor.
+ * It maintains the current state of the platform spatializer and applies the stored parameters
+ * when the spatializer engine is created and enabled.
+ * Based on the requested spatializer level, it will request the creation of a specialized output
+ * mixer to the audio policy service which will in turn notify the Spatializer of the output
+ * stream on which a spatializer engine should be created, configured and enabled.
+ * The spatializer also hosts the head tracking management logic. This logic receives the
+ * desired head tracking mode and selected head tracking sensor, registers a sensor event listener
+ * and derives the compounded head pose information to the spatializer engine.
+ *
+ * Workflow:
+ * - Initialization: when the audio policy service starts, it checks if a spatializer effect
+ * engine exists and if the audio policy manager reports a dedicated spatializer output profile.
+ * If both conditions are met, a Spatializer object is created
+ * - Capabilities discovery: AudioService will call AudioSystem::canBeSpatialized() and if true,
+ * acquire an ISpatializer interface with AudioSystem::getSpatializer(). This interface
+ * will be used to query the implementation capabilities and configure the spatializer.
+ * - Enabling: when ISpatializer::setLevel() sets a level different from NONE the spatializer
+ * is considered enabled. The audio policy callback onCheckSpatializer() is called. This
+ * triggers a request to audio policy manager to open a spatialization output stream and a
+ * spatializer mixer is created in audio flinger. When an output is returned by audio policy
+ * manager, Spatializer::attachOutput() is called which creates and enables the spatializer
+ * stage engine on the specified output.
+ * - Disabling: when the spatialization level is set to NONE, the spatializer is considered
+ * disabled. The audio policy callback onCheckSpatializer() is called. This triggers a call
+ * to Spatializer::detachOutput() and the spatializer engine is released. Then a request is
+ * made to audio policy manager to release and close the spatializer output stream and the
+ * spatializer mixer thread is destroyed.
+ */
+class Spatializer : public media::BnSpatializer, public IBinder::DeathRecipient {
+public:
+
+    static sp<Spatializer> create(SpatializerPolicyCallback *callback);
+
+           ~Spatializer() override;
+
+    /** ISpatializer, see ISpatializer.aidl */
+    binder::Status release() override;
+    binder::Status getSupportedLevels(std::vector<media::SpatializationLevel>* levels) override;
+    binder::Status setLevel(media::SpatializationLevel level) override;
+    binder::Status getLevel(media::SpatializationLevel *level) override;
+    binder::Status getSupportedHeadTrackingModes(
+            std::vector<media::HeadTrackingMode>* modes) override;
+    binder::Status setDesiredHeadTrackingMode(media::HeadTrackingMode mode) override;
+    binder::Status getActualHeadTrackingMode(media::HeadTrackingMode *mode) override;
+    binder::Status recenterHeadtracker() override;
+    binder::Status setGlobalTransform(const std::vector<float>& screenToStage) override;
+
+    /** IBinder::DeathRecipient. Listen to the death of the INativeSpatializerCallback. */
+    virtual void binderDied(const wp<IBinder>& who);
+
+    /** Registers a INativeSpatializerCallback when a client is attached to this Spatializer
+     * by audio policy service.
+     */
+    status_t registerCallback(const sp<media::INativeSpatializerCallback>& callback);
+
+    /** Level getter for use by local classes. */
+    media::SpatializationLevel getLevel() const { Mutex::Autolock _l(mLock); return mLevel; }
+
+    /** Called by audio policy service when the special output mixer dedicated to spatialization
+     * is opened and the spatializer engine must be created.
+     */
+    status_t attachOutput(audio_io_handle_t output);
+    /** Called by audio policy service when the special output mixer dedicated to spatialization
+     * is closed and the spatializer engine must be release.
+     */
+    audio_io_handle_t detachOutput();
+    /** Returns the output stream the spatializer is attached to. */
+    audio_io_handle_t getOutput() const { Mutex::Autolock _l(mLock); return mOutput; }
+
+    /** Sets the channel mask, sampling rate and format for the spatializer input. */
+    void setAudioInConfig(const audio_config_base_t& config) {
+        Mutex::Autolock _l(mLock);
+        mAudioInConfig = config;
+    }
+
+    /** Gets the channel mask, sampling rate and format set for the spatializer input. */
+    audio_config_base_t getAudioInConfig() const {
+        Mutex::Autolock _l(mLock);
+        return mAudioInConfig;
+    }
+
+    /** An implementation of an IEffect interface that can be used to pass advanced parameters to
+     * the spatializer engine. All APis are noop (i.e. the interface cannot be used to control
+     * the effect) except for passing parameters via the command() API. */
+    class EffectClient: public android::media::BnEffect {
+    public:
+
+        EffectClient(const sp<media::IEffectClient>& effectClient,
+                     Spatializer& parent);
+        virtual ~EffectClient();
+
+        // IEffect
+        android::binder::Status enable(int32_t* _aidl_return) override;
+        android::binder::Status disable(int32_t* _aidl_return) override;
+        android::binder::Status command(int32_t cmdCode,
+                                        const std::vector<uint8_t>& cmdData,
+                                        int32_t maxResponseSize,
+                                        std::vector<uint8_t>* response,
+                                        int32_t* _aidl_return) override;
+        android::binder::Status disconnect() override;
+        android::binder::Status getCblk(media::SharedFileRegion* _aidl_return) override;
+
+    private:
+        const sp<media::IEffectClient> mEffectClient;
+        sp<IMemory> mCblkMemory;
+        const Spatializer& mParent;
+        bool mDisconnected = false;
+    };
+
+private:
+
+    Spatializer(effect_descriptor_t engineDescriptor,
+                     SpatializerPolicyCallback *callback);
+
+
+    static void engineCallback(int32_t event, void* user, void *info);
+
+    /** Effect engine descriptor */
+    const effect_descriptor_t mEngineDescriptor;
+    /** Callback interface to parent audio policy service */
+    SpatializerPolicyCallback* mPolicyCallback;
+
+    /** Mutex protecting internal state */
+    mutable Mutex mLock;
+
+    /** Client AudioEffect for the engine */
+    sp<AudioEffect> mEngine GUARDED_BY(mLock);
+    /** Output stream the spatializer mixer thread is attached to */
+    audio_io_handle_t mOutput GUARDED_BY(mLock) = AUDIO_IO_HANDLE_NONE;
+    /** Virtualizer engine input configuration */
+    audio_config_base_t mAudioInConfig GUARDED_BY(mLock) = AUDIO_CONFIG_BASE_INITIALIZER;
+
+    /** Callback interface to the client (AudioService) controlling this`Spatializer */
+    sp<media::INativeSpatializerCallback> mSpatializerCallback GUARDED_BY(mLock);
+
+    /** Requested spatialization level */
+    media::SpatializationLevel mLevel GUARDED_BY(mLock) = media::SpatializationLevel::NONE;
+    /** Requested head tracking mode */
+    media::HeadTrackingMode mHeadTrackingMode GUARDED_BY(mLock)
+            = media::HeadTrackingMode::DISABLED;
+    /** Configured screen to stage transform */
+    std::vector<float> mScreenToStageTransform GUARDED_BY(mLock);
+
+    /** Extended IEffect interface is one has been created */
+    sp<EffectClient> mEffectClient GUARDED_BY(mLock);
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_SPATIALIZER_H