Description
On android, since JNIEnv
is used, one must ensure that calls to btleplug
which themselves use JNIEnv
to call into the JVM stay on the same thread (or at least on a thread "attached" to the JVM), because otherwise Err(btleplug::Other(JniCall(ThreadDetached))
) is returned.
JNIEnv
ensures it stays on the same thread by being !Send
and !Sync
. But Adapter
is Send
and Sync
and can therefore be moved to another thread in rust which won't be "attached" to the VM. This is a problem, because moves to other threads are not always visible. For example, when using tokio with a multi threaded runtime, futures can be moved to another thread which will lead to hard to debug errors (especially since panics/stderr is not logged to logcat by default).
I think a possible fix would be to follow the docs on jni::JavaVM
and surrounding each call into the JVM with an jni::AttachGuard
This will log the error described above:
/// This will be called from the android app in a new thread because it will block and otherwise android kills the app
#[no_mangle]
pub extern "system" fn Java_com_example_Rust_start(env: JNIEnv, _this: JClass) {
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Trace)
.with_tag("Rust"),
);
info!("Launching tokio...");
match launch(env) {
Ok(_) => log::info!("Finished ok"),
Err(e) => log::error!("Return error: {e:#?}",),
};
}
#[tokio::main(flavor = "multi_thread")]
async fn launch(env: JNIEnv) -> color_eyre::Result<()> {
btleplug::platform::init(&env)?;
let manager = Manager::new().await?;
// get the first (and usually only) ble adapter
let adapter = manager
.adapters()
.await?
.into_iter()
.next()
.ok_or(eyre!("No bluetooth adapter found"))?;
adapter.start_scan(ScanFilter::default()).await?;
let handle = tokio::spawn(async move {
warn!("in spawn");
match adapter.stop_scan().await {
Ok(_) => info!("works on other thread"),
Err(e) => error!("Not working on other thread: {e:#?}"),
}
});
info!("waiting for handle");
handle.await?;
Ok(())
}
For now, this error should be avoidable by using a single threaded tokio runtime.