Skip to content

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

Open
barbarbar338 wants to merge 2 commits into
s00d:mainfrom
barbarbar338:main
Open

barbarbar338 wants to merge 2 commits into
s00d:mainfrom
barbarbar338:main

Conversation

@barbarbar338

@barbarbar338 barbarbar338 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

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 on USB detach. The JNI bridge no longer returns from failed USB operations with a pending Java exception (it previously escaped during native-thread teardown), and closed serial handles now reject I/O and control operations with Disconnected instead of panicking in stale adapter clones.

Bug Fixes

  • Clears and maps pending Java exceptions before returning a JNI result, including a secondary exception 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, stale handles, reopen, and the desktop JNI harness at tests/jni-bridge; the harness runs with cargo test --manifest-path tests/jni-bridge/Cargo.toml and requires a JDK.

Written for commit 22d2d1e. Summary will update on new commits.

Review in cubic

Copilot AI lite review requested due to automatic review settings September 27, 2026 16:30
@bolt-new-by-stackblitz

Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/jni-bridge/Cargo.toml">

<violation number="1" location="tests/jni-bridge/Cargo.toml:8">
P2: The new regression harness is its own workspace (via this `[workspace]` opt-out), so none of the repo's CI/lint gates ever build or run it: `cargo fmt --all`, `cargo clippy --workspace`, `cargo nextest run --workspace`, and both local scripts (`scripts/ci-fast.sh`, `scripts/verify-android-usb-migration.sh`) all operate on the root workspace only, and a repo-wide search finds no invocation of `cargo test --manifest-path tests/jni-bridge/Cargo.toml` outside `tests/README.md`. That means the exact regression this PR fixes has no automated guard, and the crate's `#[path]`-included copies of `error.rs`/`fd_bridge.rs` will silently drift out of sync with the production sources. The Ubuntu CI job already installs `openjdk-17-jdk`, so wiring it in is cheap — e.g. in `.github/workflows/test.yml` after the golden-parity step: `cargo test --manifest-path tests/jni-bridge/Cargo.toml` (Ubuntu-only, since the macOS job installs no JDK).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

publish = false

# Run the actual JNI boundary against a JVM without linking desktop Tauri/WebView.
[workspace]

@cubic-dev-ai cubic-dev-ai Bot Sep 27, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new regression harness is its own workspace (via this [workspace] opt-out), so none of the repo's CI/lint gates ever build or run it: cargo fmt --all, cargo clippy --workspace, cargo nextest run --workspace, and both local scripts (scripts/ci-fast.sh, scripts/verify-android-usb-migration.sh) all operate on the root workspace only, and a repo-wide search finds no invocation of cargo test --manifest-path tests/jni-bridge/Cargo.toml outside tests/README.md. That means the exact regression this PR fixes has no automated guard, and the crate's #[path]-included copies of error.rs/fd_bridge.rs will silently drift out of sync with the production sources. The Ubuntu CI job already installs openjdk-17-jdk, so wiring it in is cheap — e.g. in .github/workflows/test.yml after the golden-parity step: cargo test --manifest-path tests/jni-bridge/Cargo.toml (Ubuntu-only, since the macOS job installs no JDK).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/jni-bridge/Cargo.toml, line 8:

<comment>The new regression harness is its own workspace (via this `[workspace]` opt-out), so none of the repo's CI/lint gates ever build or run it: `cargo fmt --all`, `cargo clippy --workspace`, `cargo nextest run --workspace`, and both local scripts (`scripts/ci-fast.sh`, `scripts/verify-android-usb-migration.sh`) all operate on the root workspace only, and a repo-wide search finds no invocation of `cargo test --manifest-path tests/jni-bridge/Cargo.toml` outside `tests/README.md`. That means the exact regression this PR fixes has no automated guard, and the crate's `#[path]`-included copies of `error.rs`/`fd_bridge.rs` will silently drift out of sync with the production sources. The Ubuntu CI job already installs `openjdk-17-jdk`, so wiring it in is cheap — e.g. in `.github/workflows/test.yml` after the golden-parity step: `cargo test --manifest-path tests/jni-bridge/Cargo.toml` (Ubuntu-only, since the macOS job installs no JDK).</comment>

<file context>
@@ -0,0 +1,13 @@
+publish = false
+
+# Run the actual JNI boundary against a JVM without linking desktop Tauri/WebView.
+[workspace]
+
+[dev-dependencies]
</file context>
Fix with cubic

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.

2 participants