The open source OpenXR runtime
at prediction-2 229 lines 5.5 kB view raw
1// Copyright 2020, Collabora, Ltd. 2// Copyright 2024-2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Library exposing IPC server. 7 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 8 * @ingroup ipc_android 9 */ 10 11#include "jnipp.h" 12#include "jni.h" 13 14#include "wrap/android.view.h" 15 16#include "server/ipc_server.h" 17#include "server/ipc_server_interface.h" 18#include "server/ipc_server_mainloop_android.h" 19#include "util/u_logging.h" 20 21#include <android/native_window.h> 22#include <android/native_window_jni.h> 23 24#include "android/android_globals.h" 25 26#include <chrono> 27#include <memory> 28#include <thread> 29 30using wrap::android::view::Surface; 31using namespace std::chrono_literals; 32 33namespace { 34struct IpcServerHelper 35{ 36public: 37 static IpcServerHelper & 38 instance() 39 { 40 static IpcServerHelper instance; 41 return instance; 42 } 43 44 void 45 signalStartupComplete(ipc_server *s) 46 { 47 std::unique_lock<std::mutex> lock{server_mutex}; 48 startup_complete = true; 49 server = s; 50 startup_cond.notify_all(); 51 } 52 53 void 54 startServer() 55 { 56 std::unique_lock lock(server_mutex); 57 if (!server && !server_thread) { 58 server_thread = 59 std::make_unique<std::thread>([&]() { ipc_server_main_common(&ismi, &callbacks, this); }); 60 } 61 } 62 63 static void 64 signalInitFailed(xrt_result_t xret, void *data) 65 { 66 static_cast<IpcServerHelper *>(data)->signalStartupComplete(nullptr); 67 } 68 69 static void 70 signalStartupCompleteTrampoline(ipc_server *s, xrt_instance *xsint, void *data) 71 { 72 static_cast<IpcServerHelper *>(data)->signalStartupComplete(s); 73 } 74 75 static void 76 signalShuttingDownTrampoline(ipc_server *s, xrt_instance *xsint, void *data) 77 { 78 // No-op 79 } 80 81 static void 82 signalClientConnectedTrampoline(struct ipc_server *s, uint32_t client_id, void *data) 83 { 84 // No-op 85 } 86 87 static void 88 signalClientDisconnectedTrampoline(struct ipc_server *s, uint32_t client_id, void *data) 89 { 90 // No-op 91 } 92 93 int32_t 94 addClient(int fd) 95 { 96 if (!waitForStartupComplete()) { 97 return -1; 98 } 99 return ipc_server_mainloop_add_fd(server, &server->ml, fd); 100 } 101 102 int32_t 103 shutdownServer() 104 { 105 if (!server || !server_thread) { 106 // Should not happen. 107 U_LOG_E("service: shutdownServer called before server started up!"); 108 return -1; 109 } 110 111 { 112 // Wait until IPC server stop 113 std::unique_lock lock(server_mutex); 114 ipc_server_handle_shutdown_signal(server); 115 server_thread->join(); 116 server_thread.reset(nullptr); 117 server = NULL; 118 startup_complete = false; 119 } 120 121 return 0; 122 } 123 124private: 125 IpcServerHelper() {} 126 127 bool 128 waitForStartupComplete() 129 { 130 std::unique_lock<std::mutex> lock{server_mutex}; 131 bool completed = startup_cond.wait_for(lock, START_TIMEOUT_SECONDS, [&]() { return startup_complete; }); 132 133 if (!server) { 134 U_LOG_E("Failed to create ipc server"); 135 } 136 137 if (!completed) { 138 U_LOG_E("Server startup timeout!"); 139 } 140 return server && completed; 141 } 142 143 const struct ipc_server_main_info ismi = { 144 .udgci = 145 { 146 .window_title = "Monado Android Service", 147 .open = U_DEBUG_GUI_OPEN_NEVER, 148 }, 149 }; 150 151 const struct ipc_server_callbacks callbacks = { 152 .init_failed = signalInitFailed, 153 .mainloop_entering = signalStartupCompleteTrampoline, 154 .mainloop_leaving = signalShuttingDownTrampoline, 155 .client_connected = signalClientConnectedTrampoline, 156 .client_disconnected = signalClientDisconnectedTrampoline, 157 }; 158 159 //! Reference to the ipc_server, managed by ipc_server_process 160 struct ipc_server *server = NULL; 161 162 //! Mutex for starting thread 163 std::mutex server_mutex; 164 165 //! Server thread 166 std::unique_ptr<std::thread> server_thread{}; 167 168 //! Condition variable for starting thread 169 std::condition_variable startup_cond; 170 171 //! Server startup state 172 bool startup_complete = false; 173 174 //! Timeout duration in seconds 175 static constexpr std::chrono::seconds START_TIMEOUT_SECONDS = 40s; 176}; 177} // namespace 178 179extern "C" JNIEXPORT void JNICALL 180Java_org_freedesktop_monado_ipc_MonadoImpl_nativeStartServer(JNIEnv *env, jobject thiz, jobject context) 181{ 182 JavaVM *jvm = nullptr; 183 jint result = env->GetJavaVM(&jvm); 184 assert(result == JNI_OK); 185 assert(jvm); 186 187 jni::init(env); 188 jni::Object monadoImpl(thiz); 189 U_LOG_D("service: Called nativeStartServer"); 190 191 android_globals_store_vm_and_context(jvm, context); 192 193 IpcServerHelper::instance().startServer(); 194} 195 196extern "C" JNIEXPORT jint JNICALL 197Java_org_freedesktop_monado_ipc_MonadoImpl_nativeAddClient(JNIEnv *env, jobject thiz, int fd) 198{ 199 jni::init(env); 200 jni::Object monadoImpl(thiz); 201 U_LOG_D("service: Called nativeAddClient with fd %d", fd); 202 203 int native_fd = dup(fd); 204 U_LOG_D("service: transfer ownership to native and native_fd %d", native_fd); 205 206 // We try pushing the fd number to the server. If and only if we get a 0 return, has the server taken ownership. 207 return IpcServerHelper::instance().addClient(native_fd); 208} 209 210extern "C" JNIEXPORT void JNICALL 211Java_org_freedesktop_monado_ipc_MonadoImpl_nativeAppSurface(JNIEnv *env, jobject thiz, jobject surface) 212{ 213 jni::init(env); 214 Surface surf(surface); 215 jni::Object monadoImpl(thiz); 216 217 ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface); 218 android_globals_store_window((struct _ANativeWindow *)nativeWindow); 219 U_LOG_D("Stored ANativeWindow: %p", (void *)nativeWindow); 220} 221 222extern "C" JNIEXPORT jint JNICALL 223Java_org_freedesktop_monado_ipc_MonadoImpl_nativeShutdownServer(JNIEnv *env, jobject thiz) 224{ 225 jni::init(env); 226 jni::Object monadoImpl(thiz); 227 228 return IpcServerHelper::instance().shutdownServer(); 229}