From 90ca53ac2d3111e893e7eaf6974ce6117c97f69f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 5 Nov 2025 12:50:30 +0100 Subject: [PATCH] android: Fixes to allow Panda to run from the adb shell --- dtool/src/prc/configPageManager.cxx | 6 +-- dtool/src/prc/notify.cxx | 49 ++++++++++++------- dtool/src/prc/pnotify.h | 2 +- .../androiddisplay/androidGraphicsPipe.cxx | 5 ++ panda/src/pipeline/threadPosixImpl.cxx | 21 ++++---- panda/src/pipeline/threadPosixImpl.h | 2 +- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/dtool/src/prc/configPageManager.cxx b/dtool/src/prc/configPageManager.cxx index 7821617bfd..e3dbcfa69b 100644 --- a/dtool/src/prc/configPageManager.cxx +++ b/dtool/src/prc/configPageManager.cxx @@ -97,7 +97,6 @@ reload_implicit_pages() { } _implicit_pages.clear(); -#ifndef ANDROID // If we are running inside a deployed application, see if it exposes // information about how the PRC data should be initialized. struct BlobInfo { @@ -129,11 +128,13 @@ reload_implicit_pages() { // const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo"); #elif defined(__EMSCRIPTEN__) const BlobInfo *blobinfo = nullptr; +#elif defined(ANDROID) + const BlobInfo *blobinfo = nullptr; #else const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(nullptr, RTLD_NOW), "blobinfo"); #endif if (blobinfo == nullptr) { -#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) +#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(ANDROID) // Clear the error flag. dlerror(); #endif @@ -482,7 +483,6 @@ reload_implicit_pages() { } } } -#endif // ANDROID if (!_loaded_implicit) { config_initialized(); diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index 5c4010264b..e3615b583a 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -27,6 +27,7 @@ #endif #ifdef ANDROID +#include #include #include "androidLogStream.h" #endif @@ -635,22 +636,7 @@ config_initialized() { // notify-output even after the initial import of Panda3D modules. However, // it cannot be changed after the first time it is set. -#if defined(ANDROID) - // Android redirects stdio and stderr to /dev/null, - // but does provide its own logging system. We use a special - // type of stream that redirects it to Android's log system. - - Notify *ptr = Notify::ptr(); - - for (int severity = 0; severity <= NS_fatal; ++severity) { - int priority = ANDROID_LOG_UNKNOWN; - if (severity != NS_unspecified) { - priority = severity + 1; - } - ptr->_log_streams[severity] = new AndroidLogStream(priority); - } - -#elif defined(__EMSCRIPTEN__) +#if defined(__EMSCRIPTEN__) // We have no writable filesystem in JavaScript. Instead, we set up a // special stream that logs straight into the Javascript console. @@ -715,11 +701,38 @@ config_initialized() { } #endif // BUILD_IPHONE } + #ifdef ANDROID + for (int severity = 0; severity <= NS_fatal; ++severity) { + ptr->_log_streams[severity] = ptr->_ostream_ptr; + } + } else { - // By default, we always redirect the notify stream to the Android log. + // By default, we always redirect the notify stream to the Android log, + // except if we are running from the adb shell. We decide this based + // on whether stderr is redirected to /dev/null. Notify *ptr = Notify::ptr(); - ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true); + struct stat a, b; + if (fstat(STDERR_FILENO, &a) == 0 && stat("/dev/null", &b) == 0 && + a.st_dev == b.st_dev && a.st_ino == b.st_ino) { + // Android redirects stdio and stderr to /dev/null, + // but does provide its own logging system. We use a special + // type of stream that redirects it to Android's log system. + for (int severity = 0; severity <= NS_fatal; ++severity) { + int priority = ANDROID_LOG_UNKNOWN; + if (severity != NS_unspecified) { + priority = severity + 1; + } + ptr->_log_streams[severity] = new AndroidLogStream(priority); + } + ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true); + } else { + // Running from the terminal, set all the log streams to point to the + // same output. + for (int severity = 0; severity <= NS_fatal; ++severity) { + ptr->_log_streams[severity] = &cerr; + } + } #endif } } diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index 76ab5be7fb..2c949f1e1a 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -102,7 +102,7 @@ private: Categories _categories; #if defined(ANDROID) - AndroidLogStream *_log_streams[NS_fatal + 1]; + std::ostream *_log_streams[NS_fatal + 1]; #elif defined(__EMSCRIPTEN__) EmscriptenLogStream *_log_streams[NS_fatal + 1]; #endif diff --git a/panda/src/androiddisplay/androidGraphicsPipe.cxx b/panda/src/androiddisplay/androidGraphicsPipe.cxx index 53f0f72310..e78c53878f 100644 --- a/panda/src/androiddisplay/androidGraphicsPipe.cxx +++ b/panda/src/androiddisplay/androidGraphicsPipe.cxx @@ -19,6 +19,8 @@ #include "config_androiddisplay.h" #include "frameBufferProperties.h" +extern IMPORT_CLASS struct android_app *panda_android_app; + TypeHandle AndroidGraphicsPipe::_type_handle; /** @@ -121,6 +123,9 @@ make_output(const std::string &name, // First thing to try: an eglGraphicsWindow if (retry == 0) { + if (panda_android_app == nullptr) { + return nullptr; + } if (((flags&BF_require_parasite)!=0)|| ((flags&BF_refuse_window)!=0)|| ((flags&BF_resizeable)!=0)|| diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 9a7598f3df..debea8f096 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -223,12 +223,15 @@ bind_thread(Thread *thread) { #ifdef ANDROID /** - * Attaches the thread to the Java virtual machine. If this returns true, a - * JNIEnv pointer can be acquired using get_jni_env(). + * Attaches the thread to the Java virtual machine. On success, returns a + * JNIEnv pointer; returns nullptr otherwise, in which case the application + * might not be running inside a Java VM. */ -bool ThreadPosixImpl:: +JNIEnv *ThreadPosixImpl:: attach_java_vm() { - assert(java_vm != nullptr); + if (java_vm == nullptr) { + return nullptr; + } JNIEnv *env; std::string thread_name = _parent_obj->get_name(); @@ -241,10 +244,10 @@ attach_java_vm() { << "Failed to attach Java VM to thread " << _parent_obj->get_name() << "!\n"; _jni_env = nullptr; - return false; + return nullptr; } _jni_env = env; - return true; + return env; } /** @@ -317,7 +320,7 @@ root_func(void *data) { #ifdef ANDROID // Attach the Java VM to allow calling Java functions in this thread. - self->attach_java_vm(); + JNIEnv *jni_env = self->attach_java_vm(); #endif self->_parent_obj->thread_main(); @@ -340,11 +343,11 @@ root_func(void *data) { #ifdef ANDROID // We cannot let the thread end without detaching it. - if (self->_jni_env != nullptr) { + if (jni_env != nullptr) { if (java_vm != nullptr) { java_vm->DetachCurrentThread(); } - self->_jni_env = nullptr; + jni_env = nullptr; } #endif diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index bb26f615e1..7859c2897d 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -59,7 +59,7 @@ public: #ifdef ANDROID INLINE JNIEnv *get_jni_env() const; - bool attach_java_vm(); + JNIEnv *attach_java_vm(); static void bind_java_thread(); #endif