Skip to content

android: Clear and describe Java exceptions in the core JNI calls - #41

Open
Bombatomica64 wants to merge 1 commit into
longbridge:mainfrom
Bombatomica64:upstream/jni-exceptions
Open

Bombatomica64 wants to merge 1 commit into
longbridge:mainfrom
Bombatomica64:upstream/jni-exceptions

Conversation

@Bombatomica64

Copy link
Copy Markdown

Part of #31 (PR 2 of the proposed list). This PR changes only the core files under src/android/. The platform packages are left for a follow-up (PR 6 in #31).

Problem

When a call into Java throws, the exception stays pending. jni 0.22 then refuses every later JNI call in the same frame. Until with_env returns, they all fail with the same bare Java exception was thrown, and at the end with_env replaces the closure's result with that exception. In the core files:

  • Some calls convert the error with map_err(|e| e.to_string()) and leave the exception pending: the IME show/hide calls, the keyboard layout and thermal queries, and update_ime_position.
  • Others clear it by hand, so its message is lost (find_app_class, set_system_chrome, the platform views). Some also drop the error with let _ =.
  • hide_keyboard_android and reset_keyboard_composition clear the exception before converting the error, so the message is lost there too.
  • set_system_chrome falls back to the pre-API-30 setSystemUiVisibility flags on any failure of getInsetsController, not only when the method doesn't exist.
  • get_string reads any object as a java.lang.String without checking its type (nativeIme passes it whatever Java sent).

Change

  • New JniResultExt::or_clear(env), for jni::errors::Result. On error it clears the pending exception and returns its toString(), so errors read like java.lang.SecurityException: <message> instead of Java exception was thrown.
  • New JniResultExt::or_catch(env, class), for an exception that is an expected result: an instance of class is cleared and gives Ok(None). Any other exception is handled like or_clear. set_system_chrome uses it so that only java/lang/NoSuchMethodError (API < 30) leads to the fallback.
  • take_exception(env) clears the pending exception and returns its description. set_host_activity uses it in place of its own clear.
  • The calls into Java in jni.rs, host.rs, platform.rs, window.rs, platform_view.rs and document_picker.rs now use these. Their errors are returned, or logged where the function returns nothing (warn; debug for the per-keystroke IME calls).
  • get_string checks is_instance_of(java/lang/String) first. For an object of another type it logs and returns an empty string.
  • The existing JniExt::e() is unchanged. Its doc now says that it leaves the exception pending, so it is for JNI calls that don't call into Java.

accessibility.rs is not changed: it works through accesskit_android's jni 0.21 and already clears its exceptions. The JNI exports (nativeIme, nativeOnDeepLink, …) are not changed either: #31's PR 1 edits the same lines, and this keeps the two PRs independent.

Testing

Run on this branch with the Android NDK 27, target aarch64-linux-android:

An earlier version of these changes ran on a OnePlus CPH2581 (Android 16), through the host entry point, as part of the full rework in Bombatomica64#1. This PR is that work ported onto the current main: #24–#30 moved the code (per-host Activities, night mode, assets), so the hunks were redone by hand rather than cherry-picked.

Follow-ups

The same helpers in the platform packages (PR 6 in #31), and the host Activity registry (PR 3), which uses or_clear.

🤖 Generated with Claude Code

A call into Java that throws leaves the exception pending. jni 0.22 then
refuses every later JNI call in the same frame, and the error only says
"JavaException". Several core calls converted the error with
map_err(to_string) and left the exception pending; others cleared it by
hand, so its message was lost, or dropped the error with `let _ =`.

The new JniResultExt::or_clear clears the pending exception and returns
its toString(), so errors read like "java.lang.SecurityException: ...".
or_catch makes an expected exception explicit: the system chrome now
falls back to the pre-API-30 flags only on NoSuchMethodError, where any
failure of getInsetsController used to.

The calls into Java in jni.rs, host.rs, platform.rs, window.rs,
platform_view.rs and document_picker.rs use them, and their errors are
returned or logged. get_string now checks that the object is a
java.lang.String before reading it; before, any object handed to it was
read as one. The platform packages are left for a follow-up.

Part of longbridge#31.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant