fix(android): prevent app crashes when USB devices disconnect

Merged
BA
4d ago
+227-35

USB unplug can terminate an Android app when reconnect tries to open the removed device. openDeviceFd throws a Java exception, but with_env previously returned through ? before clearing it. The pending exception then escapes native-thread teardown as an uncaught Android exception.

This change always clears and maps pending Java exceptions before returning a JNI result, including exceptions raised while reading the original exception message. It also rejects I/O and control operations on closed USB handles, preventing stale adapter clones from panicking on endpoints cleared during detach.

Validation:

  • Real-JVM regression reproduces the pending-exception leak with the old control flow and passes with the fix; all 5 harness tests pass.
  • USB driver suite passes, including three new stale-handle/close/reopen regressions.
  • The consuming Equinno Android app was rebuilt and tested by its owner, who confirmed that unplugging the dongle no longer crashes the app. Wireless logcat captured the original ExecutionException: IOException: device not found failure.

The JNI harness runs independently of desktop Tauri/WebView dependencies: cargo test --manifest-path tests/jni-bridge/Cargo.toml (requires a JDK and JAVA_HOME).


Summary by cubic

Prevents Android crashes when USB devices disconnect. The JNI bridge now clears and maps pending Java exceptions before returning from failed USB operations, so they no longer escape during native-thread teardown. Closed serial handles reject I/O and control operations with Disconnected instead of panicking in stale adapter clones.

Bug Fixes

  • Clears pending Java exceptions, including secondary exceptions raised while reading the original message.
  • Closed handles return Disconnected for read/write, line and flow control, DTR/RTS, break, purge, and reader setup.
  • Adds regression tests for the exception leak and stale handles, plus a desktop JNI harness at tests/jni-bridge that runs in CI on Ubuntu (cargo test --manifest-path tests/jni-bridge/Cargo.toml, requires a JDK).