Conversation
Breaking changes addressed: - call_method_unchecked takes &[jni::sys::jvalue] instead of &[JValue]; added JValue::to_jni() at all ~40 call sites - JavaType replaced by ReturnType in call_method_unchecked return type parameter (Object/Array/Primitive instead of carrying class strings) - JMethodID no longer has a lifetime parameter - JObject::into_inner() renamed to into_raw() - set_rust_field/get_rust_field/take_rust_field marked unsafe; wrapped all 9 call sites in unsafe blocks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major restructure of the Android JNI backend for jni-rs 0.21 breaking changes. JNIEnv now requires &mut self for all operations and JObject types are no longer Copy/Clone. Key changes: - Remove env field from all JNI wrapper structs, pass &mut JNIEnv per-method-call instead - JSendFuture/JSendStream store JavaVM and obtain env via get_env() on each poll, solving the Future::poll env-passing problem - Replace JList/JMap wrapper usage with direct env.call_method() iteration to avoid &mut borrow conflicts - Extract throw_panic from throw_unwind to enable catch_unwind in ops.rs without double &mut env borrows - Use typed arrays (JByteArray, JObjectArray) per 0.21 API - Wrap call_method_unchecked in unsafe blocks with raw jvalue args - Update test infrastructure for RefCell<JNIEnv> invariance: explicit RefMut guards, scoped setup before block_on, block-scoped borrows between await points Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jni 0.21 tightened lifetime variance on JNIEnv, requiring explicit shared lifetimes wherever JObject and JNIEnv are used together. Changes: - `with_obj` closure: use HRTB `for<'env>` so JNIEnv and JPeripheral share the same lifetime, fixing the invariant mutable reference errors - `Peripheral::new`, `report_scan_result`, `adapter_report_scan_result_internal`, `adapter_report_scan_result` JNI callback: add explicit `'a` lifetime annotations tying env and JObject parameters together - `adapter_on_connection_state_changed_internal`: get address string before acquiring the MutexGuard from `get_rust_field` to avoid double-mutable borrow of env - `JUuid::as_obj().as_raw()` → `uuid.as_raw()` via Deref: `JObject::as_obj()` was removed in jni 0.21; JUuid already Derefs to JObject - Test crate `lib.rs`: update `run_test`, `initBtleplug`, and `jni_test!` macro to use `&mut JNIEnv` as required by the updated API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dplug Phase 2 of jni 0.22 migration. Non-FFI function signatures now use Env<'a> instead of JNIEnv<'a>. GlobalRef fields in Clone structs are wrapped in Arc<Global<JObject<'static>>>. Classcache stores Arc<Global<JObject<'static>>>. extern "C" FFI functions and test_utils are left as-is for later phases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 3 of jni 0.22 migration. All string literals passed to JNI API methods (find_class, get_method_id, call_method, new_object, is_instance_of, set/get/take_rust_field, call_static_method) are now wrapped with jni_str!() for names and jni_sig!() for type signatures. classcache::find_add_class uses JNIString for runtime &str conversion. register_native_methods calls wrapped in unsafe blocks (required by 0.22). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all deprecated get_env() and attach_current_thread_permanently()
calls with jni 0.22's callback-based attach_current_thread(|env| {...}).
Production code: adapter.rs (4 sites), peripheral.rs with_obj + notifications
closure (2 sites), future.rs poll_internal (1 site), stream.rs
poll_next_internal (1 site).
Test infrastructure: Replace RefCell<JNIEnv<'static>> thread-local with
with_env(f) helper that wraps attach_current_thread. This is a fundamental
rethink — 0.22's callback model means Env can't escape the closure, so the
old pattern of storing env in a RefCell is dead. All 25 test call sites
migrated from JVM_ENV.with(|cell|) to with_env(|env|). Block_on/join tests
use nested with_env calls for independent env access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Completes the jni 0.22 FFI migration: - ops.rs: JNIEnv → Env for non-FFI code, EnvUnowned + with_env() for extern "C" fn_adapter_call_internal and fn_adapter_close_internal - jni/mod.rs: extern "C" callbacks migrated to EnvUnowned + with_env() - classcache: Store Global<JClass<'static>> instead of Global<JObject<'static>> so &Global<JClass> satisfies Desc<JClass> for new_object/get_method_id - Fix <&JClass>::from(class.as_obj()) → class.as_ref() across all files - arrays.rs: new_byte_array now takes usize, remove jint cast Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- exception_check() now returns bool (was Result<bool>), remove ? - exception_occurred() now returns Option<JThrowable> (was Result), use .unwrap() - exception_clear() now returns () (was Result<()>), remove ?/.unwrap() - throw() now returns Err(JavaException) on success; use match or let _ = instead of .unwrap()/.? to preserve original control flow semantics - JObject → JThrowable/JString: use env.cast_local::<T>() (From impls removed) - NativeMethod struct fields replaced by unsafe from_raw_parts constructor - JObjectArray::from_raw now requires env param and element type param - JavaStr → MUTF8Chars in test string assertions (String::from(chars)) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rors Migrate deprecated array methods to type methods (JByteArray::set_region, JPrimitiveArray::len, JObjectArray::get_element), replace removed From<JObject> for JString with cast_local, update from_raw calls to pass env, fix jboolean (now bool) comparisons, resolve JObject Send lifetime issue in async connect by scoping Global references, and replace env.get_string with JString::mutf8_chars throughout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tern Replace deprecated JNIEnv with EnvUnowned in extern "system" FFI entry points, use with_env/into_outcome for env access, and update throw_new to use jni_str! and JNIString for 0.22 string type requirements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aVM::singleton() Convert all hand-rolled JNI wrapper types (struct + from_env + Deref + From impls) to jni 0.22's bind_java_type! macro. Replace GLOBAL_JVM OnceCell with JavaVM::singleton(). Simplify JSendFuture/JSendStream by dropping cached JMethodID fields in favour of cast_local per poll call. Net reduction of ~780 lines of boilerplate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The macro generates extern "system" trampolines with automatic catch_unwind and error-to-Java-exception propagation, replacing the manual EnvUnowned pattern that silently swallowed errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the hand-rolled DashMap-based class cache with bind_java_type!'s built-in global class caching via Reference::lookup_class(). Add bare bind_java_type! declarations for exception types, FnAdapter, and other utility classes previously only tracked in classcache. Delete classcache.rs entirely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use string literal syntax for Java class paths containing "impl"
keyword (bind_java_type! parses dot-separated idents, and "impl" is
reserved in Rust)
- Use block syntax for methods with name overrides (inline sig + block
is not supported, must use { name = "...", sig = (...) -> T } form)
- Fix borrow checker issues in JPeripheral wrapper methods by splitting
raw call and cast_local into separate statements
- Fix error type mismatches in with_obj and notification stream by
unifying on crate::Result
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…apping bind_java_type! maps JObject to Ljava/lang/Object; in JNI signatures, but Java methods using domain-specific types (UUID, Future, Stream, ScanResult, byte[], List, Map, etc.) require exact signature matches. This caused runtime "Method not found" errors and a SIGSEGV in the scan callback. Changes: - objects.rs: Move all methods with domain-typed params/returns out of bind_java_type! into manual env.call_method() with correct JNI signatures. Keep bind_java_type! for class definitions and primitive-only methods. - future.rs: Move JFuture::poll to manual impl with correct Waker/PollResult sigs - stream.rs: Move JStream::poll_next to manual impl with correct Waker/PollResult sigs - mod.rs: Fix reportScanResult to use extern "C" + EnvUnowned + with_env for raw JNI ABI compatibility (from_raw_parts requires raw C calling convention). Add env.get_java_vm() to seed JavaVM singleton during init. - peripheral_finder.rs: Add catch_unwind and logging to adapter background thread for Android debugging (silent thread death caused confusing RecvError) Verified: 28/29 Android integration tests pass on Pixel 9a. The single failure (testPropertiesContainPeripheralInfo TX power) is a test-peripheral issue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This was referenced Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
5 years of updates and I didn't do any of it because none of us have any idea how this part of the library works.
@gedgygedgy, wherever you are, your code has stood the test of time well, and even the robot says it was well structured and a pleasure to upgrade.