AudioPolicy: several spatializer output management fixes

Apply several fixes for spatializer output management when
the spatializer can be attached to two hardware modules (for instance
the primary and Bluetooth hardware modules).

- in getSpatializerOutput() open the output with
openOutputWithProfileAndDevice() to properly create the duplicating
output if needed and handle dynamic audio profiles.
- in openOutputWithProfileAndDevice(), do not open a duplicating output
for outputs opened on the primary module.
- in closeOutput(), clear mSpatializerOutput if this is the output being
closed.
- Allow canBeSpatialized() to ignore config matching with opened mSpatializerOutput
when not required.
- Force multichannel mixer mask in SwAudioOutputDescriptor::open() when
opening a spatializer output and the mixer config is not specified.

Bug: 210803914
Test: spatializer over Bluetooth A2DP
Change-Id: I1c5ca3ce0a230d674f3a22fcb1fe1d904665efe9
(cherry picked from commit b4f42a9b0d823c6460285b8d7a80fd429c4a6564)
Merged-In: I1c5ca3ce0a230d674f3a22fcb1fe1d904665efe9
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index f3d2326..866f1b5 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -549,6 +549,11 @@
     audio_output_flags_t halFlags = mFlags;
     if ((mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) {
         halFlags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
+        // If no mixer config is specified for a spatializer output, default to 5.1 for proper
+        // configuration of the final downmixer or spatializer
+        if (mixerConfig == nullptr) {
+            lMixerConfig.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+        }
     }
 
     ALOGV("opening output for device %s profile %p name %s",
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e334532..65e9448 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1372,7 +1372,8 @@
     }
 
     if (mSpatializerOutput != nullptr
-            && canBeSpatialized(attr, config, devices.toTypeAddrVector())) {
+            && canBeSpatializedInt(attr, config,
+                    devices.toTypeAddrVector(), false /* allowCurrentOutputReconfig */)) {
         return mSpatializerOutput->mIoHandle;
     }
 
@@ -4852,9 +4853,10 @@
     }
 }
 
