-
-
Notifications
You must be signed in to change notification settings - Fork 185
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.