audio policy: fix output session effect leak

Clean up session effects in releaseOutput in case stopOutput
is not received before release.

Bug: 124689305
Test: start/stop cast screen while playing music
Change-Id: Ie0588dd3336d56d34c2d717268fcd0918cbf5717
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index b8036bb..2eb272e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -225,6 +225,21 @@
     return result;
 }
 
+void AudioPolicyService::getPlaybackClientAndEffects(audio_port_handle_t portId,
+                                                     sp<AudioPlaybackClient>& client,
+                                                     sp<AudioPolicyEffects>& effects,
+                                                     const char *context)
+{
+    Mutex::Autolock _l(mLock);
+    const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+    if (index < 0) {
+        ALOGE("%s AudioTrack client not found for portId %d", context, portId);
+        return;
+    }
+    client = mAudioPlaybackClients.valueAt(index);
+    effects = mAudioPolicyEffects;
+}
+
 status_t AudioPolicyService::startOutput(audio_port_handle_t portId)
 {
     if (mAudioPolicyManager == NULL) {
@@ -233,16 +248,9 @@
     ALOGV("startOutput()");
     sp<AudioPlaybackClient> client;
     sp<AudioPolicyEffects>audioPolicyEffects;
-    {
-        Mutex::Autolock _l(mLock);
-        const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-        if (index < 0) {
-            ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-            return INVALID_OPERATION;
-        }
-        client = mAudioPlaybackClients.valueAt(index);
-        audioPolicyEffects = mAudioPolicyEffects;
-    }
+
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
     if (audioPolicyEffects != 0) {
         // create audio processors according to stream
         status_t status = audioPolicyEffects->addOutputSessionEffects(
@@ -275,17 +283,9 @@
     ALOGV("doStopOutput");
     sp<AudioPlaybackClient> client;
     sp<AudioPolicyEffects>audioPolicyEffects;
-    {
-        Mutex::Autolock _l(mLock);
 
-        const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-        if (index < 0) {
-            ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-            return INVALID_OPERATION;
-        }
-        client = mAudioPlaybackClients.valueAt(index);
-        audioPolicyEffects = mAudioPolicyEffects;
-    }
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
     if (audioPolicyEffects != 0) {
         // release audio processors from the stream
         status_t status = audioPolicyEffects->releaseOutputSessionEffects(
@@ -315,13 +315,17 @@
 void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId)
 {
     ALOGV("doReleaseOutput from tid %d", gettid());
-    Mutex::Autolock _l(mLock);
-    const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-    if (index < 0) {
-        ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-        return;
+    sp<AudioPlaybackClient> client;
+    sp<AudioPolicyEffects> audioPolicyEffects;
+
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
+    if (audioPolicyEffects != 0 && client->active) {
+        // clean up effects if output was not stopped before being released
+        audioPolicyEffects->releaseOutputSessionEffects(
+            client->io, client->stream, client->session);
     }
-    sp<AudioPlaybackClient> client = mAudioPlaybackClients.valueAt(index);
+    Mutex::Autolock _l(mLock);
     mAudioPlaybackClients.removeItem(portId);
 
     // called from internal thread: no need to clear caller identity