audio policy: update spatializer policy

Modify the rules to open and close the spatializer output
to address more scenarios:
- more than one spatializer output profile available
- some devices only reachable via a spatializer output profile

The spatializer outputs are opened and closed like other outputs
and a logic evaluates the best output when getSpatializerOutput()
is called

Bug: 188502620
Test: make
Change-Id: Id336e5c38a398525b3fab406cedce8a010d6c854
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e9e98ca..b069462 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -195,6 +195,10 @@
 
     audio_output_flags_t getOutputFlags() const { return mFlags; }
     float getSpeed() const { return mSpeed; }
+
+    bool canBeSpatialized() const { return (mAttr.flags
+            & (AUDIO_FLAG_CONTENT_SPATIALIZED | AUDIO_FLAG_NEVER_SPATIALIZE)) == 0; }
+
 protected:
     // for numerous
     friend class PlaybackThread;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 767199f..683c4f7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2039,6 +2039,7 @@
     free(mSinkBuffer);
     free(mMixerBuffer);
     free(mEffectBuffer);
+    free(mEffectToSinkBuffer);
 }
 
 // Thread virtuals
@@ -3001,11 +3002,18 @@
     // Originally this was int16_t[] array, need to remove legacy implications.
     free(mSinkBuffer);
     mSinkBuffer = NULL;
+    free(mEffectToSinkBuffer);
+    mEffectToSinkBuffer = nullptr;
+
     // For sink buffer size, we use the frame size from the downstream sink to avoid problems
     // with non PCM formats for compressed music, e.g. AAC, and Offload threads.
     const size_t sinkBufferSize = mNormalFrameCount * mFrameSize;
     (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize);
 
+    if (mType == SPATIALIZER) {
+        (void)posix_memalign(&mEffectToSinkBuffer, 32, sinkBufferSize);
+    }
+
     // We resize the mMixerBuffer according to the requirements of the sink buffer which
     // drives the output.
     free(mMixerBuffer);
@@ -3836,11 +3844,11 @@
             //
             // mMixerBufferValid is only set true by MixerThread::prepareTracks_l().
             // TODO use mSleepTimeUs == 0 as an additional condition.
+            uint32_t mixerChannelCount = mEffectBufferValid ?
+                        audio_channel_count_from_out_mask(mMixerChannelMask) : mChannelCount;
             if (mMixerBufferValid) {
                 void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
                 audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
-                uint32_t channelCount = mEffectBufferValid ?
-                            audio_channel_count_from_out_mask(mMixerChannelMask) : mChannelCount;
 
                 // mono blend occurs for mixer threads only (not direct or offloaded)
                 // and is handled here if we're going directly to the sink.
@@ -3858,7 +3866,7 @@
                 }
 
                 memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
-                        mNormalFrameCount * (channelCount + mHapticChannelCount));
+                        mNormalFrameCount * (mixerChannelCount + mHapticChannelCount));
 
                 // If we're going directly to the sink and there are haptic channels,
                 // we should adjust channels as the sample data is partially interleaved
