AAudioService: add dumpsys capability

Test: adb shell dumpsys media.aaudio
Bug: 38396780
Change-Id: I8b5c421bdd1b3228d8140816a68740709aa9757b
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index f894bc0..efd663d 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -17,6 +17,8 @@
 #ifndef UTILITY_AAUDIO_UTILITIES_H
 #define UTILITY_AAUDIO_UTILITIES_H
 
+#include <algorithm>
+#include <functional>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -211,4 +213,27 @@
  */
 int32_t AAudioProperty_getHardwareBurstMinMicros();
 
+/**
+ * Try a function f until it returns true.
+ *
+ * The function is always called at least once.
+ *
+ * @param f the function to evaluate, which returns a bool.
+ * @param times the number of times to evaluate f.
+ * @param sleepMs the sleep time per check of f, if greater than 0.
+ * @return true if f() eventually returns true.
+ */
+static inline bool AAudio_tryUntilTrue(
+        std::function<bool()> f, int times, int sleepMs) {
+    static const useconds_t US_PER_MS = 1000;
+
+    sleepMs = std::max(sleepMs, 0);
+    for (;;) {
+        if (f()) return true;
+        if (times <= 1) return false;
+        --times;
+        usleep(sleepMs * US_PER_MS);
+    }
+}
+
 #endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/src/utility/HandleTracker.cpp b/media/libaaudio/src/utility/HandleTracker.cpp
index f957234..d4202db 100644
--- a/media/libaaudio/src/utility/HandleTracker.cpp
+++ b/media/libaaudio/src/utility/HandleTracker.cpp
@@ -20,11 +20,15 @@
 #include <utils/Log.h>
 
 #include <assert.h>
+#include <functional>
+#include <iomanip>
 #include <new>
+#include <sstream>
 #include <stdint.h>
 #include <utils/Mutex.h>
 
 #include <aaudio/AAudio.h>
+#include "AAudioUtilities.h"
 #include "HandleTracker.h"
 
 using android::Mutex;
@@ -93,6 +97,46 @@
     return mHandleAddresses != nullptr;
 }
 
