-
Notifications
You must be signed in to change notification settings - Fork 23
Introduce GATT functionality for Android #40
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
base: main
Are you sure you want to change the base?
Conversation
|
In I just found the |
|
I have been also been working on this is an as yet unpublished crate (still working out the bugs). I also have a branch that uses that crate in bluest (See the basic commit MaticianInc@76e6457). We are using it in our (still in beta) Android app at Matic. I have been waiting to publish it until we have something stable. I can either help review this or publish my version when is its ready. |
|
@abezukor I'm glad to see yet another solution of Android support; however, where is the |
Will do. I wasnt planning on publishing it until I had fixed all the bugs, but I can if you want to collaborate. |
|
@abezukor Would you please describe about those most significant bugs? I think It's difficult to determine which implementation is more viable if you don't make it public. |
I think An adapter config parameter to enable requesting the MTU on connection would also be good. |
I hope to open up my code later this week. It has been an internal project until now, and I need to spend some time writing documentation etc. |
|
@abezukor I am willing to dismiss my current implementation if the On the other hand, something like blurdroid (with C code) might not be acceptable. I guess you are not making something similar to that crate. |
|
@abezukor I can't wait for knowing if your implementation has structural advantages over this attempt. Could you make it public earlier, even with minor bugs? Otherwise I may continue to improve my own implementation. |
I have released a version https://github.com/abezukor/android_rust. I had to re-organize a bunch of it to make it compatible with Quickly looking at your implementation, the main structural difference is I decided to put more logic on the java side, because I found it more ergonomic to write that way. I also have created a bunch of components that are reused in an NSD (mDNS) crate. |
|
@abezukor Some notes for |
|
I am still confused about the schedule of "planned support for Android". Maybe it's time to make the decision: to close #40 as unmerged and accept the implementation from @abezukor, or to check this PR seriously. A few changes could be made here if #40 should be kept:
|
|
I have just fixed some problems noted above, and solved the problems of no responding in case of scanning without permission and reading/writing data while the device is disconnected. Test case to be built by cargo-apk or cargo-apk2[workspace]
[package]
name = "bluest-test"
version = "0.1.0"
edition = "2024"
publish = false
[dependencies]
bluest = { path = "..", features = ["unstable", "l2cap"] }
tracing = "0.1.36"
tracing-subscriber = "0.3.15"
# android-activity uses `log`
log = "0.4"
tracing-log = "0.2.0"
ndk-context = "0.1.1"
android-activity = { version = "0.6", features = ["native-activity"] }
# jni-min-helper = { version = "0.3", features = ["futures"] }
futures-lite = "2.6"
async-channel = "2.2.0"
futures-timer = "3.0.3"
[lib]
crate-type = ["cdylib"]
[package.metadata.android]
package = "com.example.bluest_test"
build_targets = ["aarch64-linux-android"]
# "Arm64V8a" is for `cargo-apk2` which is required for putting `PermActivity` in the app
# build_targets = [ "Arm64V8a" ]
# put <https://docs.rs/crate/jni-min-helper/0.3.2/source/java/PermActivity.java> in this folder
# java_sources = "java"
# Android 12 or above may require runtime permission request.
# <https://developer.android.com/develop/connectivity/bluetooth/bt-permissions>
# <https://docs.rs/jni-min-helper/0.3.2/jni_min_helper/struct.PermissionRequest.html>
# Use `cargo-apk2` or check <https://github.com/rust-mobile/cargo-apk/pull/72>
[package.metadata.android.sdk]
min_sdk_version = 23
target_sdk_version = 33
[[package.metadata.android.uses_feature]]
name = "android.hardware.bluetooth_le"
required = true
[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_SCAN"
min_sdk_version = 31
[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_CONNECT"
min_sdk_version = 31
[[package.metadata.android.uses_permission]]
name = "android.permission.ACCESS_FINE_LOCATION"
# TODO: uncomment this line when `usesPermissionFlags` becomes supported in `cargo-apk2`.
# max_sdk_version = 30
[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH"
max_sdk_version = 30
[[package.metadata.android.uses_permission]]
name = "android.permission.BLUETOOTH_ADMIN"
max_sdk_version = 30
# these are for `cargo-apk2`
# [[package.metadata.android.application.activity]]
# name = "android.app.NativeActivity"
# [[package.metadata.android.application.activity.intent_filter]]
# actions = ["android.intent.action.VIEW", "android.intent.action.MAIN"]
# categories = ["android.intent.category.LAUNCHER"]
# [[package.metadata.android.application.activity.meta_data]]
# name = "android.app.lib_name"
# value = "bluest_test"
# [[package.metadata.android.application.activity]]
# name = "rust.jniminhelper.PermActivity"#![allow(unused)]
use bluest::btuuid::bluetooth_uuid_from_u16;
use bluest::Uuid;
use futures_timer::Delay;
use std::time::Duration;
use android_activity::{AndroidApp, MainEvent, PollEvent};
use futures_lite::{FutureExt, StreamExt};
use tracing::{error, info};
#[unsafe(no_mangle)]
fn android_main(app: AndroidApp) {
// android_logger::init_once(
// android_logger::Config::default()
// .with_max_level(log::LevelFilter::Info)
// .with_tag("bluest_test".as_bytes()),
// );
// NOTE: View tracing log on the host with `adb logcat RustStdoutStderr:D '*:S'`.
let subscriber = tracing_subscriber::FmtSubscriber::builder().without_time().finish();
tracing::subscriber::set_global_default(subscriber).expect("setting tracing default failed");
tracing_log::LogTracer::init().expect("setting log tracer failed");
// calling `block_on` with bluetooth operations in `android_main` thread may block forever...
let (tx, rx) = async_channel::unbounded();
std::thread::spawn(move || {
let res = futures_lite::future::block_on(async_main().or(async {
let _ = rx.recv().await;
info!("async thread received stop signal.....");
Ok(())
}));
if let Err(e) = res {
info!("async thread's `block_on` received error: {e}");
} else {
info!("async thread terminates itself after it received stop signal.");
}
});
let mut on_destroy = false;
loop {
app.poll_events(None, |event| match event {
PollEvent::Main(MainEvent::Stop) => {
info!("Main Stop Event.");
let _ = tx.send(());
}
PollEvent::Main(MainEvent::Destroy) => {
on_destroy = true;
}
_ => (),
});
if on_destroy {
return;
}
}
}
async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
// Currently this requires `cargo-apk2` instead of `cargo-apk` to work.
// But this is required if the user chooses to confirm permission on every startup.
/*
let req = jni_min_helper::PermissionRequest::request(
"BLE Test",
[
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT",
"android.permission.ACCESS_FINE_LOCATION",
],
)?;
if let Some(req) = req {
info!("requesting permissions...");
let result = req.await;
for (perm_name, granted) in result.unwrap_or_default() {
if !granted {
eprintln!("{perm_name} is denied by the user.");
return Ok(());
}
}
};
*/
let adapter = bluest::Adapter::with_config(bluest::AdapterConfig::default()).await?;
adapter.wait_available().await?;
info!("adapter is now available.");
// Please put your test case here.
info!("async task terminates itself.");
Ok(())
} |
|
I did a test that adds log messages for ACTION_ACL_CONNECTED and I am afraid that the problem marked by I will not make further changes without your suggestions. Thank you. |
|
I realized a structural problem in my current implementation: while it seems possible to reconnect a device with private random resolvable address, Another problem found in my I think the implementation from @abezukor is (currently) less problematic at these points. |
Note: this is still experimental, and behaviors on services changed event is untested; pairing with user confirmation needs testing on newest Android versions.