Skip to content

deps(rust): upgrade jni to 0.22.4#640

Draft
kasnder wants to merge 2 commits into
masterfrom
codex/upgrade-jni-0.22
Draft

deps(rust): upgrade jni to 0.22.4#640
kasnder wants to merge 2 commits into
masterfrom
codex/upgrade-jni-0.22

Conversation

@kasnder

@kasnder kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

  • upgrade the Android-only jni dependency from 0.21.1 to 0.22.4
  • migrate native entry points to the FFI-safe EnvUnowned / borrowed Env API
  • update global references, thread attachment, JNI strings, signatures, and primitive arrays for jni 0.22

Why

Dependabot PR #629 only changed the dependency and lockfile. jni 0.22 contains breaking API changes, so the Android Rust build failed in :app:wgbridgeBuild. This PR includes the required source migration.

Impact

No intended Java/Kotlin API or runtime behavior changes. Native callbacks remain globally referenced and Tokio worker threads remain permanently attached on first use.

Validation

  • cargo test — 18 passed
  • cargo ndk ... check — armeabi-v7a, arm64-v8a, x86, and x86_64
  • Android ARM64 Clippy with warnings denied (excluding two pre-existing lints)
  • ./gradlew --console=plain testGithubDebugUnitTest

@kasnder

kasnder commented Jul 12, 2026

Copy link
Copy Markdown
Member Author

Review: deps(rust): upgrade jni to 0.22.4

Went through the Rust-side migration in jni_bindings.rs against the jni 0.22.4 API and cross-checked against Logger.java in this repo (verbosef/errorf names match the new jni_sig! calls). Summary: the migration looks correct, including the trickiest part.

Verified: "permanently attached" claim holds

The PR description says worker threads remain permanently attached, and the code swaps attach_current_thread_permanently() for attach_current_thread(closure) in both JavaCallback::with_env (jni_bindings.rs:76-90) and the on_worker_start closure in nativeStartTunnel. That name change looks suspicious at first glance — attach_current_thread in 0.21 was the scoped (auto-detach) variant. But per the 0.22 docs for JavaVM::attach_current_thread:

"If the thread was not already attached then a new attachment is made which will be automatically detached when the current thread terminates."

That's exactly the old _permanently semantics; the scoped/detach-on-return behavior moved to the new attach_current_thread_for_scope. So this is a correct, non-behavior-changing rename, not a regression. Worth calling out explicitly since it's easy to misread as a bug on a first pass.

with_native_env! macro (jni_bindings.rs:19-29)

The macro wraps each native fn body in an immediately-invoked closure (|| $body)() so that early returns inside let ... else { throw(...); return X; } guards work. Since with_native_env!(...) is always the sole tail expression of its enclosing extern "system" fn, and the inner closure's inferred return type always matches the outer fn's return type, the early-return semantics are preserved correctly for all call sites (void, jstring, jlong, jlongArray). No control-flow bug here — just flagging it as a subtle pattern worth a one-line comment for future readers, since "returns inside a macro-generated closure" is the kind of thing that looks wrong on a skim.

Minor / non-blocking nitpicks

  • jni-macros pulls in a proc-macro build chain (syn, quote, proc-macro2, rustc_version) that wasn't previously a dependency of the Android build (see Cargo.lock diff). Not a correctness issue, just a bigger trusted-build surface/slightly longer build times — probably fine to accept for a security/privacy-focused app, but worth being aware of.
  • jbool() (jni_bindings.rs, bottom) is now a literal identity function (b instead of b != 0), implying jboolean is a bool alias in 0.22. Since it's already #[allow(dead_code)] and kept "for future natives," consider just deleting it now and re-adding when actually needed — a no-op wrapper adds more confusion than value.
  • In JavaCallback::with_env's error path (jni_bindings.rs:83-89), clearing the pending exception does a second attach_current_thread call just to get an Env handle. Harmless (the thread is already attached so this is cheap), but a comment noting why a second call is needed here (borrow-checker: f already consumed the first Env) would help future readers.

Testing

The PR description lists cargo test, cargo ndk check across all 4 ABIs, Clippy with warnings denied, and the Gradle unit tests — that's solid coverage for a dependency-shape migration like this where the risk is almost entirely "does it compile with correct semantics" rather than new logic.

Nice migration — the semantic diff here is smaller than the line diff suggests, and the tricky "does permanent-attach still work" question checks out against upstream docs.

Conflicts in wgbridge-rs/Cargo.toml and jni_bindings.rs came from
master's independent daemon-attach fix for the detached-thread
GlobalRef warning (c50fdae) landing after this branch. jni 0.22's
attach_current_thread already attaches permanently with auto-detach
on thread exit, so this branch's jni-0.22 code subsumes that fix.
Kept master's newer android_logger 0.15 alongside jni 0.22.

Co-Authored-By: Claude Sonnet 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