Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android part 1: scanning #10

Merged
merged 2 commits into from
Mar 20, 2024
Merged

Android part 1: scanning #10

merged 2 commits into from
Mar 20, 2024

Conversation

Dirbaio
Copy link
Contributor

@Dirbaio Dirbaio commented Mar 13, 2024

Part 1 of #9

This PR lays down basic infrastructure to use Android APIs from Rust, and implements Bluetooth scanning.

Some interesting thing:

  • Calling Java APIs from Rust turned out to be more annoying than I expected. Here's what I tried:
    • using the jni crate: works, requires no unsafe which is nice. However it's incredibly annoying, each call ends up being 10+ lines and there is A TON of calls you have to do (ScanRecord has a TON of getters for example 🥲).
    • using jni-bindgen + jni-android-sys: it's abandoned, jni-android-sys needs updating to newer Android API levels (for L2CAP CoC support). The approach of generating one "mega-crate" has some problems: it requires thousands of Cargo features to avoid compile time bloat, which is not accepted anymore in crates.io. Its approach is great though, and allows calling Java APIs with reasonably non-ugly code.
    • So, I've forked jni-bindgen, bringing it up to date and improving/simplifying jni-glue. It generates a standalone bindings.rs instead of a crate, so we can include it directly in bluest. It supports filtering so we only generate android.bluetooth.* and other utils. I plan on renaming the fork and publishing to crates.io under a new name, so we can remove the git dep in bluest Cargo.toml.
  • We need Java code because some APIs need you to create Java "listener" classes to receive callbacks. There's no way to magically create a Java class from Rust, so we need to write them in Java and the user has to include them in their build. I've placed them in src/android/java/ which users have to copypaste for now. I'll study providing a .jar or prebuilt gradle/maven deps in the future.
  • There's a CallbackRouter thing to receive callbacks from our java listeners and route them back to the right channel. Routing is done by an i32 ID, to avoid having to pass Rust pointers through Java which is scary.
  • It's not possible to implement Adapter::default(). Using bluetooth requires access to the BluetoothManager, which you have to obtain from an Android Context which you can't get out of thin air. I've added an Android-specific Adapter::new(vm, manager) for this.

TODO:

  • Better error handling, remove all (most?) unwraps.

@Dirbaio Dirbaio force-pushed the android branch 3 times, most recently from 4fda0e2 to 6b76b11 Compare March 14, 2024 19:09
@Dirbaio Dirbaio marked this pull request as ready for review March 14, 2024 19:56
Copy link
Owner

@alexmoon alexmoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Dirbaio! This looks like a great start and Android looks like a giant pain. We'll need to work on fairly detailed documentation on how to actually get a project to build on Android once this is close to complete.

Comment on lines 38 to 40
pub unsafe fn new(vm: *mut jni_glue::sys::JavaVM, manager: jni_glue::sys::jobject) -> Result<Self> {
let vm = VM::from_raw(vm);
let manager: Global<BluetoothManager> = Global::from_raw(vm, manager);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this just take a VM and a Global<BluetoothManager> instead of the raw pointers? I think that would push the unsafe bits onto the invariants of VM::from_raw and Global::from_raw and this could become safe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do it because it'd require making crate::android::bindings public, or at least BluetoothManager. The bindings stuff feels a bit like "implementation details" to me: we decided to abstract JNI this way, but it's not necessarily the way the user would want to. Or if they're just getting the pointer from java and passing it to bluest they might not even need any abstractions, just jni-sys.

keeping the bindings private allows upgrading jni-glue/jni-bindgen to new versions that break absolutely everything, without breaking bluest's public api.

I don't like using raw pointers and unsafe either, but given the "generate ad-hoc bindings and embed them in the crate" it makes more sense I think. If we were using jni-andtoid-sys then it'd make nmore sense to take a Global<BluetoothManager> indeed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok. Makes sense.

Cargo.toml Outdated
Comment on lines 57 to 58
[target.'cfg(target_os = "android")'.dependencies]
jni-glue = { git = "https://github.com/akiles/jni-bindgen", rev = "45ddf3129bbe5987ab19f9c9be091de3bf3c5c25" }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep main releasable on crates.io, so perhaps we should merge into an android branch until you're ready to publish jni-glue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can publish the fork right now before merging. I think it's mostly ready (or at least ready to the extent bluest needs to work...). I just need to come up with a name :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've released 🍝 java-spaghetti v0.1.0, there's no git deps anymore.

@alexmoon alexmoon merged commit 359c026 into alexmoon:main Mar 20, 2024
4 checks passed
@wuwbobo2021 wuwbobo2021 mentioned this pull request Jan 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants