From e4ba1e3a8e3b9d2c6579cbbc2ba2c065c0fb0651 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Tue, 28 Jul 2026 00:36:01 +0200 Subject: [PATCH] fix: attach the logger thread on android `JavaVM::AttachCurrentThread` takes a `JNIEnv **` on android and a `void **` on the jdk, and neither pointer converts to the other, so the `void **` cast `ScopedEnv` used broke the android build outright: jni/src/jni_logger.cpp:31:37: error: cannot initialize a parameter of type 'JNIEnv **' with an rvalue of type 'void **' The branch lives in a small `attach_current_thread` helper, so the call site reads like a plain JNI call and there is one place to look when a jni.h moves again. `GetEnv` right above it is `void **` on both and stays as it was. Deducing the pointer type from `&JavaVM::AttachCurrentThread` compiles on both platforms without the macro, but a template around a two line call reads worse than the branch it replaces. Nothing in the bindings reaches the attach path yet: the one native taking a logger, `Odr.open`, runs on the calling java thread, and the reaper that runs `~JavaLogger` is a java thread too. `HttpServer` logs from cpp-httplib worker threads though, so the attach is what keeps those lines from silently vanishing once it takes a logger. Found building odrcore 6.0.0 for OpenDocument.droid, which is the only consumer that cross compiles this for android - the jni workflow builds host side only, where the jdk declaration compiles. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01YVri5Ct8L6JYa6ce2YWTSJ --- jni/src/jni_logger.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/jni/src/jni_logger.cpp b/jni/src/jni_logger.cpp index d7f8fc60..19264551 100644 --- a/jni/src/jni_logger.cpp +++ b/jni/src/jni_logger.cpp @@ -16,6 +16,18 @@ using odr_jni::make_handle; using odr_jni::to_jstring; using odr_jni::to_string; +/// `JavaVM::AttachCurrentThread` takes a `JNIEnv **` on android and a `void **` +/// on the jdk, and neither pointer converts to the other, so the argument has +/// to be typed per platform. `GetEnv` is `void **` on both and needs none of +/// this. +jint attach_current_thread(JavaVM *const vm, JNIEnv **const env) { +#ifdef __ANDROID__ + return vm->AttachCurrentThread(env, nullptr); +#else + return vm->AttachCurrentThread(reinterpret_cast(env), nullptr); +#endif +} + /// A `JNIEnv` for the calling thread, attaching it if the JVM does not know it /// yet. Log calls arrive on whatever thread the library happens to be working /// on, which is not necessarily one the JVM started. @@ -28,8 +40,7 @@ class ScopedEnv { const jint status = m_vm->GetEnv(reinterpret_cast(&m_env), JNI_VERSION_1_6); if (status == JNI_EDETACHED) { - if (m_vm->AttachCurrentThread(reinterpret_cast(&m_env), - nullptr) == JNI_OK) { + if (attach_current_thread(m_vm, &m_env) == JNI_OK) { m_attached = true; } else { m_env = nullptr;