@@ -3891,8 +3899,11 @@
                             && activeHapticSessionId == effectChains[i]->sessionId()) {
                         // Haptic data is active in this case, copy it directly from
                         // in buffer to out buffer.
+                        uint32_t channelCount =
+                                effectChains[i]->sessionId() == AUDIO_SESSION_OUTPUT_STAGE ?
+                                        mixerChannelCount : mChannelCount;
                         const size_t audioBufferSize = mNormalFrameCount
-                                * audio_bytes_per_frame(mChannelCount, EFFECT_BUFFER_FORMAT);
+                                * audio_bytes_per_frame(channelCount, EFFECT_BUFFER_FORMAT);
                         memcpy_by_audio_format(
                                 (uint8_t*)effectChains[i]->outBuffer() + audioBufferSize,
                                 EFFECT_BUFFER_FORMAT,
@@ -3932,8 +3943,23 @@
                 mBalance.process((float *)mEffectBuffer, mNormalFrameCount);
             }
 
-            memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
-                    mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+            if (mType == SPATIALIZER) {
+                memcpy_by_audio_format(mEffectToSinkBuffer, mFormat, mEffectBuffer,
+                        mEffectBufferFormat,
+                        mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+                accumulate_by_audio_format(mSinkBuffer, mEffectToSinkBuffer, mFormat,
+                                           mNormalFrameCount * mChannelCount);
+                const size_t audioBufferSize = mNormalFrameCount
+                        * audio_bytes_per_frame(mChannelCount, mFormat);
+                memcpy_by_audio_format(
+                        (uint8_t*)mSinkBuffer + audioBufferSize,
+                        mFormat,
+                        (uint8_t*)mEffectToSinkBuffer + audioBufferSize,
+                        mFormat, mNormalFrameCount * mHapticChannelCount);
+            } else {
+                memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
+                        mNormalFrameCount * (mChannelCount + mHapticChannelCount));
+            }
             // The sample data is partially interleaved when haptic channels exist,
             // we need to adjust channels here.
             if (mHapticChannelCount > 0) {
@@ -4588,6 +4614,7 @@
                 && Intersection(outDeviceTypes(), getAudioDeviceOutAllA2dpSet()).empty();
         break;
     }
+    ALOG_ASSERT(initFastMixer && mType == SPATIALIZER);
     ALOGW_IF(initFastMixer == false && mFrameCount < mNormalFrameCount,
             "FastMixer is preferred for this sink as frameCount %zu is less than threshold %zu",
             mFrameCount, mNormalFrameCount);
@@ -4946,6 +4973,9 @@
         // before effects processing or output.
         if (mMixerBufferValid) {
             memset(mMixerBuffer, 0, mMixerBufferSize);
+            if (mType == SPATIALIZER) {
+                memset(mSinkBuffer, 0, mSinkBufferSize);
+            }
         } else {
             memset(mSinkBuffer, 0, mSinkBufferSize);
         }
@@ -5438,11 +5468,21 @@
                 trackId,
                 AudioMixer::TRACK,
                 AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
-            mAudioMixer->setParameter(
-                trackId,
-                AudioMixer::TRACK,
-                AudioMixer::MIXER_CHANNEL_MASK,
-                (void *)(uintptr_t)(mMixerChannelMask | mHapticChannelMask));
+
+            if (mType == SPATIALIZER && !track->canBeSpatialized()) {
+                mAudioMixer->setParameter(
+                    trackId,
+                    AudioMixer::TRACK,
+                    AudioMixer::MIXER_CHANNEL_MASK,
+                    (void *)(uintptr_t)(mChannelMask | mHapticChannelMask));
+            } else {
+                mAudioMixer->setParameter(
+                    trackId,
+                    AudioMixer::TRACK,
+                    AudioMixer::MIXER_CHANNEL_MASK,
+                    (void *)(uintptr_t)(mMixerChannelMask | mHapticChannelMask));
+            }
+
             // limit track sample rate to 2 x output sample rate, which changes at re-configuration
             uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;
             uint32_t reqSampleRate = proxy->getSampleRate();
@@ -5479,16 +5519,27 @@
             if (mMixerBufferEnabled
                     && (track->mainBuffer() == mSinkBuffer
                             || track->mainBuffer() == mMixerBuffer)) {
-                mAudioMixer->setParameter(
-                        trackId,
-                        AudioMixer::TRACK,
-                        AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
-                mAudioMixer->setParameter(
-                        trackId,
-                        AudioMixer::TRACK,
-                        AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
-                // TODO: override track->mainBuffer()?
-                mMixerBufferValid = true;
+                if (mType == SPATIALIZER && !track->canBeSpatialized()) {
+                    mAudioMixer->setParameter(
+                            trackId,
+                            AudioMixer::TRACK,
+                            AudioMixer::MIXER_FORMAT, (void *)mFormat);
+                    mAudioMixer->setParameter(
+                            trackId,
+                            AudioMixer::TRACK,
+                            AudioMixer::MAIN_BUFFER, (void *)mSinkBuffer);
+                } else {
+                    mAudioMixer->setParameter(
+                            trackId,
+                            AudioMixer::TRACK,
+                            AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+                    mAudioMixer->setParameter(
+                            trackId,
+                            AudioMixer::TRACK,
+                            AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
+                    // TODO: override track->mainBuffer()?
+                    mMixerBufferValid = true;
+                }
             } else {
                 mAudioMixer->setParameter(
                         trackId,
@@ -5678,8 +5729,10 @@
     // sink or mix buffer must be cleared if all tracks are connected to an
     // effect chain as in this case the mixer will not write to the sink or mix buffer
     // and track effects will accumulate into it
-    if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
-            (mixedTracks == 0 && fastTracks > 0))) {
+    // always clear sink buffer for spatializer output as the output of the spatializer
+    // effect will be accumulated into it
+    if ((mBytesRemaining == 0) && (((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
+            (mixedTracks == 0 && fastTracks > 0)) || (mType == SPATIALIZER))) {
         // FIXME as a performance optimization, should remember previous zero status
         if (mMixerBufferValid) {
             memset(mMixerBuffer, 0, mMixerBufferSize);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 58864e7..0e86391 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1122,6 +1122,11 @@
     // for any processing (including output processing).
     bool                            mEffectBufferValid;
 
+    // Frame size aligned buffer used to convert mEffectBuffer samples to mSinkBuffer format prior
+    // to accumulate into mSinkBuffer on SPATIALIZER threads
+    void*                           mEffectToSinkBuffer = nullptr;
+
+
     // suspend count, > 0 means suspended.  While suspended, the thread continues to pull from
     // tracks and mix, but doesn't write to HAL.  A2DP and SCO HAL implementations can't handle
     // concurrent use of both of them, so Audio Policy Service suspends one of the threads to
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a2dbeb2..e334532 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -248,9 +248,7 @@
                     // been opened by checkOutputsForDevice() to query dynamic parameters
                     if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)
                             || (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
-                                (desc->mDirectOpenCount == 0))
-                            || (((desc->mFlags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) &&
-                                (desc != mSpatializerOutput))) {
+                                (desc->mDirectOpenCount == 0))) {
                         clearAudioSourcesForOutput(output);
                         closeOutput(output);
                     }
@@ -928,8 +926,7 @@
 }
 
 sp<IOProfile> AudioPolicyManager::getSpatializerOutputProfile(
-        const audio_config_t *config __unused, const AudioDeviceTypeAddrVector &devices,
-        bool forOpening) const
+        const audio_config_t *config __unused, const AudioDeviceTypeAddrVector &devices) const
 {
     for (const auto& hwModule : mHwModules) {
         for (const auto& curProfile : hwModule->getOutputProfiles()) {
@@ -947,9 +944,6 @@
                     continue;
                 }
             }
-            if (forOpening && !curProfile->canOpenNewIo()) {
-                continue;
-            }
             ALOGV("%s found profile %s", __func__, curProfile->getName().c_str());
             return curProfile;
         }
@@ -4843,6 +4837,21 @@
     return source;
 }
 
+/* static */
+bool AudioPolicyManager::isChannelMaskSpatialized(audio_channel_mask_t channels) {
+    switch (channels) {
+        case AUDIO_CHANNEL_OUT_5POINT1:
+        case AUDIO_CHANNEL_OUT_5POINT1POINT2:
+        case AUDIO_CHANNEL_OUT_5POINT1POINT4:
+        case AUDIO_CHANNEL_OUT_7POINT1:
+        case AUDIO_CHANNEL_OUT_7POINT1POINT2:
+        case AUDIO_CHANNEL_OUT_7POINT1POINT4:
+            return true;
+        default:
+            return false;
+    }
+}
+
 bool AudioPolicyManager::canBeSpatialized(const audio_attributes_t *attr,
                                       const audio_config_t *config,
                                       const AudioDeviceTypeAddrVector &devices)  const
@@ -4851,9 +4860,13 @@
     // the AUDIO_ATTRIBUTES_INITIALIZER value.
     // If attributes are specified, current policy is to only allow spatialization for media
     // and game usages.
-    if (attr != nullptr && *attr != AUDIO_ATTRIBUTES_INITIALIZER &&
-            attr->usage != AUDIO_USAGE_MEDIA && attr->usage != AUDIO_USAGE_GAME) {
-        return false;
+    if (attr != nullptr && *attr != AUDIO_ATTRIBUTES_INITIALIZER) {
+        if (attr->usage != AUDIO_USAGE_MEDIA && attr->usage != AUDIO_USAGE_GAME) {
+            return false;
+        }
+        if ((attr->flags & (AUDIO_FLAG_CONTENT_SPATIALIZED | AUDIO_FLAG_NEVER_SPATIALIZE)) != 0) {
+            return false;
+        }
     }
 
     // The caller can have the devices criteria ignored by passing and empty vector, and
@@ -4861,7 +4874,7 @@
     // Otherwise an output profile supporting a spatializer effect that can be routed
     // to the specified devices must exist.
     sp<IOProfile> profile =
-            getSpatializerOutputProfile(config, devices, false /*forOpening*/);
+            getSpatializerOutputProfile(config, devices);
     if (profile == nullptr) {
         return false;
     }
@@ -4869,37 +4882,36 @@
     // The caller can have the audio config criteria ignored by either passing a null ptr or
     // the AUDIO_CONFIG_INITIALIZER value.
     // If an audio config is specified, current policy is to only allow spatialization for
-    // 5.1, 7.1and 7.1.4 audio.
+    // some positional channel masks.
     // If the spatializer output is already opened, only channel masks included in the
     // spatializer output mixer channel mask are allowed.
+
     if (config != nullptr && *config != AUDIO_CONFIG_INITIALIZER) {
-        if (config->channel_mask != AUDIO_CHANNEL_OUT_5POINT1
-                && config->channel_mask != AUDIO_CHANNEL_OUT_7POINT1
-                && config->channel_mask != AUDIO_CHANNEL_OUT_7POINT1POINT4) {
+        if (!isChannelMaskSpatialized(config->channel_mask)) {
             return false;
         }
-        if (mSpatializerOutput != nullptr) {
+        if (mSpatializerOutput != nullptr && mSpatializerOutput->mProfile == profile) {
             if ((config->channel_mask & mSpatializerOutput->mMixerChannelMask)
                     != config->channel_mask) {
                 return false;
             }
         }
     }
-
     return true;
 }
 
 void AudioPolicyManager::checkVirtualizerClientRoutes() {
     std::set<audio_stream_type_t> streamsToInvalidate;
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
-        for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
+        const sp<SwAudioOutputDescriptor>& desc = mOutputs[i];
+        for (const sp<TrackClientDescriptor>& client : desc->getClientIterable()) {
             audio_attributes_t attr = client->attributes();
             DeviceVector devices = mEngine->getOutputDevicesForAttributes(attr, nullptr, false);
             AudioDeviceTypeAddrVector devicesTypeAddress = devices.toTypeAddrVector();
             audio_config_base_t clientConfig = client->config();
             audio_config_t config = audio_config_initializer(&clientConfig);
-            if (canBeSpatialized(&attr, &config, devicesTypeAddress)) {
+            if (desc != mSpatializerOutput
+                    && canBeSpatialized(&attr, &config, devicesTypeAddress)) {
                 streamsToInvalidate.insert(client->stream());
             }
         }
@@ -4915,10 +4927,6 @@
                                                         audio_io_handle_t *output) {
     *output = AUDIO_IO_HANDLE_NONE;
 
-    if (mSpatializerOutput != nullptr) {
-        return INVALID_OPERATION;
-    }
-
     DeviceVector devices = mEngine->getOutputDevicesForAttributes(*attr, nullptr, false);
     AudioDeviceTypeAddrVector devicesTypeAddress = devices.toTypeAddrVector();
     audio_config_t *configPtr = nullptr;
@@ -4928,35 +4936,87 @@
         configPtr = &config;
     }
     if (!canBeSpatialized(attr, configPtr, devicesTypeAddress)) {
+        ALOGW("%s provided attributes or mixer config cannot be spatialized", __func__);
         return BAD_VALUE;
     }
 
     sp<IOProfile> profile =
-            getSpatializerOutputProfile(configPtr, devicesTypeAddress, true /*forOpening*/);
+            getSpatializerOutputProfile(configPtr, devicesTypeAddress);
     if (profile == nullptr) {
+        ALOGW("%s no suitable output profile for provided attributes or mixer config", __func__);
         return BAD_VALUE;
     }
 
-    mSpatializerOutput = new SwAudioOutputDescriptor(profile, mpClientInterface);
-    status_t status = mSpatializerOutput->open(nullptr, mixerConfig, devices,
+    if (mSpatializerOutput != nullptr && mSpatializerOutput->mProfile == profile
+            && configPtr != nullptr
+            && configPtr->channel_mask == mSpatializerOutput->mMixerChannelMask) {
+        *output = mSpatializerOutput->mIoHandle;
+        ALOGV("%s returns current spatializer output %d", __func__, *output);
+        return NO_ERROR;
+    }
+    mSpatializerOutput.clear();
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+        if (!desc->isDuplicated() && desc->mProfile == profile) {
+            mSpatializerOutput = desc;
+            break;
+        }
+    }
+    if (mSpatializerOutput == nullptr) {
+        ALOGW("%s no opened spatializer output for profile %s",
+                __func__, profile->getName().c_str());
+        return BAD_VALUE;
+    }
+
+    if (configPtr != nullptr
+            && configPtr->channel_mask != mSpatializerOutput->mMixerChannelMask) {
+        audio_config_base_t savedMixerConfig = {
+            .sample_rate = mSpatializerOutput->getSamplingRate(),
+            .format = mSpatializerOutput->getFormat(),
+            .channel_mask = mSpatializerOutput->mMixerChannelMask,
+        };
+        DeviceVector savedDevices = mSpatializerOutput->devices();
+
+        closeOutput(mSpatializerOutput->mIoHandle);
+        mSpatializerOutput.clear();
+
+        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) {
-        ALOGV("%s failed opening output: status %d, output %d", __func__, status, *output);
-        if (*output != AUDIO_IO_HANDLE_NONE) {
-            mSpatializerOutput->close();
+        if (status != NO_ERROR) {
+            ALOGW("%s failed opening output: status %d, output %d", __func__, status, *output);
+            if (*output != AUDIO_IO_HANDLE_NONE) {
+                desc->close();
+            }
+            // 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);
+            } else {
+                mSpatializerOutput = desc;
+                addOutput(*output, desc);
+            }
+            mPreviousOutputs = mOutputs;
+            mpClientInterface->onAudioPortListUpdate();
+            *output = AUDIO_IO_HANDLE_NONE;
+            return status;
         }
-        mSpatializerOutput.clear();
-        *output = AUDIO_IO_HANDLE_NONE;
-        return status;
+        mSpatializerOutput = desc;
+        addOutput(*output, desc);
+        mPreviousOutputs = mOutputs;
+        mpClientInterface->onAudioPortListUpdate();
     }
 
     checkVirtualizerClientRoutes();
 
-    addOutput(*output, mSpatializerOutput);
-    mPreviousOutputs = mOutputs;
-    mpClientInterface->onAudioPortListUpdate();
-
+    *output = mSpatializerOutput->mIoHandle;
     ALOGV("%s returns new spatializer output %d", __func__, *output);
     return NO_ERROR;
 }
@@ -4968,8 +5028,11 @@
     if (mSpatializerOutput->mIoHandle != output) {
         return BAD_VALUE;
     }
-    closeOutput(output);
+
     mSpatializerOutput.clear();
+
+    checkVirtualizerClientRoutes();
+
     return NO_ERROR;
 }
 
@@ -5186,8 +5249,7 @@
                     outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
                 mPrimaryOutput = outputDesc;
             }
-            if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0
-                || (outProfile->getFlags() & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0 ) {
+            if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
                 outputDesc->close();
             } else {
                 addOutput(output, outputDesc);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index fd5c58f..967aa10 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -962,8 +962,9 @@
                 audio_io_handle_t *output);
 
         sp<IOProfile> getSpatializerOutputProfile(const audio_config_t *config,
-                                                       const AudioDeviceTypeAddrVector &devices,
-                                                       bool forOpening) const;
+                                                  const AudioDeviceTypeAddrVector &devices) const;
+
+        static bool isChannelMaskSpatialized(audio_channel_mask_t channels);
 
         void checkVirtualizerClientRoutes();
 
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 58359be..8504489 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -121,11 +121,14 @@
     ALOGV("setDeviceConnectionState()");
     Mutex::Autolock _l(mLock);
     AutoCallerClear acc;
-    return binderStatusFromStatusT(
-            mAudioPolicyManager->setDeviceConnectionState(device, state,
+    status_t status = mAudioPolicyManager->setDeviceConnectionState(device, state,
                                                           deviceAidl.address.c_str(),
                                                           deviceNameAidl.c_str(),
-                                                          encodedFormat));
+                                                          encodedFormat);
+    if (status == NO_ERROR) {
+        onCheckSpatializer_l();
+    }
+    return binderStatusFromStatusT(status);
 }
 
 Status AudioPolicyService::getDeviceConnectionState(const media::AudioDevice& deviceAidl,
@@ -165,9 +168,13 @@
     ALOGV("handleDeviceConfigChange()");
     Mutex::Autolock _l(mLock);
     AutoCallerClear acc;
-    return binderStatusFromStatusT(
-            mAudioPolicyManager->handleDeviceConfigChange(device, deviceAidl.address.c_str(),
-                                                          deviceNameAidl.c_str(), encodedFormat));
+    status_t status =  mAudioPolicyManager->handleDeviceConfigChange(
+            device, deviceAidl.address.c_str(), deviceNameAidl.c_str(), encodedFormat);
+
+    if (status == NO_ERROR) {
+       onCheckSpatializer_l();
+    }
+    return binderStatusFromStatusT(status);
 }
 
 Status AudioPolicyService::setPhoneState(media::AudioMode stateAidl, int32_t uidAidl)
@@ -234,6 +241,7 @@
     Mutex::Autolock _l(mLock);
     AutoCallerClear acc;
     mAudioPolicyManager->setForceUse(usage, config);
+    onCheckSpatializer_l();
     return Status::ok();
 }
 
@@ -2062,8 +2070,11 @@
         return binderStatusFromStatusT(NO_INIT);
     }
     Mutex::Autolock _l(mLock);
