audioflinger: Dump software patches

Example output:

    Software patches:
      Patch 44: thread 0xa6d83900 => thread 0xa7dfe000

This helps to identify threads used for the patch.

Test: adb shell dumpsys media.audio_flinger
Change-Id: I6c70945abd8e4abd46cd0311559d35efb6127555
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index be3286f..76661e7 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -467,6 +467,14 @@
     mPlayback.closeConnections(panel);
 }
 
+String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle)
+{
+    String8 result;
+    result.appendFormat("Patch %d: thread %p => thread %p\n",
+            myHandle, mRecord.thread().get(), mPlayback.thread().get());
+    return result;
+}
+
 /* Disconnect a patch */
 status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
 {
@@ -555,4 +563,26 @@
     return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
 }
 
+void AudioFlinger::PatchPanel::dump(int fd)
+{
+    // Only dump software patches.
+    bool headerPrinted = false;
+    for (auto& iter : mPatches) {
+        if (iter.second.isSoftware()) {
+            if (!headerPrinted) {
+                String8 header("\nSoftware patches:\n");
+                write(fd, header.string(), header.size());
+                headerPrinted = true;
+            }
+            String8 patchDump("  ");
+            patchDump.append(iter.second.dump(iter.first));
+            write(fd, patchDump.string(), patchDump.size());
+        }
+    }
+    if (headerPrinted) {
+        String8 trailing("\n");
+        write(fd, trailing.string(), trailing.size());
+    }
+}
+
 } // namespace android