Spatializer: Disable head tracking for stereo spatialization

Do not enable head tracking if all the spatialized tracks are stereo.

Flag: EXEMPT bugfix
Test: manual spatial audio tests
Test: atest spatializer_tests
Bug: 356953811
Change-Id: I75e2cb63ee4c5c5bfba628802198d056fd30e216
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 739e201..90d7709 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -6358,10 +6358,11 @@
     // mode is not requested.
 
     if (config != nullptr && *config != AUDIO_CONFIG_INITIALIZER) {
-        static const bool stereo_spatialization_enabled =
+        static const bool stereo_spatialization_prop_enabled =
                 property_get_bool("ro.audio.stereo_spatialization_enabled", false);
         const bool channel_mask_spatialized =
-                (stereo_spatialization_enabled && com_android_media_audio_stereo_spatialization())
+                (stereo_spatialization_prop_enabled
+                        && com_android_media_audio_stereo_spatialization())
                 ? audio_channel_mask_contains_stereo(config->channel_mask)
                 : audio_is_channel_mask_spatialized(config->channel_mask);
         if (!channel_mask_spatialized) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index f414862..e598897 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -477,7 +477,7 @@
 
         sp<AudioPlaybackClient> client =
                 new AudioPlaybackClient(attr, output, attributionSource, session,
-                    portId, selectedDeviceId, stream, isSpatialized);
+                    portId, selectedDeviceId, stream, isSpatialized, config.channel_mask);
         mAudioPlaybackClients.add(portId, client);
 
         _aidl_return->output = VALUE_OR_RETURN_BINDER_STATUS(
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 6144f8c..cbc0b41 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -590,12 +590,13 @@
             if (status == NO_ERROR && currentOutput == newOutput) {
                 return;
             }
-            size_t numActiveTracks = countActiveClientsOnOutput_l(newOutput);
+            std::vector<audio_channel_mask_t> activeTracksMasks =
+                    getActiveTracksMasks_l(newOutput);
             mMutex.unlock();
             // It is OK to call detachOutput() is none is already attached.
             mSpatializer->detachOutput();
             if (status == NO_ERROR && newOutput != AUDIO_IO_HANDLE_NONE) {
-                status = mSpatializer->attachOutput(newOutput, numActiveTracks);
+                status = mSpatializer->attachOutput(newOutput, activeTracksMasks);
             }
             mMutex.lock();
             if (status != NO_ERROR) {
@@ -613,17 +614,17 @@
     }
 }
 
-size_t AudioPolicyService::countActiveClientsOnOutput_l(
+std::vector<audio_channel_mask_t> AudioPolicyService::getActiveTracksMasks_l(
         audio_io_handle_t output, bool spatializedOnly) {
-    size_t count = 0;
+    std::vector<audio_channel_mask_t> activeTrackMasks;
     for (size_t i = 0; i < mAudioPlaybackClients.size(); i++) {
         auto client = mAudioPlaybackClients.valueAt(i);
         if (client->io == output && client->active
                 && (!spatializedOnly || client->isSpatialized)) {
-            count++;
+            activeTrackMasks.push_back(client->channelMask);
         }
     }
-    return count;
+    return activeTrackMasks;
 }
 
 void AudioPolicyService::onUpdateActiveSpatializerTracks_l() {
@@ -639,12 +640,12 @@
         return;
     }
     audio_io_handle_t output = mSpatializer->getOutput();
-    size_t activeClients;
+    std::vector<audio_channel_mask_t> activeTracksMasks;
     {
         audio_utils::lock_guard _l(mMutex);
-        activeClients = countActiveClientsOnOutput_l(output);
+        activeTracksMasks = getActiveTracksMasks_l(output);
     }
-    mSpatializer->updateActiveTracks(activeClients);
+    mSpatializer->updateActiveTracks(activeTracksMasks);
 }
 
 status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 720ba84..2b4126c 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -976,13 +976,15 @@
                       const audio_io_handle_t io, AttributionSourceState attributionSource,
                             const audio_session_t session, audio_port_handle_t portId,
                             audio_port_handle_t deviceId, audio_stream_type_t stream,
