audiopolicy: fix audioflinger patch release while using SwBridge

Test: make

When using a SwBridge, AudioFlinger creates a new patch to reach the
sink device from the SwOutput.
Upon release, the patch is released, hence the associated mixer thread
has released the patch.
Previously, setOutputDevices was forced on the SwOutput to recreate a
patch.
However, it consumes times, hence leading to for example certification
issue of CarPlay (call shall be routed within less than 100ms while in
FM)

Change-Id: I84209b8e69d202e5754946a341f8bb1d89b7d5a6
Signed-off-by: François Gaffie <francois.gaffie@renault.com>
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 89f0a6e..5f238fb 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -412,7 +412,23 @@
                 mAfPatchPanelCallback->updateOutDevicesForRecordThreads_l(devices);
             }
 
+            // For endpoint patches, we do not need to re-evaluate the device effect state
+            // if the same HAL patch is reused (see calls to mAfPatchPanelCallback below)
+            if (endpointPatch) {
+                for (auto& p : mPatches) {
+                    // end point patches are skipped so we do not compare against this patch
+                    if (!p.second.mIsEndpointPatch && patchesHaveSameRoute(
+                            newPatch.mAudioPatch, p.second.mAudioPatch)) {
+                        ALOGV("%s() Sw Bridge endpoint reusing halHandle=%d", __func__,
+                              p.second.mHalHandle);
+                        halHandle = p.second.mHalHandle;
+                        reuseExistingHalPatch = true;
+                        break;
+                    }
+                }
+            }
             mAfPatchPanelCallback->mutex().unlock();
+
             status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
             mAfPatchPanelCallback->mutex().lock();
             if (status == NO_ERROR) {
@@ -442,11 +458,18 @@
         *handle = static_cast<audio_patch_handle_t>(
                 mAfPatchPanelCallback->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH));
         newPatch.mHalHandle = halHandle;
-        if (reuseExistingHalPatch) {
-            mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
-                    oldhandle, *handle, newPatch);
-        } else {
-            mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(*handle, newPatch);
+        // Skip device effect:
+        //  -for sw bridge as effect are likely held by endpoint patches
+        //  -for endpoint reusing a HalPatch handle
+        if (!(newPatch.isSoftware()
+                || (endpointPatch && reuseExistingHalPatch))) {
+            if (reuseExistingHalPatch) {
+                mAfPatchPanelCallback->getPatchCommandThread()->updateAudioPatch(
+                        oldhandle, *handle, newPatch);
+            } else {
+                 mAfPatchPanelCallback->getPatchCommandThread()->createAudioPatch(
+                        *handle, newPatch);
+            }
         }
         if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
             addSoftwarePatchToInsertedModules_l(insertedModule, *handle, &newPatch.mAudioPatch);
@@ -734,12 +757,14 @@
  {
     ALOGV("%s handle %d", __func__, handle);
     status_t status = NO_ERROR;
+    bool doReleasePatch = true;
 
     auto iter = mPatches.find(handle);
     if (iter == mPatches.end()) {
         return BAD_VALUE;
     }
     Patch &removedPatch = iter->second;
+    const bool isSwBridge = removedPatch.isSoftware();
     const struct audio_patch &patch = removedPatch.mAudioPatch;
 
     const struct audio_port_config &src = patch.sources[0];
@@ -791,15 +816,31 @@
                     break;
                 }
             }
-            mAfPatchPanelCallback->mutex().unlock();
-            status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
-            mAfPatchPanelCallback->mutex().lock();
+            // Check whether the removed patch Hal Handle is used in another non-Endpoint patch.
+            // Since this is a non-Endpoint patch, the removed patch is not considered (it is
+            // removed later from mPatches).
+            if (removedPatch.mIsEndpointPatch) {
+                for (auto& p: mPatches) {
+                    if (!p.second.mIsEndpointPatch
+                            && p.second.mHalHandle == removedPatch.mHalHandle) {
+                        ALOGV("%s() Sw Bridge endpoint used existing halHandle=%d, do not release",
+                              __func__,  p.second.mHalHandle);
+                        doReleasePatch = false;
+                        break;
+                    }
+                }
+            }
+            if (doReleasePatch) {
+                mAfPatchPanelCallback->mutex().unlock();
+                status = thread->sendReleaseAudioPatchConfigEvent(removedPatch.mHalHandle);
+                mAfPatchPanelCallback->mutex().lock();
+            }
         } break;
         default:
             status = BAD_VALUE;
     }
 
-    erasePatch(handle);
+    erasePatch(handle, /* reuseExistingHalPatch= */ !doReleasePatch || isSwBridge);
     return status;
 }