Skip to content

v0.13.0 - #470

Closed
qdot wants to merge 78 commits into
masterfrom
dev
Closed

v0.13.0#470
qdot wants to merge 78 commits into
masterfrom
dev

Conversation

@qdot

@qdot qdot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

No description provided.

szymonlesisz and others added 30 commits October 23, 2025 11:48
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>
Replace the deprecated `into_outcome()` pattern with jni-rs 0.22's
`resolve::<ThrowRuntimeExAndDefault>()` for scan result reporting,
FnAdapter call/close callbacks, and test initialization.

In `adapter_on_connection_state_changed`, errors were previously
swallowed with `let _ =`. Now Rust-side errors are thrown as Java
RuntimeExceptions when no JNI exception is already pending.
- bluez/peripheral: fix never_loop, use map entry API, use .values()
- bluez/adapter: collapse nested match into outer pattern
- droidplug/exceptions: remove redundant closure call, collapse nested ifs
- droidplug/ops: suppress unused_unit and type_complexity from macro-generated code
- lib.rs: allow dead_code on jni-host-tests module (callers are Android-only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete the retrieval implementation introduced by PR #448, carrying forward reusable mechanics from PR #437 while preserving dev's newer adapter-address and Android infrastructure changes.
Document platform-gated retrieval helpers, limit the unsupported helper to tests, and fix the CoreBluetooth lazy-doc continuation warning. Full strict Clippy remains blocked by unrelated pre-existing lints in corebluetooth/internal.rs.
On macOS, `peripheral:didDiscoverDescriptorsForCharacteristic:error:`
only sent the `DiscoveredCharacteristicDescriptors` event when error
was nil. If CoreBluetooth reported an error for any characteristic's
descriptors (e.g. "The specified UUID is not allowed for this
operation"), the event was never emitted and `discover_services()`
would hang indefinitely.

This moves the event send outside the error check so it always fires
— with an empty descriptor map on error — allowing service discovery
to complete. A warning is now logged when descriptor discovery fails.

This matches the Windows behaviour where
`BLEDevice::get_characteristic_descriptors` uses `unwrap_or` to
return an empty list on error (PR #362).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mika Tammi <mikatammi@gmail.com>
clear_peripherals() only cleared the AdapterManager's public DashMap
but not CoreBluetoothInternal's private HashMap. After clearing,
re-discovered peripherals were treated as DeviceUpdated (already known
internally) instead of DeviceDiscovered, so they were never re-added
to the public map.

Send a ClearPeripherals message to the CoreBluetooth thread so both
maps are cleared in sync. Windows and Android are unaffected as they
only have the single AdapterManager map.
qdot added 29 commits August 29, 2026 23:44
@qdot qdot closed this Aug 31, 2026
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.

9 participants