-                            bool isSpatialized) :
+                            bool isSpatialized, audio_channel_mask_t channelMask) :
                     AudioClient(attributes, io, attributionSource, session, portId,
-                        deviceId), stream(stream), isSpatialized(isSpatialized)  {}
+                        deviceId), stream(stream), isSpatialized(isSpatialized),
+                        channelMask(channelMask) {}
                 ~AudioPlaybackClient() override = default;
 
         const audio_stream_type_t stream;
         const bool isSpatialized;
+        const audio_channel_mask_t channelMask;
     };
 
     void getPlaybackClientAndEffects(audio_port_handle_t portId,
@@ -1013,14 +1015,14 @@
     void unloadAudioPolicyManager();
 
     /**
-     * Returns the number of active audio tracks on the specified output mixer.
+     * Returns the channel masks for active audio tracks on the specified output mixer.
      * The query can be specified to only include spatialized audio tracks or consider
      * all tracks.
      * @param output the I/O handle of the output mixer to consider
      * @param spatializedOnly true if only spatialized tracks should be considered
-     * @return the number of active tracks.
+     * @return a list of channel masks for all active tracks matching the condition.
      */
-    size_t countActiveClientsOnOutput_l(
+    std::vector<audio_channel_mask_t> getActiveTracksMasks_l(
             audio_io_handle_t output, bool spatializedOnly = true) REQUIRES(mMutex);
 
     mutable audio_utils::mutex mMutex{audio_utils::MutexOrder::kAudioPolicyService_Mutex};
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index c98f8df..9cc3b8f 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -936,7 +936,8 @@
             });
 }
 
