From d70bbfed131a0303f8133dd6eefd37f57d0d9169 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Thu, 20 Aug 2026 22:16:23 -0400 Subject: [PATCH] Guard against Gio's null-view detach event (recents-wipe SIGABRT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gio's GioView.onDestroyView sends ViewEvent{View: 0} as its detach signal when the view is destroyed — i.e. the activity going away on a recents-wipe. An app that forwards the event to Enable() passes a zero view through to registerFragment, whose GetObjectClass(null) aborts the process (JNI DETECTED ERROR: java_object == null in call to GetObjectClass). Enable() now returns early on a zero view so all callers are covered even if they forward the event unfiltered, and registerFragment null-checks as defense in depth. Same fix as pad (fdbffc9); verified there on a Pixel 9 Pro / Android 17 where the recents swipe used to crash the app. --- ble_android.go | 9 +++++++++ jni_android.c | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/ble_android.go b/ble_android.go index 81f130a..493730b 100644 --- a/ble_android.go +++ b/ble_android.go @@ -225,6 +225,15 @@ func NewBLE() *BLE { //Enable func (b *BLE) Enable(view uintptr) { log.Printf("ble.Enable()") + // view == 0 is Gio's detach signal (GioView.onDestroyView sends + // ViewEvent{View: 0} when the view is destroyed, e.g. the activity + // going away on a recents-wipe). Nothing to register; passing the null + // ref on would abort in JNI (GetObjectClass on null). A re-attach + // arrives as a fresh event with a live view. Callers that forward the + // event unfiltered are protected by this guard. + if view == 0 { + return + } setJVM(app.JavaVM()) runInJVM(func(env *JNIEnv) { log.Printf("ble.Enable(): inside runInJVM()") diff --git a/jni_android.c b/jni_android.c index 8c3331e..8af028f 100644 --- a/jni_android.c +++ b/jni_android.c @@ -5,6 +5,14 @@ void registerFragment(JNIEnv *env, jobject view) { + if (view == NULL) { + // Detach signal (Gio sends ViewEvent{View: 0} when the view is + // destroyed, e.g. the activity going away on a recents-wipe). + // The Go side already filters these; this guard keeps the JNI + // calls safe if one ever slips through (GetObjectClass on null + // aborts the process). + return; + } jclass cls = (*env)->GetObjectClass(env, view); jmethodID mid = (*env)->GetMethodID(env, cls, "getContext", "()Landroid/content/Context;"); jobject ctx = (*env)->CallObjectMethod(env, view, mid);