Audio V4: Propagate track attributes to HAL

This patch does not propagate the volume.

Bug: 38184704
Bug: 69623109
Test: log metadata in HAL
Change-Id: I313563cafd9f2b29a13839c3c075beb65a170632
Signed-off-by: Kevin Rocard <krocard@google.com>
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 58fb83a..b5b50f8 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -57,6 +57,7 @@
 #include <powermanager/PowerManager.h>
 
 #include <media/audiohal/EffectsFactoryHalInterface.h>
+#include <media/audiohal/StreamHalInterface.h>
 
 #include "AudioFlinger.h"
 #include "FastMixer.h"
@@ -1554,6 +1555,7 @@
     mActiveTracksGeneration++;
     mLatestActiveTrack = track;
     ++mBatteryCounter[track->uid()].second;
+    mHasChanged = true;
     return mActiveTracks.add(track);
 }
 
@@ -1568,6 +1570,7 @@
     mActiveTracksGeneration++;
     --mBatteryCounter[track->uid()].second;
     // mLatestActiveTrack is not cleared even if is the same as track.
+    mHasChanged = true;
     return index;
 }
 
@@ -1578,6 +1581,7 @@
         logTrack("clear", track);
     }
     mLastActiveTracksGeneration = mActiveTracksGeneration;
+    if (!mActiveTracks.empty()) { mHasChanged = true; }
     mActiveTracks.clear();
     mLatestActiveTrack.clear();
     mBatteryCounter.clear();
@@ -1615,6 +1619,13 @@
 }
 
 template <typename T>
+bool AudioFlinger::ThreadBase::ActiveTracks<T>::readAndClearHasChanged() {
+    const bool hasChanged = mHasChanged;
+    mHasChanged = false;
+    return hasChanged;
+}
+
+template <typename T>
 void AudioFlinger::ThreadBase::ActiveTracks<T>::logTrack(
         const char *funcName, const sp<T> &track) const {
     if (mLocalLog != nullptr) {
@@ -2610,6 +2621,24 @@
     }
 }
 
+void AudioFlinger::PlaybackThread::updateMetadata_l()
+{
+    // TODO: add volume support
+    if (mOutput == nullptr || mOutput->stream == nullptr ||
+            !mActiveTracks.readAndClearHasChanged()) {
+        return;
+    }
+    StreamOutHalInterface::SourceMetadata metadata;
+    for (const sp<Track> &track : mActiveTracks) {
+        // No track is invalid as this is called after prepareTrack_l in the same critical section
+        metadata.tracks.push_back({
+                .usage = track->attributes().usage,
+                .content_type = track->attributes().content_type,
+                .gain = 1,
+        });
+    }
+    mOutput->stream->updateSourceMetadata(metadata);
+}
 
 status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
 {
@@ -3307,6 +3336,8 @@
 
             mActiveTracks.updatePowerState(this);
 
+            updateMetadata_l();
+
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
             // or modified if an effect is created or deleted
@@ -6118,6 +6149,17 @@
     return true;
 }
 
+void AudioFlinger::DuplicatingThread::updateMetadata_l()
+{
+    // TODO: The duplicated track metadata are stored in other threads
+    // (accessible through mActiveTracks::OutputTrack::thread()::mActiveTracks::Track::attributes())
+    // but this information can be mutated at any time by the owning threads.
+    // Taking the lock of any other owning threads is no possible due to timing constrains.
+    // Similarly, the other threads can not push the metadatas in this thread as cross deadlock
+    // would be possible.
+    // A lock-free structure needs to be used to shared the metadata (maybe an atomic shared_ptr ?).
+}
+
 uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() const
 {
     return (mWaitTimeMs * 1000) / 2;
@@ -6445,6 +6487,8 @@
 
             mActiveTracks.updatePowerState(this);
 
+            updateMetadata_l();
+
             if (allStopped) {
                 standbyIfNotAlreadyInStandby();
             }
@@ -7135,6 +7179,23 @@
     return status;
 }
 
+void AudioFlinger::RecordThread::updateMetadata_l()
+{
+    if (mInput == nullptr || mInput->stream == nullptr ||
+            !mActiveTracks.readAndClearHasChanged()) {
+        return;
+    }
+    StreamInHalInterface::SinkMetadata metadata;
+    for (const sp<RecordTrack> &track : mActiveTracks) {
+        // No track is invalid as this is called after prepareTrack_l in the same critical section
+        metadata.tracks.push_back({
+                .source = track->attributes().source,
+                .gain = 1, // capture tracks do not have volumes
+        });
+    }
+    mInput->stream->updateSinkMetadata(metadata);
+}
+
 // destroyTrack_l() must be called with ThreadBase::mLock held
 void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
 {
@@ -8099,6 +8160,8 @@
 
         mActiveTracks.updatePowerState(this);
 
+        updateMetadata_l();
+
         lockEffectChains_l(effectChains);
         for (size_t i = 0; i < effectChains.size(); i ++) {
             effectChains[i]->process_l();
@@ -8646,6 +8709,24 @@
     }
 }
 
+void AudioFlinger::MmapPlaybackThread::updateMetadata_l()
+{
+    if (mOutput == nullptr || mOutput->stream == nullptr ||
+            !mActiveTracks.readAndClearHasChanged()) {
+        return;
+    }
+    StreamOutHalInterface::SourceMetadata metadata;
+    for (const sp<MmapTrack> &track : mActiveTracks) {
+        // No track is invalid as this is called after prepareTrack_l in the same critical section
+        metadata.tracks.push_back({
+                .usage = track->attributes().usage,
+                .content_type = track->attributes().content_type,
+                .gain = mHalVolFloat, // TODO: propagate from aaudio pre-mix volume
+        });
+    }
+    mOutput->stream->updateSourceMetadata(metadata);
+}
+
 void AudioFlinger::MmapPlaybackThread::checkSilentMode_l()
 {
     if (!mMasterMute) {
@@ -8690,4 +8771,22 @@
     mInput = NULL;
     return input;
 }
+
+void AudioFlinger::MmapCaptureThread::updateMetadata_l()
+{
+    if (mInput == nullptr || mInput->stream == nullptr ||
+            !mActiveTracks.readAndClearHasChanged()) {
+        return;
+    }
+    StreamInHalInterface::SinkMetadata metadata;
+    for (const sp<MmapTrack> &track : mActiveTracks) {
+        // No track is invalid as this is called after prepareTrack_l in the same critical section
+        metadata.tracks.push_back({
+                .source = track->attributes().source,
+                .gain = 1, // capture tracks do not have volumes
+        });
+    }
+    mInput->stream->updateSinkMetadata(metadata);
+}
+
 } // namespace android