+
+
+std::string HandleTracker::dump() const {
+    if (!isInitialized()) {
+        return "HandleTracker is not initialized\n";
+    }
+
+    std::stringstream result;
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mLock.tryLock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "HandleTracker may be deadlocked\n";
+    }
+
+    result << "Handles:\n";
+    // atLineStart() can be changed to support an arbitrary line breaking algorithm;
+    // it should return true when a new line starts.
+    // For simplicity, we will use a constant 16 items per line.
+    const auto atLineStart = [](int index) -> bool {
+        // Magic constant of 0xf used for mask to detect start every 16 items.
+        return (index & 0xf) == 0; };
+    const auto atLineEnd = [this, &atLineStart](int index) -> bool {
+        return atLineStart(index + 1) || index == mMaxHandleCount - 1; };
+
+    for (int i = 0; i < mMaxHandleCount; ++i) {
+        if (atLineStart(i)) {
+            result << "  ";
+        }
+        result << std::hex << std::setw(4) << std::setfill('0') << mHandleHeaders[i]
+               << (atLineEnd(i) ? "\n" : " ");
+    }
+
+    if (isLocked) {
+        mLock.unlock();
+    }
+    return result.str();
+}
+
 handle_tracker_slot_t HandleTracker::allocateSlot_l() {
     void **allocated = mNextFreeAddress;
     if (allocated == nullptr) {
diff --git a/media/libaaudio/src/utility/HandleTracker.h b/media/libaaudio/src/utility/HandleTracker.h
index 23a73ed..a4c51c0 100644
--- a/media/libaaudio/src/utility/HandleTracker.h
+++ b/media/libaaudio/src/utility/HandleTracker.h
@@ -18,6 +18,7 @@
 #define UTILITY_HANDLE_TRACKER_H
 
 #include <stdint.h>
+#include <string>
 #include <utils/Mutex.h>
 
 typedef int32_t  aaudio_handle_t;
@@ -53,6 +54,18 @@
     bool isInitialized() const;
 
     /**
+     * Returns HandleTracker information.
+     *
+     * Will attempt to get the object lock, but will proceed
+     * even if it cannot.
+     *
+     * Each line of information ends with a newline.
+     *
+     * @return a string representing the HandleTracker info.
+     */
+    std::string dump() const;
+
+    /**
      * Store a pointer and return a handle that can be used to retrieve the pointer.
      *
      * It is safe to call put() or remove() from multiple threads.
@@ -99,7 +112,7 @@
     // This Mutex protects the linked list of free nodes.
     // The list is managed using mHandleAddresses and mNextFreeAddress.
     // The data in mHandleHeaders is only changed by put() and remove().
-    android::Mutex              mLock;
+    mutable android::Mutex      mLock;
 
     /**
      * Pull slot off of a list of empty slots.
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 5c6825d..5f6d599 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -19,8 +19,11 @@
 #include <utils/Log.h>
 
 #include <assert.h>
+#include <functional>
 #include <map>
 #include <mutex>
+#include <sstream>
+#include <utility/AAudioUtilities.h>
 
 #include "AAudioEndpointManager.h"
 
@@ -35,6 +38,34 @@
         , mOutputs() {
 }
 
+std::string AAudioEndpointManager::dump() const {
+    std::stringstream result;
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "EndpointManager may be deadlocked\n";
+    }
+
+    size_t inputs = mInputs.size();
+    result << "Inputs: " << inputs << "\n";
+    for (const auto &input : mInputs) {
+        result << "  Input(" << input.first << ", " << input.second << ")\n";
+    }
+
+    size_t outputs = mOutputs.size();
+    result << "Outputs: " << outputs << "\n";
+    for (const auto &output : mOutputs) {
+        result << "  Output(" << output.first << ", " << output.second << ")\n";
+    }
+
+    if (isLocked) {
+        mLock.unlock();
+    }
+    return result.str();
+}
+
 AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
                                                            aaudio_direction_t direction) {
     AAudioServiceEndpoint *endpoint = nullptr;
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 899ea35..6dc5adf 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -34,6 +34,18 @@
     ~AAudioEndpointManager() = default;
 
     /**
+     * Returns EndpointManager information.
+     *
+     * Will attempt to get the object lock, but will proceed
+     * even if it cannot.
+     *
+     * Each line of information ends with a newline.
+     *
+     * @return a string representing the EndpointManager info
+     */
+    std::string dump() const;
+
+    /**
      * Find a service endpoint for the given deviceId and direction.
      * If an endpoint does not already exist then try to create one.
      *
@@ -49,7 +61,7 @@
 
 private:
 
-    std::mutex    mLock;
+    mutable std::mutex mLock;
 
     std::map<int32_t, AAudioServiceEndpointCapture *> mInputs;
     std::map<int32_t, AAudioServiceEndpointPlay *> mOutputs;
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index b0e0a74..0841127 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#include <sstream>
 //#include <time.h>
 //#include <pthread.h>
 
@@ -26,11 +27,13 @@
 #include <utils/String16.h>
 
 #include "binding/AAudioServiceMessage.h"
+#include "AAudioEndpointManager.h"
 #include "AAudioService.h"
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceStreamMMAP.h"
 #include "binding/IAAudioService.h"
+#include "ServiceUtilities.h"
 #include "utility/HandleTracker.h"
 
 using namespace android;
@@ -49,6 +52,23 @@
 AAudioService::~AAudioService() {
 }
 
+status_t AAudioService::dump(int fd, const Vector<String16>& args) {
+    std::string result;
+
+    if (!dumpAllowed()) {
+        std::stringstream ss;
+        ss << "Permission denial: can't dump AAudioService from pid="
+                << IPCThreadState::self()->getCallingPid() << ", uid="
+                << IPCThreadState::self()->getCallingUid() << "\n";
+        result = ss.str();
+        ALOGW("%s", result.c_str());
+    } else {
+        result = mHandleTracker.dump() + AAudioEndpointManager::getInstance().dump();
+    }
+    (void)write(fd, result.c_str(), result.size());
+    return NO_ERROR;
+}
+
 aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                           aaudio::AAudioStreamConfiguration &configurationOutput) {
     aaudio_result_t result = AAUDIO_OK;
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index f5a7d2f..c622f5d 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -44,6 +44,8 @@
 
     static const char* getServiceName() { return AAUDIO_SERVICE_NAME; }
 
+    virtual status_t        dump(int fd, const Vector<String16>& args) override;
+
     virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                                      aaudio::AAudioStreamConfiguration &configuration);
 
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index 7f7d465..12f7e04 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -50,6 +50,7 @@
     libbinder \
     libcutils \
     libmediautils \
+    libserviceutility \
     libutils \
     liblog