Merge "RpcSession attaches/detaches JVM for Java thread"
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index da570f3..928f772 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -192,6 +192,7 @@
 
     header_libs: [
         "libbinder_headers",
+        "libandroid_runtime_threads_headers",
     ],
 
     export_header_lib_headers: [
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ee5e8bb..bdf1bbe 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -18,13 +18,16 @@
 
 #include <binder/RpcSession.h>
 
+#include <dlfcn.h>
 #include <inttypes.h>
 #include <poll.h>
+#include <pthread.h>
 #include <unistd.h>
 
 #include <string_view>
 
 #include <android-base/macros.h>
+#include <android_runtime/threads.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/Stability.h>
@@ -274,10 +277,53 @@
     };
 }
 
+namespace {
+// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
+// Android Runtime doesn't exist, no-op.
+class JavaThreadAttacher {
+public:
+    JavaThreadAttacher() {
+        // Use dlsym to find androidJavaAttachThread because libandroid_runtime is loaded after
+        // libbinder.
+        static auto attachFn = reinterpret_cast<decltype(&androidJavaAttachThread)>(
+                dlsym(RTLD_DEFAULT, "androidJavaAttachThread"));
+        if (attachFn == nullptr) return;
+
+        char buf[16];
+        const char* threadName = "UnknownRpcSessionThread"; // default thread name
+        if (0 == pthread_getname_np(pthread_self(), buf, sizeof(buf))) {
+            threadName = buf;
+        }
+        LOG_RPC_DETAIL("Attaching current thread %s to JVM", threadName);
+        LOG_ALWAYS_FATAL_IF(!attachFn(threadName), "Cannot attach thread %s to JVM", threadName);
+        mAttached = true;
+    }
+    ~JavaThreadAttacher() {
+        if (!mAttached) return;
+        static auto detachFn = reinterpret_cast<decltype(&androidJavaDetachThread)>(
+                dlsym(RTLD_DEFAULT, "androidJavaDetachThread"));
+        LOG_ALWAYS_FATAL_IF(detachFn == nullptr,
+                            "androidJavaAttachThread exists but androidJavaDetachThread doesn't");
+
+        LOG_RPC_DETAIL("Detaching current thread from JVM");
+        if (detachFn()) {
+            mAttached = false;
+        } else {
+            ALOGW("Unable to detach current thread from JVM");
+        }
+    }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher);
+    bool mAttached = false;
+};
+} // namespace
+
 void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) {
     sp<RpcConnection>& connection = setupResult.connection;
 
     if (setupResult.status == OK) {
+        JavaThreadAttacher javaThreadAttacher;
         while (true) {
             status_t status = session->state()->getAndExecuteCommand(connection, session,
                                                                      RpcState::CommandType::ANY);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index e452678..29bde34 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1208,6 +1208,43 @@
             << "After server->shutdown() returns true, join() did not stop after 2s";
 }
 
+TEST(BinderRpc, Java) {
+#if !defined(__ANDROID__)
+    GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on"
+                    "createRpcDelegateServiceManager() with a device attached, such test belongs "
+                    "to binderHostDeviceTest. Hence, just disable this test on host.";
+#endif // !__ANDROID__
+    sp<IServiceManager> sm = defaultServiceManager();
+    ASSERT_NE(nullptr, sm);
+    // Any Java service with non-empty getInterfaceDescriptor() would do.
+    // Let's pick batteryproperties.
+    auto binder = sm->checkService(String16("batteryproperties"));
+    ASSERT_NE(nullptr, binder);
+    auto descriptor = binder->getInterfaceDescriptor();
+    ASSERT_GE(descriptor.size(), 0);
+    ASSERT_EQ(OK, binder->pingBinder());
+
+    auto rpcServer = RpcServer::make();
+    rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    unsigned int port;
+    ASSERT_TRUE(rpcServer->setupInetServer(0, &port));
+    auto socket = rpcServer->releaseServer();
+
+    auto keepAlive = sp<BBinder>::make();
+    ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive));
+
+    auto rpcSession = RpcSession::make();
+    ASSERT_TRUE(rpcSession->setupInetClient("127.0.0.1", port));
+    auto rpcBinder = rpcSession->getRootObject();
+    ASSERT_NE(nullptr, rpcBinder);
+
+    ASSERT_EQ(OK, rpcBinder->pingBinder());
+
+    ASSERT_EQ(descriptor, rpcBinder->getInterfaceDescriptor())
+            << "getInterfaceDescriptor should not crash system_server";
+    ASSERT_EQ(OK, rpcBinder->pingBinder());
+}
+
 } // namespace android
 
 int main(int argc, char** argv) {