summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYifan Hong <elsk@google.com>2021-07-14 18:16:57 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-07-14 18:16:57 +0000
commit487109e7bb7c3478306be8465b3ba959177c537d (patch)
tree47b02b67c455af638e0e12e47ef4d854f1093f72
parent1c0d5f71509131d2fd527d629cf706f9df0d6139 (diff)
parent0f9c5c7d0bc52e16438f7abb8b5060854ccb7867 (diff)
downloadnative-487109e7bb7c3478306be8465b3ba959177c537d.tar.gz
Merge "RpcSession attaches/detaches JVM for Java thread"
-rw-r--r--libs/binder/Android.bp1
-rw-r--r--libs/binder/RpcSession.cpp46
-rw-r--r--libs/binder/tests/binderRpcTest.cpp37
3 files changed, 84 insertions, 0 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index da570f3dc2..928f772e1d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -192,6 +192,7 @@ cc_library {
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 ee5e8bb5bb..bdf1bbef02 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 @@ RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(base::unique_fd fd) {
};
}
+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 e452678755..29bde340a1 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1208,6 +1208,43 @@ TEST(BinderRpc, Shutdown) {
<< "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) {