Break cyclic binder dependency

Break a cyclic binder dependency by having media.extractor perform
datasource->close() on a separate thread.

Bug: 199822700
Test: Boot, populate device
Test: OEM verified against their tests / reproduction cases
Change-Id: I981b9230589677cc559af4cc89c1e3643ea215f7
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index baa2ca1..574fc1a 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -16,9 +16,16 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "RemoteMediaExtractor"
+
+#include <list>
+#include <pthread.h>
+#include <condition_variable>
+#include <mutex>
+
 #include <utils/Log.h>
 
 #include <binder/IPCThreadState.h>
+#include <cutils/properties.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/MediaMetricsItem.h>
 #include <media/stagefright/MediaSource.h>
@@ -90,10 +97,65 @@
     }
 }
 
+static pthread_t myThread;
+static std::list<sp<DataSource>> pending;
+static std::mutex pending_mutex;
+static std::condition_variable pending_added;
+
+static void* closing_thread_func(void *arg) {
+    while (true) {
+        sp<DataSource> ds = nullptr;
+        std::unique_lock _lk(pending_mutex);
+        pending_added.wait(_lk, []{return !pending.empty();});
+        ALOGV("worker thread wake up with %zu entries", pending.size());
+        if (!pending.empty()) {
+            ds = pending.front();
+            (void) pending.pop_front();
+        }
+        _lk.unlock();       // unique_lock is not scoped
+        if (ds != nullptr) {
+            ds->close();
+        }
+    }
+
+    ALOGE("[unexpected] worker thread quit");
+    return arg;
+}
+
+// this can be '&ds' as long as the pending.push_back() bumps the
+// reference counts to ensure the object lives long enough
+static void start_close_thread(sp<DataSource> &ds) {
+
+    // make sure we have our (single) worker thread
+    static std::once_flag sCheckOnce;
+    std::call_once(sCheckOnce, [&](){
+        pthread_attr_t attr;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+        pthread_create(&myThread, &attr, closing_thread_func, nullptr);
+        pthread_attr_destroy(&attr);
+    });
+
+    {
+        std::lock_guard _lm(pending_mutex);     // scoped, no explicit unlock
+        pending.push_back(ds);
+    }
+    pending_added.notify_one();     // get the worker thread going
+}
+
 RemoteMediaExtractor::~RemoteMediaExtractor() {
     delete mExtractor;
-    mSource->close();
-    mSource.clear();
+    // TODO(287851984) hook for changing behavior this dynamically, drop after testing
+    int8_t new_scheme = property_get_bool("debug.mediaextractor.delayedclose", 1);
+    if (new_scheme != 0) {
+        ALOGV("deferred close()");
+        start_close_thread(mSource);
+        mSource.clear();
+    } else {
+        ALOGV("immediate close()");
+        mSource->close();
+        mSource.clear();
+    }
     mExtractorPlugin = nullptr;
     // log the current record, provided it has some information worth recording
     if (MEDIA_LOG) {