Attach the runtime threads to the JVM, or nothing scans
Every scan on the phone failed with "bluetooth error: JNI call failed", which is the entire symptom: jni's Display for JniCall drops the source that says what actually went wrong. It was ThreadDetached. droidplug reaches the JVM through JavaVM::get_env(), which does not attach — it fails outright on any thread the JVM has never seen. Every BLE call in this app is made from a Tauri task, and Tauri's default runtime spawns plain Rust worker threads, so on Android no BLE call could ever have worked. This was invisible until it ran on hardware: the desktop build shares the code and does not care. Attaching inside the tasks would not have fixed it. A Tokio task can move to another worker at any .await, so the thread that starts a scan is not necessarily the one that polls it next — the attachment has to belong to the threads, not the work. on_thread_start is the hook that gets that right, and it covers the blocking pool too. Permanent rather than scoped, because a scoped attachment detaches at the end of the guard, which for a worker thread means after the first task it runs. Ordering is load-bearing at both ends. The JVM is stashed in initBtleplug, which runs before the super chain that starts us, so it is there when the runtime is built; and the runtime is installed before tauri::Builder, because async_runtime::set only affects later spawns. The scan log now carries the Debug form as well as Display. The chain read `Bluetooth(Other(JniCall(ThreadDetached)))` all along and would have named this in the first minute rather than the last. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -603,7 +603,13 @@ async fn scan_loop(mut on: watch::Receiver<bool>, tx: watch::Sender<ScanSnapshot
|
||||
generation,
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "scan failed");
|
||||
// Debug as well as Display, because the useful half of a BLE
|
||||
// failure is usually in the source chain that Display drops.
|
||||
// "bluetooth error: JNI call failed" was the *entire* symptom of
|
||||
// a detached-thread bug; the Debug form said
|
||||
// `Bluetooth(Other(JniCall(ThreadDetached)))` and would have
|
||||
// named it outright.
|
||||
tracing::warn!(error = %e, cause = ?e, "scan failed");
|
||||
ScanSnapshot {
|
||||
devices: Vec::new(),
|
||||
error: Some(e.to_string()),
|
||||
|
||||
Reference in New Issue
Block a user