Skip to content

Instantly share code, notes, and snippets.

@qdot
Created July 28, 2022 07:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qdot/210f31439888927fc23b14d2e3d40503 to your computer and use it in GitHub Desktop.
Save qdot/210f31439888927fc23b14d2e3d40503 to your computer and use it in GitHub Desktop.
btleplug android bringup w/ thread local storage
use flutter_rust_bridge::StreamSink;
use jni::objects::GlobalRef;
use jni::{JNIEnv, JavaVM, AttachGuard};
use once_cell::sync::OnceCell;
use tokio::runtime::Runtime;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::cell::RefCell;
use dashmap::DashMap;
use crate::mobile_init::Error;
static CLASS_LOADER: OnceCell<GlobalRef> = OnceCell::new();
pub static JAVAVM: OnceCell<JavaVM> = OnceCell::new();
pub static RUNTIME: OnceCell<Runtime> = OnceCell::new();
std::thread_local! {
static JNI_ENV: RefCell<Option<AttachGuard<'static>>> = RefCell::new(None);
}
pub fn create_runtime(sink: StreamSink<String>) -> Result<(), Error> {
let vm = JAVAVM.get().ok_or(Error::JavaVM)?;
let env = vm.attach_current_thread().unwrap();
setup_class_loader(&env);
let sink_clone = sink.clone();
let sink_clone_1 = sink.clone();
let sink_clone_2 = sink.clone();
let runtime = {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_name_fn(|| {
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
format!("intiface-thread-{}", id)
})
.on_thread_stop(move || {
sink_clone.add("STOPPING THREAD".to_owned());
JNI_ENV.with(|f| *f.borrow_mut() = None);
})
.on_thread_start(move || {
sink.add("WRAPPING NEW THREAD IN VM".to_string());
// We now need to call the following code block via JNI calls. God help us.
//
// java.lang.Thread.currentThread().setContextClassLoader(
// java.lang.ClassLoader.getSystemClassLoader()
// );
sink.add("Adding classloader to thread".to_string());
let vm = JAVAVM.get().unwrap();
let env = vm.attach_current_thread().unwrap();
let thread = env
.call_static_method(
"java/lang/Thread",
"currentThread",
"()Ljava/lang/Thread;",
&[],
)
.unwrap()
.l()
.unwrap();
env.call_method(
thread,
"setContextClassLoader",
"(Ljava/lang/ClassLoader;)V",
&[CLASS_LOADER.get().unwrap().as_obj().into()],
)
.unwrap();
sink.add("Classloader added to thread".to_string());
JNI_ENV.with(|f| *f.borrow_mut() = Some(env));
})
.build()
.unwrap()
};
RUNTIME.set(runtime).map_err(|_| Error::Runtime)?;
Ok(())
}
fn setup_class_loader(env: &JNIEnv) -> Result<(), Error> {
let thread = env
.call_static_method(
"java/lang/Thread",
"currentThread",
"()Ljava/lang/Thread;",
&[],
)?
.l()?;
let class_loader = env
.call_method(
thread,
"getContextClassLoader",
"()Ljava/lang/ClassLoader;",
&[],
)?
.l()?;
CLASS_LOADER
.set(env.new_global_ref(class_loader)?)
.map_err(|_| Error::ClassLoader)
}
#[no_mangle]
pub extern "C" fn JNI_OnLoad(vm: jni::JavaVM, _res: *const std::os::raw::c_void) -> jni::sys::jint {
let env = vm.get_env().unwrap();
jni_utils::init(&env).unwrap();
btleplug::platform::init(&env).unwrap();
let _ = JAVAVM.set(vm);
jni::JNIVersion::V6.into()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment