audioflinger: fix reused audio patch resource release

Add missing explicit release of audio HAL patch
when a patch is created reusing the same patch handle but
the source or sink devices are not on the same HW module.

Bug: 28953359

Change-Id: Ib751a4b5b2badd745f3b43e69734dd6a3e60eabe
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 0699a15..bee17fd 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -168,12 +168,46 @@
                 ALOGV("createAudioPatch() removing patch handle %d", *handle);
                 halHandle = mPatches[index]->mHalHandle;
                 Patch *removedPatch = mPatches[index];
+                // free resources owned by the removed patch if applicable
+                // 1) if a software patch is present, release the playback and capture threads and
+                // tracks created. This will also release the corresponding audio HAL patches
                 if ((removedPatch->mRecordPatchHandle
                         != AUDIO_PATCH_HANDLE_NONE) ||
                         (removedPatch->mPlaybackPatchHandle !=
                                 AUDIO_PATCH_HANDLE_NONE)) {
                     clearPatchConnections(removedPatch);
                 }
+                // 2) if the new patch and old patch source or sink are devices from different
+                // hw modules,  clear the audio HAL patches now because they will not be updated
+                // by call to create_audio_patch() below which will happen on a different HW module
+                if (halHandle != AUDIO_PATCH_HANDLE_NONE) {
+                    audio_module_handle_t hwModule = AUDIO_MODULE_HANDLE_NONE;
+                    if ((removedPatch->mAudioPatch.sources[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+                        ((patch->sources[0].type != AUDIO_PORT_TYPE_DEVICE) ||
+                          (removedPatch->mAudioPatch.sources[0].ext.device.hw_module !=
+                           patch->sources[0].ext.device.hw_module))) {
+                        hwModule = removedPatch->mAudioPatch.sources[0].ext.device.hw_module;
+                    } else if ((patch->num_sinks == 0) ||
+                            ((removedPatch->mAudioPatch.sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+                             ((patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) ||
+                              (removedPatch->mAudioPatch.sinks[0].ext.device.hw_module !=
+                               patch->sinks[0].ext.device.hw_module)))) {
+                        // Note on (patch->num_sinks == 0): this situation should not happen as
+                        // these special patches are only created by the policy manager but just
+                        // in case, systematically clear the HAL patch.
+                        // Note that removedPatch->mAudioPatch.num_sinks cannot be 0 here because
+                        // halHandle would be AUDIO_PATCH_HANDLE_NONE in this case.
+                        hwModule = removedPatch->mAudioPatch.sinks[0].ext.device.hw_module;
+                    }
+                    if (hwModule != AUDIO_MODULE_HANDLE_NONE) {
+                        ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(hwModule);
+                        if (index >= 0) {
+                            audio_hw_device_t *hwDevice =
+                                    audioflinger->mAudioHwDevs.valueAt(index)->hwDevice();
+                            hwDevice->release_audio_patch(hwDevice, halHandle);
+                        }
+                    }
+                }
                 mPatches.removeAt(index);
                 delete removedPatch;
                 break;
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index e31179c..16ec278 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -62,12 +62,24 @@
 
         struct audio_patch              mAudioPatch;
         audio_patch_handle_t            mHandle;
+        // handle for audio HAL patch handle present only when the audio HAL version is >= 3.0
         audio_patch_handle_t            mHalHandle;
+        // below members are used by a software audio patch connecting a source device from a
+        // given audio HW module to a sink device on an other audio HW module.
+        // playback thread created by createAudioPatch() and released by clearPatchConnections() if
+        // no existing playback thread can be used by the software patch
         sp<PlaybackThread>              mPlaybackThread;
+        // audio track created by createPatchConnections() and released by clearPatchConnections()
         sp<PlaybackThread::PatchTrack>  mPatchTrack;
+        // record thread created by createAudioPatch() and released by clearPatchConnections()
         sp<RecordThread>                mRecordThread;
+        // audio record created by createPatchConnections() and released by clearPatchConnections()
         sp<RecordThread::PatchRecord>   mPatchRecord;
+        // handle for audio patch connecting source device to record thread input.
+        // created by createPatchConnections() and released by clearPatchConnections()
         audio_patch_handle_t            mRecordPatchHandle;
+        // handle for audio patch connecting playback thread output to sink device
+        // created by createPatchConnections() and released by clearPatchConnections()
         audio_patch_handle_t            mPlaybackPatchHandle;
 
     };