Skip to content

Commit

Permalink
AsyncReadWrite L2Cap. Add Linux and Apple L2Cap Channels.
Browse files Browse the repository at this point in the history
  • Loading branch information
abezukor committed Jun 11, 2024
1 parent 39fd968 commit 5c60d1a
Show file tree
Hide file tree
Showing 18 changed files with 648 additions and 384 deletions.
14 changes: 13 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ targets = [

[features]
serde = ["dep:serde", "uuid/serde", "bluer/serde"]
l2cap = ["dep:tokio", "bluer/l2cap"]

[dependencies]
async-trait = "0.1.57"
Expand All @@ -30,7 +31,12 @@ serde = { version = "1.0.143", optional = true, features = ["derive"] }
tracing = { version = "0.1.36", default-features = false }

[dev-dependencies]
tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread", "time"] }
tokio = { version = "1.20.1", features = [
"macros",
"rt-multi-thread",
"time",
"io-util",
] }
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }

[target.'cfg(not(target_os = "linux"))'.dependencies]
Expand All @@ -57,13 +63,19 @@ tokio = { version = "1.20.1", features = ["rt-multi-thread"] }
[target.'cfg(target_os = "android")'.dependencies]
java-spaghetti = "0.2.0"
async-channel = "2.2.0"
tokio = { version = "1.20.1", features = ["rt", "io-util"], optional = true }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
async-broadcast = "0.5.1"
objc = "0.2.7"
objc_id = "0.1.1"
objc-foundation = "0.1.1"
tokio = { version = "1.20.1", features = ["net"], optional = true }

[[example]]
name = "scan"
doc-scrape-examples = true

[[example]]
name = "l2cap_client"
required-features = ["l2cap"]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ below.
| [`Device::pair_with_agent`][Device::pair_with_agent] ||||
| [`Device::unpair`][Device::unpair] ||||
| [`Device::rssi`][Device::rssi] ||||
| [`Device::open_l2cap_channel`][Device::open_l2cap_channel] | ⌛️ || ⌛️ |
| [`Service::uuid`][Service::uuid] ||| ⌛️ |
| [`Service::is_primary`][Service::is_primary] ||||
| [`Characteristic::uuid`][Characteristic::uuid] ||| ⌛️ |
Expand Down Expand Up @@ -149,6 +150,7 @@ Refer to the [API documentation] for more details.
[Device::unpair]: https://docs.rs/bluest/latest/bluest/struct.Device.html#method.unpair
[Device::discover_services]: https://docs.rs/bluest/latest/bluest/struct.Device.html#method.discover_services
[Device::rssi]: https://docs.rs/bluest/latest/bluest/struct.Device.html#method.rssi
[Device::open_l2cap_channel]: https://docs.rs/bluest/latest/bluest/struct.Device.html#method.open_l2cap_channel
[Service::uuid]: https://docs.rs/bluest/latest/bluest/struct.Service.html#method.uuid
[Service::is_primary]: https://docs.rs/bluest/latest/bluest/struct.Service.html#method.is_primary
[Service::discover_characteristics]: https://docs.rs/bluest/latest/bluest/struct.Service.html#method.discover_characteristics
Expand Down
64 changes: 64 additions & 0 deletions examples/l2cap_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This is designed to be used in conjunction with the l2cap_server example from bluer. https://github.com/bluez/bluer/blob/869dab889140e3be5a0f1791c40825730893c8b6/bluer/examples/l2cap_server.rs

use std::error::Error;

use bluest::{Adapter, Uuid as BluestUUID};
use futures_lite::StreamExt;
use tokio::io::AsyncReadExt;
use tracing::info;
use tracing::metadata::LevelFilter;

#[cfg(target_os = "linux")]
const SERVICE_UUID: BluestUUID = bluer::Uuid::from_u128(0xFEED0000F00D);

#[cfg(not(target_os = "linux"))]
use uuid::Uuid;
#[cfg(not(target_os = "linux"))]
const SERVICE_UUID: BluestUUID = Uuid::from_u128(0xFEED0000F00D);

const PSM: u16 = 0x80 + 5;

const HELLO_MSG: &[u8] = b"Hello from l2cap_server!";

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};

tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();

let adapter = Adapter::default().await.unwrap();
adapter.wait_available().await?;

info!("looking for device");
let device = adapter
.discover_devices(&[SERVICE_UUID])
.await?
.next()
.await
.ok_or("Failed to discover device")??;
info!(
"found device: {} ({:?})",
device.name().as_deref().unwrap_or("(unknown)"),
device.id()
);

adapter.connect_device(&device).await.unwrap();

let mut channel = device.open_l2cap_channel(PSM, true).await.unwrap();

info!("Reading from channel.");
let mut hello_buf = [0u8; HELLO_MSG.len()];
channel.read_exact(&mut hello_buf).await.unwrap();

info!("Got {} from channel", std::str::from_utf8(&hello_buf).unwrap());
assert_eq!(hello_buf, HELLO_MSG);
Ok(())
}
12 changes: 5 additions & 7 deletions src/android/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use java_spaghetti::Global;
use uuid::Uuid;

use super::bindings::android::bluetooth::BluetoothDevice;
use super::l2cap_channel::{L2capChannelReader, L2capChannelWriter};
use crate::pairing::PairingAgent;
use crate::{DeviceId, Result, Service, ServicesChanged};

#[derive(Clone)]
pub struct DeviceImpl {
pub(super) id: DeviceId,

#[allow(unused)]
pub(super) device: Global<BluetoothDevice>,
}

Expand Down Expand Up @@ -97,12 +98,9 @@ impl DeviceImpl {
todo!()
}

pub async fn open_l2cap_channel(
&self,
psm: u16,
secure: bool,
) -> std::prelude::v1::Result<(L2capChannelReader, L2capChannelWriter), crate::Error> {
super::l2cap_channel::open_l2cap_channel(self.device.clone(), psm, secure)
#[cfg(feature = "l2cap")]
pub async fn open_l2cap_channel(&self, psm: u16, secure: bool) -> Result<super::l2cap_channel::Channel> {
super::l2cap_channel::Channel::new(self.device.clone(), psm, secure)
}
}

Expand Down
Loading

0 comments on commit 5c60d1a

Please sign in to comment.