-status_t Spatializer::attachOutput(audio_io_handle_t output, size_t numActiveTracks) {
+status_t Spatializer::attachOutput(audio_io_handle_t output,
+          const std::vector<audio_channel_mask_t>& activeTracksMasks) {
     bool outputChanged = false;
     sp<media::INativeSpatializerCallback> callback;
 
@@ -944,7 +945,7 @@
         audio_utils::lock_guard lock(mMutex);
         ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
         mLocalLog.log("%s with output %d tracks %zu (mOutput %d)", __func__, (int)output,
-                      numActiveTracks, (int)mOutput);
+                      activeTracksMasks.size(), (int)mOutput);
         if (mOutput != AUDIO_IO_HANDLE_NONE) {
             LOG_ALWAYS_FATAL_IF(mEngine == nullptr, "%s output set without FX engine", __func__);
             // remove FX instance
@@ -969,7 +970,7 @@
 
         outputChanged = mOutput != output;
         mOutput = output;
-        mNumActiveTracks = numActiveTracks;
+        mActiveTracksMasks = activeTracksMasks;
         AudioSystem::addSupportedLatencyModesCallback(this);
 
         std::vector<audio_latency_mode_t> latencyModes;
@@ -1008,7 +1009,8 @@
 
     {
         audio_utils::lock_guard lock(mMutex);
-        mLocalLog.log("%s with output %d tracks %zu", __func__, (int)mOutput, mNumActiveTracks);
+        mLocalLog.log("%s with output %d num tracks %zu",
+            __func__, (int)mOutput, mActiveTracksMasks.size());
         ALOGV("%s mOutput %d", __func__, (int)mOutput);
         if (mOutput == AUDIO_IO_HANDLE_NONE) {
             return output;
@@ -1051,11 +1053,13 @@
     }
 }
 
-void Spatializer::updateActiveTracks(size_t numActiveTracks) {
+void Spatializer::updateActiveTracks(
+        const std::vector<audio_channel_mask_t>& activeTracksMasks) {
     audio_utils::lock_guard lock(mMutex);
-    if (mNumActiveTracks != numActiveTracks) {
-        mLocalLog.log("%s from %zu to %zu", __func__, mNumActiveTracks, numActiveTracks);
-        mNumActiveTracks = numActiveTracks;
+    if (mActiveTracksMasks != activeTracksMasks) {
+        mLocalLog.log("%s from %zu to %zu",
+                __func__, mActiveTracksMasks.size(), activeTracksMasks.size());
+        mActiveTracksMasks = activeTracksMasks;
         checkEngineState_l();
         checkSensorsState_l();
     }
@@ -1114,7 +1118,7 @@
         if (mPoseController != nullptr) {
             // TODO(b/253297301, b/255433067) reenable low latency condition check
             // for Head Tracking after Bluetooth HAL supports it correctly.
-            if (mNumActiveTracks > 0 && mLevel != Spatialization::Level::NONE
+            if (shouldUseHeadTracking_l() && mLevel != Spatialization::Level::NONE
                     && mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
                     && mHeadSensor != SpatializerPoseController::INVALID_SENSOR) {
                 if (supportsLowLatencyMode) {
@@ -1146,9 +1150,28 @@
     }
 }
 
+
+/* static */
+bool Spatializer::containsImmersiveChannelMask(
+        const std::vector<audio_channel_mask_t>& masks)
+{
+    for (auto mask : masks) {
+        if (audio_is_channel_mask_spatialized(mask)) {
+            return true;
+        }
+    }
+    // Only non-immersive channel masks, e.g. AUDIO_CHANNEL_OUT_STEREO, are present.
+    return false;
+}
+
+bool Spatializer::shouldUseHeadTracking_l() const {
+    // Headtracking only available on immersive channel masks.
+    return containsImmersiveChannelMask(mActiveTracksMasks);
+}
+
 void Spatializer::checkEngineState_l() {
     if (mEngine != nullptr) {
-        if (mLevel != Spatialization::Level::NONE && mNumActiveTracks > 0) {
+        if (mLevel != Spatialization::Level::NONE && mActiveTracksMasks.size() > 0) {
             mEngine->setEnabled(true);
             setEffectParameter_l(SPATIALIZER_PARAM_LEVEL,
                     std::vector<Spatialization::Level>{mLevel});
@@ -1237,7 +1260,8 @@
     base::StringAppendF(&ss, "\n%smSupportsHeadTracking: %s\n", prefixSpace.c_str(),
                         mSupportsHeadTracking ? "true" : "false");
     // 2. Settings (Output, tracks)
-    base::StringAppendF(&ss, "%smNumActiveTracks: %zu\n", prefixSpace.c_str(), mNumActiveTracks);
+    base::StringAppendF(&ss, "%sNum Active Tracks: %zu\n",
+            prefixSpace.c_str(), mActiveTracksMasks.size());
     base::StringAppendF(&ss, "%sOutputStreamHandle: %d\n", prefixSpace.c_str(), (int)mOutput);
 
     // 3. Sensors, Effect information.
@@ -1248,8 +1272,12 @@
                         mDisplayOrientation);
 
     // 4. Show flag or property state.
+    static const bool stereo_spatialization_prop_enabled =
+            property_get_bool("ro.audio.stereo_spatialization_enabled", false);
+    const bool stereo_spatialization = com_android_media_audio_stereo_spatialization()
+            && stereo_spatialization_prop_enabled;
     base::StringAppendF(&ss, "%sStereo Spatialization: %s\n", prefixSpace.c_str(),
-            com_android_media_audio_stereo_spatialization() ? "true" : "false");
+            stereo_spatialization ? "true" : "false");
 
     ss.append(prefixSpace + "CommandLog:\n");
     ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index c5f159c..5ea3258 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -185,7 +185,8 @@
     /** 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, size_t numActiveTracks);
+    status_t attachOutput(audio_io_handle_t output,
+                          const std::vector<audio_channel_mask_t>& activeTracksMasks);
     /** Called by audio policy service when the special output mixer dedicated to spatialization
      * is closed and the spatializer engine must be release.
      */
@@ -199,7 +200,7 @@
         mOutput = output;
     }
 
-    void updateActiveTracks(size_t numActiveTracks);
+    void updateActiveTracks(const std::vector<audio_channel_mask_t>& activeTracksMasks);
 
     /** Gets the channel mask, sampling rate and format set for the spatializer input. */
     audio_config_base_t getAudioInConfig() const;
@@ -227,6 +228,16 @@
     void onSupportedLatencyModesChangedMsg(
             audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes);
 
+    // Made public for test only
+    /**
+     * Returns true if there exists an immersive channel mask in the vector.
+     *
+     * Example of a non-immersive channel mask such as AUDIO_CHANNEL_OUT_STEREO
+     * versus an immersive channel mask such as AUDIO_CHANNEL_OUT_5POINT1.
+     */
+    static bool containsImmersiveChannelMask(
+            const std::vector<audio_channel_mask_t>& masks);
+
 private:
     Spatializer(effect_descriptor_t engineDescriptor,
                      SpatializerPolicyCallback *callback);
@@ -462,6 +473,11 @@
      */
     audio_latency_mode_t selectHeadtrackingConnectionMode_l() REQUIRES(mMutex);
 
+    /**
+     * Indicates if current conditions are compatible with head tracking.
+     */
+    bool shouldUseHeadTracking_l() const REQUIRES(mMutex);
+
     /** Effect engine descriptor */
     const effect_descriptor_t mEngineDescriptor;
     /** Callback interface to parent audio policy service */
@@ -539,7 +555,7 @@
     sp<ALooper> mLooper;
     sp<EngineCallbackHandler> mHandler;
 
-    size_t mNumActiveTracks GUARDED_BY(mMutex) = 0;
+    std::vector<audio_channel_mask_t> mActiveTracksMasks GUARDED_BY(mMutex);
     std::vector<audio_latency_mode_t> mSupportedLatencyModes GUARDED_BY(mMutex);
     /** preference order for low latency modes according to persist.bluetooth.hid.transport */
     std::vector<audio_latency_mode_t> mOrderedLowLatencyModes;
diff --git a/services/audiopolicy/tests/spatializer_tests.cpp b/services/audiopolicy/tests/spatializer_tests.cpp
index 73bef43..0b40f32 100644
--- a/services/audiopolicy/tests/spatializer_tests.cpp
+++ b/services/audiopolicy/tests/spatializer_tests.cpp
@@ -33,6 +33,38 @@
 using media::audio::common::HeadTracking;
 using media::audio::common::Spatialization;
 
+// Test Spatializer Helper Methods
+
+TEST(Spatializer, containsImmersiveChannelMask) {
+    // Regardless of the implementation, we expect the following
+    // behavior.
+
+    // Pure non-immersive
+    EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_STEREO }));
+    EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO }));
+    EXPECT_FALSE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+              AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_MONO }));
+
+    // Pure immersive
+    EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_5POINT1 }));
+    EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_7POINT1 }));
+    EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_5POINT1, AUDIO_CHANNEL_OUT_7POINT1,
+              AUDIO_CHANNEL_OUT_22POINT2 }));
+
+    // Mixed immersive/non-immersive
+    EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_7POINT1POINT4 }));
+    EXPECT_TRUE(Spatializer::containsImmersiveChannelMask(
+            { AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+              AUDIO_CHANNEL_OUT_7POINT1 }));
+}
+
 class TestSpatializerPolicyCallback :
         public SpatializerPolicyCallback {
 public:
@@ -68,7 +100,7 @@
         mSpatializer->setOutput(AUDIO_IO_HANDLE_NONE);
         mSpatializer->setDesiredHeadTrackingMode(HeadTracking::Mode::DISABLED);
         mSpatializer->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
-        mSpatializer->updateActiveTracks(0);
+        mSpatializer->updateActiveTracks({});
     }
 
     static constexpr audio_io_handle_t sTestOutput= 1977;
@@ -174,12 +206,12 @@
     ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
 
     // requested latency mode must be low if at least one spatialized tracks is active
-    mSpatializer->updateActiveTracks(1);
+    mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
     requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
     ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_LOW);
 
     // requested latency mode must be free after stopping the last spatialized tracks
-    mSpatializer->updateActiveTracks(0);
+    mSpatializer->updateActiveTracks({});
     requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
     ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
 }
@@ -202,7 +234,7 @@
 
     // requested latency mode must be low software if at least one spatialized tracks is active
     // and the only supported low latency mode is low software
-    mSpatializer->updateActiveTracks(1);
+    mSpatializer->updateActiveTracks({AUDIO_CHANNEL_OUT_5POINT1});
     requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
     ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_SOFTWARE);
 
@@ -225,7 +257,7 @@
     }
 
     // requested latency mode must be free after stopping the last spatialized tracks
-    mSpatializer->updateActiveTracks(0);
+    mSpatializer->updateActiveTracks({});
     requestedLatencyMode = mSpatializer->getRequestedLatencyMode();
     ASSERT_EQ(requestedLatencyMode, AUDIO_LATENCY_MODE_FREE);
 }