btleplug icon indicating copy to clipboard operation
btleplug copied to clipboard

Call `jni_utils::init` to avoid panic because of missing classes in classcache

Open Erik1000 opened this issue 8 months ago • 6 comments

I have just done a lovely 4 hour debugging session to find a panic. Because java + rust + tokio sucks ass, backtraces were invalid. Anyhow, I do not know how nobody had this problem yet, because as soon as you connect to a Peripheral, one would have noticed that it panics inside jni-utils. To be more precise this unwrap on a None value: https://github.com/deviceplug/jni-utils-rs/blob/1737fd69e8d980ec045df6daafef250f2dd07ebd/rust/future.rs#L42 JFuture::from_env is being called here: https://github.com/deviceplug/btleplug/blob/c227cc1adf885e143217f274758968afc02490fb/src/droidplug/jni/objects.rs#L150

The classcache gets populated here: https://github.com/deviceplug/jni-utils-rs/blob/1737fd69e8d980ec045df6daafef250f2dd07ebd/rust/ops.rs#L382-L393 and this ops::init function is being called automatically if jni_utls::init(env) is called.

Note that btleplugs droidplug uses one of these classes in connect: Lio/github/gedgygedgy/rust/future/Future

BUT droidplug never called jni_utils::init. In the droidplug::init function and the following call to self::jni only the btleplug classes get added to the classcache: https://github.com/deviceplug/btleplug/blob/c227cc1adf885e143217f274758968afc02490fb/src/droidplug/jni/mod.rs#L27-L53

But jni_utils::init is never called and therefore the classcache is missing all these classes: https://github.com/deviceplug/jni-utils-rs/blob/1737fd69e8d980ec045df6daafef250f2dd07ebd/rust/ops.rs#L384-L393

... which leads to total failure. To fix this, droidplug::init only has to also call jni_utils::init and everything works fine....

Erik1000 avatar Apr 22 '25 00:04 Erik1000

If you want a working example, here's my btleplug on android setup code:

https://github.com/intiface/intiface-central/blob/main/intiface-engine-flutter-bridge/src/mobile_init/setup/android.rs

That said, yeah, maybe calling jni_utils init ain't a bad idea here. I... think this should work, but I'll review when I have my wits about me more.

java + rust + tokio sucks ass

Yeah this is why this part of the library remains in a godawful but working state. I'm afraid of ever having to debug it if I try to make it better and instead fuck up lol.

qdot avatar Apr 22 '25 05:04 qdot

If you want a working example, here's my btleplug on android setup code:

https://github.com/intiface/intiface-central/blob/main/intiface-engine-flutter-bridge/src/mobile_init/setup/android.rs

That said, yeah, maybe calling jni_utils init ain't a bad idea here. I... think this should work, but I'll review when I have my wits about me more.

java + rust + tokio sucks ass

Yeah this is why this part of the library remains in a godawful but working state. I'm afraid of ever having to debug it if I try to make it better and instead fuck up lol.

Thanks for the example! I forgot about Initiface Central. But at least for me everything works with my added commit/line (on a single threaded tokio runtime). I see that you also manually call jni_utils::init. So I think integrating it in the library makes sense. https://github.com/intiface/intiface-central/blob/7fdc88ec7ceb096ad5afe62cab3f2eb53a363f59/intiface-engine-flutter-bridge/src/mobile_init/setup/android.rs#L100

Yeah this is why this part of the library remains in a godawful but working state. I'm afraid of ever having to debug it if I try to make it better and instead fuck up lol.

I feel you. But thank you very very much for adding android support in the first place with all the weird JNI and future stuff. I couldn't have done that and withstand the pain.

Erik1000 avatar Apr 22 '25 11:04 Erik1000

@qdot one question: I am still exploring the weird android stuff and I am finding interesting stuff that is not easy to find for someone who just wants to do BLE on android. For example, reconnecting via the normal connect method does not work, because Android requires you to to create a new BluetoothDevice 🐵 . I have worked around this by exposing the Peripherals::add method. So my question is: should I PR my findings/improvements from my branch or not? Maintaining this is a pain, so I understand if you do not want to even look at it again.

Erik1000 avatar Apr 26 '25 21:04 Erik1000

Feel free to at least open a PR and we can consider it. Even if I don't bring it in, having the idea hanging around might be nice. The Android core needs a complete refactor at some point, I've either just gotta find energy to do it myself (very unlikely) or a willing victim to take over maintenance (very unlikely due to the esoteric set of knowledge it takes). Assuming that does happen though, that info might be useful.

qdot avatar Apr 26 '25 22:04 qdot

Have you considered avoiding the JVM completely and talking directly to the underlying binder stuff? If you were to rewrite the android backend, I think this could make sense but I haven't looked further into it.

Erik1000 avatar Apr 27 '25 16:04 Erik1000

I... honestly had not thought of that for this project, but when I worked on FirefoxOS way the fuck back in the day this is exactly what we did. Granted we'd also stripped dalvik off completely, so I kinda figured that might not be doable here. It's something I should revisit.

qdot avatar Apr 29 '25 19:04 qdot