-    return binderStatusFromStatusT(
-            mAudioPolicyManager->setDevicesRoleForStrategy(strategy, role, devices));
+    status_t status = mAudioPolicyManager->setDevicesRoleForStrategy(strategy, role, devices);
+    if (status == NO_ERROR) {
+       onCheckSpatializer_l();
+    }
+    return binderStatusFromStatusT(status);
 }
 
 Status AudioPolicyService::removeDevicesRoleForStrategy(int32_t strategyAidl,
@@ -2076,8 +2087,11 @@
         return binderStatusFromStatusT(NO_INIT);
     }
     Mutex::Autolock _l(mLock);
-    return binderStatusFromStatusT(
-            mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role));
+    status_t status = mAudioPolicyManager->removeDevicesRoleForStrategy(strategy, role);
+    if (status == NO_ERROR) {
+       onCheckSpatializer_l();
+    }
+    return binderStatusFromStatusT(status);
 }
 
 Status AudioPolicyService::getDevicesForRoleAndStrategy(
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 56c472b..de71a00 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -366,41 +366,51 @@
 void AudioPolicyService::onCheckSpatializer()
 {
     Mutex::Autolock _l(mLock);
-    mOutputCommandThread->checkSpatializerCommand();
+    onCheckSpatializer_l();
+}
+
+void AudioPolicyService::onCheckSpatializer_l()
+{
+    if (mSpatializer != nullptr) {
+        mOutputCommandThread->checkSpatializerCommand();
+    }
 }
 
 void AudioPolicyService::doOnCheckSpatializer()
 {
-    sp<Spatializer> spatializer;
-    {
-        Mutex::Autolock _l(mLock);
-        spatializer = mSpatializer;
+    Mutex::Autolock _l(mLock);
 
-        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);
+    if (mSpatializer != nullptr) {
+        if (mSpatializer->getLevel() != media::SpatializationLevel::NONE) {
+            audio_io_handle_t currentOutput = mSpatializer->getOutput();
+            audio_io_handle_t newOutput;
+            const audio_attributes_t attr = attributes_initializer(AUDIO_USAGE_MEDIA);
+            audio_config_base_t config = mSpatializer->getAudioInConfig();
+            status_t status =
+                    mAudioPolicyManager->getSpatializerOutput(&config, &attr, &newOutput);
+
+            if (status == NO_ERROR && currentOutput == newOutput) {
+                return;
+            }
+            mLock.unlock();
+            // It is OK to call detachOutput() is none is already attached.
+            mSpatializer->detachOutput();
+            if (status != NO_ERROR || newOutput == AUDIO_IO_HANDLE_NONE) {
                 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);
-                }
+                return;
+            }
+            status = mSpatializer->attachOutput(newOutput);
+            mLock.lock();
+            if (status != NO_ERROR) {
+                mAudioPolicyManager->releaseSpatializerOutput(newOutput);
+            }
+        } else if (mSpatializer->getLevel() == media::SpatializationLevel::NONE
+                               && mSpatializer->getOutput() != AUDIO_IO_HANDLE_NONE) {
+            mLock.unlock();
+            audio_io_handle_t output = mSpatializer->detachOutput();
+            mLock.lock();
+            if (output != AUDIO_IO_HANDLE_NONE) {
+                mAudioPolicyManager->releaseSpatializerOutput(output);
             }
         }
     }
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index b897a44..27c4e1c 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -327,6 +327,7 @@
      * by audio policy manager and attach/detach the spatializer effect accordingly.
      */
     void onCheckSpatializer() override;
+    void onCheckSpatializer_l();
     void doOnCheckSpatializer();
 
     void setEffectSuspended(int effectId,