diff options
author | Yifan Hong <elsk@google.com> | 2021-07-14 18:16:57 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-07-14 18:16:57 +0000 |
commit | 487109e7bb7c3478306be8465b3ba959177c537d (patch) | |
tree | 47b02b67c455af638e0e12e47ef4d854f1093f72 | |
parent | 1c0d5f71509131d2fd527d629cf706f9df0d6139 (diff) | |
parent | 0f9c5c7d0bc52e16438f7abb8b5060854ccb7867 (diff) | |
download | native-487109e7bb7c3478306be8465b3ba959177c537d.tar.gz |
Merge "RpcSession attaches/detaches JVM for Java thread"
-rw-r--r-- | libs/binder/Android.bp | 1 | ||||
-rw-r--r-- | libs/binder/RpcSession.cpp | 46 | ||||
-rw-r--r-- | libs/binder/tests/binderRpcTest.cpp | 37 |
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) { |