-bool AudioPolicyManager::canBeSpatialized(const audio_attributes_t *attr,
+bool AudioPolicyManager::canBeSpatializedInt(const audio_attributes_t *attr,
                                       const audio_config_t *config,
-                                      const AudioDeviceTypeAddrVector &devices)  const
+                                      const AudioDeviceTypeAddrVector &devices,
+                                      bool allowCurrentOutputReconfig)  const
 {
     // The caller can have the audio attributes criteria ignored by either passing a null ptr or
     // the AUDIO_ATTRIBUTES_INITIALIZER value.
@@ -4890,7 +4892,8 @@
         if (!isChannelMaskSpatialized(config->channel_mask)) {
             return false;
         }
-        if (mSpatializerOutput != nullptr && mSpatializerOutput->mProfile == profile) {
+        if (!allowCurrentOutputReconfig && mSpatializerOutput != nullptr
+                && mSpatializerOutput->mProfile == profile) {
             if ((config->channel_mask & mSpatializerOutput->mMixerChannelMask)
                     != config->channel_mask) {
                 return false;
@@ -4911,7 +4914,8 @@
             audio_config_base_t clientConfig = client->config();
             audio_config_t config = audio_config_initializer(&clientConfig);
             if (desc != mSpatializerOutput
-                    && canBeSpatialized(&attr, &config, devicesTypeAddress)) {
+                    && canBeSpatializedInt(&attr, &config,
+                            devicesTypeAddress, false /* allowCurrentOutputReconfig */)) {
                 streamsToInvalidate.insert(client->stream());
             }
         }
@@ -4935,7 +4939,8 @@
         config = audio_config_initializer(mixerConfig);
         configPtr = &config;
     }
-    if (!canBeSpatialized(attr, configPtr, devicesTypeAddress)) {
+    if (!canBeSpatializedInt(
+            attr, configPtr, devicesTypeAddress)) {
         ALOGW("%s provided attributes or mixer config cannot be spatialized", __func__);
         return BAD_VALUE;
     }
@@ -4958,6 +4963,7 @@
     for (size_t i = 0; i < mOutputs.size(); i++) {
         sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         if (!desc->isDuplicated() && desc->mProfile == profile) {
+            ALOGV("%s found output %d for spatializer profile", __func__, desc->mIoHandle);
             mSpatializerOutput = desc;
             break;
         }
@@ -4977,39 +4983,29 @@
         };
         DeviceVector savedDevices = mSpatializerOutput->devices();
 
-        closeOutput(mSpatializerOutput->mIoHandle);
-        mSpatializerOutput.clear();
+        ALOGV("%s reopening spatializer output to match channel mask %#x (current mask %#x)",
+            __func__, configPtr->channel_mask, mSpatializerOutput->mMixerChannelMask);
 
-        const sp<SwAudioOutputDescriptor> desc =
-                new SwAudioOutputDescriptor(profile, mpClientInterface);
-        status_t status = desc->open(nullptr, mixerConfig, devices,
-                                                    mEngine->getStreamTypeForAttributes(*attr),
-                                                    AUDIO_OUTPUT_FLAG_SPATIALIZER, output);
-        if (status != NO_ERROR) {
-            ALOGW("%s failed opening output: status %d, output %d", __func__, status, *output);
-            if (*output != AUDIO_IO_HANDLE_NONE) {
-                desc->close();
-            }
+        closeOutput(mSpatializerOutput->mIoHandle);
+        //from now on mSpatializerOutput is null
+
+        sp<SwAudioOutputDescriptor> desc =
+                openOutputWithProfileAndDevice(profile, devices, mixerConfig);
+        if (desc == nullptr) {
             // re open the spatializer output with previous channel mask
-            status_t newStatus = desc->open(nullptr, &savedMixerConfig, savedDevices,
-                                mEngine->getStreamTypeForAttributes(*attr),
-                                AUDIO_OUTPUT_FLAG_SPATIALIZER, output);
-            if (newStatus != NO_ERROR) {
-                if (*output != AUDIO_IO_HANDLE_NONE) {
-                    desc->close();
-                }
-                ALOGE("%s failed to re-open mSpatializerOutput, status %d", __func__, newStatus);
+            desc = openOutputWithProfileAndDevice(profile, savedDevices, &savedMixerConfig);
+            if (desc == nullptr) {
+                ALOGE("%s failed to restore mSpatializerOutput with previous config", __func__);
             } else {
                 mSpatializerOutput = desc;
-                addOutput(*output, desc);
             }
             mPreviousOutputs = mOutputs;
             mpClientInterface->onAudioPortListUpdate();
             *output = AUDIO_IO_HANDLE_NONE;
-            return status;
+            ALOGW("%s could not open spatializer output with requested config", __func__);
+            return BAD_VALUE;
         }
         mSpatializerOutput = desc;
-        addOutput(*output, desc);
         mPreviousOutputs = mOutputs;
         mpClientInterface->onAudioPortListUpdate();
     }
@@ -5682,6 +5678,9 @@
 
     removeOutput(output);
     mPreviousOutputs = mOutputs;
+    if (closingOutput == mSpatializerOutput) {
+        mSpatializerOutput.clear();
+    }
 
     // MSD patches may have been released to support a non-MSD direct output. Reset MSD patch if
     // no direct outputs are open.
@@ -7222,7 +7221,8 @@
 }
 
 sp<SwAudioOutputDescriptor> AudioPolicyManager::openOutputWithProfileAndDevice(
-        const sp<IOProfile>& profile, const DeviceVector& devices)
+        const sp<IOProfile>& profile, const DeviceVector& devices,
+        const audio_config_base_t *mixerConfig)
 {
     for (const auto& device : devices) {
         // TODO: This should be checking if the profile supports the device combo.
@@ -7232,7 +7232,7 @@
     }
     sp<SwAudioOutputDescriptor> desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
     audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
-    status_t status = desc->open(nullptr /* halConfig */, nullptr /* mixerConfig */, devices,
+    status_t status = desc->open(nullptr /* halConfig */, mixerConfig, devices,
             AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
     if (status != NO_ERROR) {
         return nullptr;
@@ -7262,7 +7262,7 @@
         config.offload_info.channel_mask = config.channel_mask;
         config.offload_info.format = config.format;
 
-        status = desc->open(&config, nullptr /* mixerConfig */, devices,
+        status = desc->open(&config, mixerConfig, devices,
                             AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
         if (status != NO_ERROR) {
             return nullptr;
@@ -7270,6 +7270,7 @@
     }
 
     addOutput(output, desc);
+
     if (audio_is_remote_submix_device(deviceType) && address != "0") {
         sp<AudioPolicyMix> policyMix;
         if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) == NO_ERROR) {
@@ -7280,9 +7281,13 @@
                     address.string());
         }
 
-    } else if (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && hasPrimaryOutput()) {
-        // no duplicated output for direct outputs and
-        // outputs used by dynamic policy mixes
+    } else if (hasPrimaryOutput() && profile->getModule()
+                != mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)
+            && ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
+        // no duplicated output for:
+        // - direct outputs
+        // - outputs used by dynamic policy mixes
+        // - outputs opened on the primary HW module
         audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
 
         //TODO: configure audio effect output stage here
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 967aa10..b1454fd 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -358,7 +358,9 @@
 
         virtual bool canBeSpatialized(const audio_attributes_t *attr,
                                       const audio_config_t *config,
-                                      const AudioDeviceTypeAddrVector &devices) const;
+                                      const AudioDeviceTypeAddrVector &devices) const {
+            return canBeSpatializedInt(attr, config, devices);
+        }
 
         virtual status_t getSpatializerOutput(const audio_config_base_t *config,
                                                 const audio_attributes_t *attr,
@@ -961,6 +963,30 @@
                 const DeviceVector &devices,
                 audio_io_handle_t *output);
 
+        /**
+         * @brief 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, sample rate...
+         * @param devices the sink audio device selected for playback
+         * @param allowCurrentOutputReconfig if true, the result will be considering it is possible
+         *      to close and reopen an existing spatializer output stream to match the requested
+         *      criteria. If false, the criteria must be compatible with the opened sptializer
+         *      output.
+         * @return true if spatialization is possible for this context, false otherwise.
+         */
+        virtual bool canBeSpatializedInt(const audio_attributes_t *attr,
+                                      const audio_config_t *config,
+                                      const AudioDeviceTypeAddrVector &devices,
+                                      bool allowCurrentOutputReconfig = true) const;
+
         sp<IOProfile> getSpatializerOutputProfile(const audio_config_t *config,
                                                   const AudioDeviceTypeAddrVector &devices) const;
 
@@ -1056,8 +1082,20 @@
 
         bool areAllActiveTracksRerouted(const sp<SwAudioOutputDescriptor>& output);
 
-        sp<SwAudioOutputDescriptor> openOutputWithProfileAndDevice(const sp<IOProfile>& profile,
-                                                                   const DeviceVector& devices);
+        /**
+         * @brief Opens an output stream from the supplied IOProfile and route it to the
+         * supplied audio devices. If a mixer config is specified, it is forwarded to audio
+         * flinger. If not, a default config is derived from the output stream config.
+         * Also opens a duplicating output if needed and queries the audio HAL for supported
+         * audio profiles if the IOProfile is dynamic.
+         * @param[in] profile IOProfile to use as template
+         * @param[in] devices initial route to apply to this output stream
+         * @param[in] mixerConfig if not null, use this to configure the mixer
+         * @return an output descriptor for the newly opened stream or null in case of error.
+         */
+        sp<SwAudioOutputDescriptor> openOutputWithProfileAndDevice(
+                const sp<IOProfile>& profile, const DeviceVector& devices,
+                const audio_config_base_t *mixerConfig = nullptr);
 
 };
 
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 102b376..12cd136 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -392,7 +392,8 @@
             audio_config_base_t config = mSpatializer->getAudioInConfig();
             status_t status =
                     mAudioPolicyManager->getSpatializerOutput(&config, &attr, &newOutput);
-
+            ALOGV("%s currentOutput %d newOutput %d channel_mask %#x",
+                    __func__, currentOutput, newOutput, config.channel_mask);
             if (status == NO_ERROR && currentOutput == newOutput) {
                 return;
             }
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index 0fdbe20..54d9094 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -227,12 +227,8 @@
     if (status != NO_ERROR) {
         return status;
     }
-    status = getHalParameter<true>(effect, SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS,
+    return getHalParameter<true>(effect, SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS,
                                  &mChannelMasks);
-    if (status != NO_ERROR) {
-        return status;
-    }
-    return NO_ERROR;
 }
 
 /** Gets the channel mask, sampling rate and format set for the spatializer input. */