1#![deny(missing_docs)]
2#![deny(clippy::missing_docs_in_private_items)]
3#![warn(unused_extern_crates)]
4
5#[cfg(target_os = "android")]
9use std::sync::Arc;
10#[cfg(target_os = "android")]
11use std::sync::Mutex;
12#[cfg(target_os = "android")]
13mod android;
14#[cfg(target_os = "android")]
15pub use android::Bluetooth;
16#[cfg(target_os = "android")]
17pub use android::Java;
18#[cfg(target_os = "android")]
19use winit::platform::android::activity::AndroidApp;
20
21#[cfg(target_os = "linux")]
22mod linux;
23
24#[cfg(target_os = "windows")]
25mod windows;
26
27mod bluetooth_uuid;
28pub use bluetooth_uuid::BluetoothUuid;
29
30#[derive(Debug, serde::Deserialize, serde::Serialize)]
32pub enum BluetoothCommand {
33 DetectAdapters,
35 QueryNumAdapters,
37}
38
39pub enum MessageToBluetoothHost {
41 DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
43 ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
45 CancelDisplayPasskey,
47}
48
49#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
50pub enum MessageFromBluetoothHost {
52 PasskeyMessage(ResponseToPasskey),
54}
55
56#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
57pub enum ResponseToPasskey {
59 Yes,
61 No,
63 Cancel,
65 Waiting,
67}
68
69pub enum BluetoothResponse {
71 Adapters(usize),
73}
74
75#[derive(Clone)]
77pub struct BluetoothRfcommProfileSettings {
78 pub uuid: String,
80 pub name: Option<String>,
82 pub service_uuid: Option<String>,
84 pub channel: Option<u16>,
86 pub psm: Option<u16>,
88 pub authenticate: Option<bool>,
90 pub authorize: Option<bool>,
92 pub auto_connect: Option<bool>,
94 pub sdp_record: Option<String>,
96 pub sdp_version: Option<u16>,
98 pub sdp_features: Option<u16>,
100}
101
102#[derive(Clone)]
104pub struct BluetoothL2capProfileSettings {
105 pub uuid: String,
107 pub name: Option<String>,
109 pub service_uuid: Option<String>,
111 pub channel: Option<u16>,
113 pub psm: Option<u16>,
115 pub authenticate: Option<bool>,
117 pub authorize: Option<bool>,
119 pub auto_connect: Option<bool>,
121 pub sdp_record: Option<String>,
123 pub sdp_version: Option<u16>,
125 pub sdp_features: Option<u16>,
127}
128
129#[enum_dispatch::enum_dispatch]
131pub trait BluetoothDiscoveryTrait {}
132
133#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
135pub enum BluetoothDiscovery {
136 #[cfg(target_os = "android")]
138 Android(android::BluetoothDiscovery),
139 #[cfg(target_os = "linux")]
141 Bluez(linux::BluetoothDiscovery),
142 #[cfg(target_os = "windows")]
144 Windows(windows::BluetoothDiscovery),
145}
146
147pub enum BluetoothAdapterAddress {
149 String(String),
151 Byte([u8; 6]),
153}
154
155#[enum_dispatch::enum_dispatch]
157#[async_trait::async_trait]
158pub trait AsyncBluetoothAdapterTrait {
159 async fn register_rfcomm_profile(
161 &self,
162 settings: BluetoothRfcommProfileSettings,
163 ) -> Result<BluetoothRfcommProfileAsync, String>;
164 async fn register_l2cap_profile(
166 &self,
167 settings: BluetoothL2capProfileSettings,
168 ) -> Result<BluetoothL2capProfileAsync, String>;
169 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
171 fn start_discovery(&self) -> BluetoothDiscovery;
173 async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
175 async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
177}
178
179#[enum_dispatch::enum_dispatch]
181pub trait SyncBluetoothAdapterTrait {
182 fn register_rfcomm_profile(
184 &self,
185 settings: BluetoothRfcommProfileSettings,
186 ) -> Result<BluetoothRfcommProfileSync, String>;
187 fn register_l2cap_profile(
189 &self,
190 settings: BluetoothL2capProfileSettings,
191 ) -> Result<BluetoothL2capProfileAsync, String>;
192 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
194 fn start_discovery(&self) -> BluetoothDiscovery;
196 fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
198 fn set_discoverable(&self, d: bool) -> Result<(), ()>;
200}
201
202#[enum_dispatch::enum_dispatch]
204pub trait BluetoothAdapterTrait {
205 fn supports_async(&self) -> Option<&dyn AsyncBluetoothAdapterTrait>;
207 fn supports_sync(&self) -> Option<&dyn SyncBluetoothAdapterTrait>;
209}
210
211pub enum PairingStatus {
213 NotPaired,
215 Pairing,
217 Paired,
219 Unknown,
221}
222
223#[enum_dispatch::enum_dispatch]
225pub trait BluetoothDeviceTrait {
226 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
228
229 fn get_name(&self) -> Result<String, std::io::Error>;
231
232 fn get_address(&mut self) -> Result<String, std::io::Error>;
234
235 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
237
238 fn get_rfcomm_socket(
240 &mut self,
241 uuid: BluetoothUuid,
242 is_secure: bool,
243 ) -> Result<BluetoothSocket, String>;
244
245 fn get_l2cap_socket(
247 &mut self,
248 uuid: BluetoothUuid,
249 is_secure: bool,
250 ) -> Result<BluetoothSocket, String>;
251
252 fn run_sdp(&mut self);
254}
255
256#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
258pub enum BluetoothDevice {
259 #[cfg(target_os = "android")]
261 Android(android::BluetoothDevice),
262 #[cfg(target_os = "linux")]
264 Bluez(linux::LinuxBluetoothDevice),
265 #[cfg(target_os = "windows")]
267 Windows(windows::BluetoothDevice),
268}
269
270#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
272pub enum BluetoothAdapter {
273 #[cfg(target_os = "android")]
275 Android(android::Bluetooth),
276 #[cfg(target_os = "linux")]
278 Bluez(linux::BluetoothHandler),
279 #[cfg(target_os = "windows")]
281 Windows(windows::BluetoothHandler),
282}
283
284pub struct BluetoothAdapterBuilder {
286 #[cfg(target_os = "android")]
288 app: Option<AndroidApp>,
289 s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
291}
292
293impl Default for BluetoothAdapterBuilder {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299impl BluetoothAdapterBuilder {
300 pub fn new() -> Self {
302 Self {
303 #[cfg(target_os = "android")]
304 app: None,
305 s: None,
306 }
307 }
308
309 #[cfg(target_os = "android")]
311 pub fn with_android_app(&mut self, app: AndroidApp) {
312 self.app = Some(app);
313 }
314
315 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
317 self.s = Some(s);
318 }
319
320 pub fn build(self) -> Result<BluetoothAdapter, String> {
322 #[cfg(target_os = "android")]
323 {
324 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
325 self.app.unwrap(),
326 )));
327 }
328 Err("No synchronous builders available".to_string())
329 }
330
331 pub async fn async_build(self) -> Result<BluetoothAdapter, String> {
333 #[cfg(target_os = "android")]
334 {
335 return self.build();
336 }
337 #[cfg(target_os = "linux")]
338 {
339 return Ok(BluetoothAdapter::Bluez(
340 linux::BluetoothHandler::new(self.s.unwrap()).await?,
341 ));
342 }
343 #[cfg(target_os = "windows")]
344 {
345 return Ok(BluetoothAdapter::Windows(
346 windows::BluetoothHandler::new(self.s.unwrap()).await?,
347 ));
348 }
349 Err("No async builders available".to_string())
350 }
351}
352
353pub enum BluetoothStream {
355 #[cfg(target_os = "linux")]
357 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
358 #[cfg(target_os = "android")]
360 Android(android::RfcommStream),
361 #[cfg(target_os = "windows")]
363 Windows(windows::WindowsRfcommStream),
364}
365
366macro_rules! pin_match {
367 ($this:expr, $s:ident => $body:expr) => {
368 match $this.get_mut() {
369 #[cfg(target_os = "linux")]
370 BluetoothStream::Bluez($s) => $body,
371
372 #[cfg(target_os = "android")]
373 BluetoothStream::Android($s) => $body,
374
375 #[cfg(target_os = "windows")]
376 BluetoothStream::Windows($s) => $body,
377 }
378 };
379}
380
381impl tokio::io::AsyncWrite for BluetoothStream {
382 fn poll_write(
383 self: std::pin::Pin<&mut Self>,
384 cx: &mut std::task::Context<'_>,
385 buf: &[u8],
386 ) -> std::task::Poll<std::io::Result<usize>> {
387 pin_match!(self, s => {
388 tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf)
390 })
391 }
392
393 fn poll_flush(
394 self: std::pin::Pin<&mut Self>,
395 cx: &mut std::task::Context<'_>,
396 ) -> std::task::Poll<std::io::Result<()>> {
397 pin_match!(self, s => {
398 tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx)
399 })
400 }
401
402 fn poll_shutdown(
403 self: std::pin::Pin<&mut Self>,
404 cx: &mut std::task::Context<'_>,
405 ) -> std::task::Poll<std::io::Result<()>> {
406 pin_match!(self, s => {
407 tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx)
408 })
409 }
410}
411
412impl tokio::io::AsyncRead for BluetoothStream {
413 fn poll_read(
414 self: std::pin::Pin<&mut Self>,
415 cx: &mut std::task::Context<'_>,
416 buf: &mut tokio::io::ReadBuf<'_>,
417 ) -> std::task::Poll<std::io::Result<()>> {
418 pin_match!(self, s => {
419 tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
420 })
421 }
422}
423
424impl BluetoothStream {
425 pub fn supports_async_read(&mut self) -> Option<&mut dyn tokio::io::AsyncRead> {
427 match self {
428 #[cfg(target_os = "linux")]
429 BluetoothStream::Bluez(pin) => Some(pin),
430 #[cfg(target_os = "android")]
431 BluetoothStream::Android(_pin) => None,
432 #[cfg(target_os = "windows")]
433 BluetoothStream::Windows(_pin) => None,
434 }
435 }
436
437 pub fn supports_async_write(&mut self) -> Option<&mut dyn tokio::io::AsyncWrite> {
439 match self {
440 #[cfg(target_os = "linux")]
441 BluetoothStream::Bluez(pin) => Some(pin),
442 #[cfg(target_os = "android")]
443 BluetoothStream::Android(_pin) => None,
444 #[cfg(target_os = "windows")]
445 BluetoothStream::Windows(_pin) => None,
446 }
447 }
448
449 pub fn supports_sync_read(&mut self) -> Option<&mut dyn std::io::Read> {
451 match self {
452 #[cfg(target_os = "linux")]
453 BluetoothStream::Bluez(_pin) => None,
454 #[cfg(target_os = "android")]
455 BluetoothStream::Android(pin) => Some(pin),
456 #[cfg(target_os = "windows")]
457 BluetoothStream::Windows(pin) => Some(pin),
458 }
459 }
460
461 pub fn supports_sync_write(&mut self) -> Option<&mut dyn std::io::Write> {
463 match self {
464 #[cfg(target_os = "linux")]
465 BluetoothStream::Bluez(_pin) => None,
466 #[cfg(target_os = "android")]
467 BluetoothStream::Android(pin) => Some(pin),
468 #[cfg(target_os = "windows")]
469 BluetoothStream::Windows(pin) => Some(pin),
470 }
471 }
472}
473
474#[async_trait::async_trait]
476#[enum_dispatch::enum_dispatch]
477pub trait BluetoothRfcommConnectableAsyncTrait {
478 async fn accept(self) -> Result<BluetoothStream, String>;
480}
481
482#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableAsyncTrait)]
484pub enum BluetoothRfcommConnectableAsync {
485 #[cfg(target_os = "android")]
487 Android(android::BluetoothRfcommConnectable),
488 #[cfg(target_os = "linux")]
490 Bluez(bluer::rfcomm::ConnectRequest),
491 #[cfg(target_os = "windows")]
493 Windows(windows::BluetoothRfcommConnectable),
494}
495
496#[enum_dispatch::enum_dispatch]
498pub trait BluetoothRfcommConnectableSyncTrait {
499 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
501}
502
503#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
505pub enum BluetoothRfcommConnectableSync {
506 #[cfg(target_os = "android")]
508 Android(android::BluetoothRfcommConnectable),
509}
510
511#[enum_dispatch::enum_dispatch]
513pub trait BluetoothL2capConnectableAsyncTrait {
514 async fn accept(self) -> Result<BluetoothStream, String>;
516}
517
518#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
520pub enum BluetoothL2capConnectableAsync {
521 #[cfg(target_os = "android")]
523 Android(android::BluetoothRfcommConnectable),
524 #[cfg(target_os = "linux")]
526 Bluez(bluer::rfcomm::ConnectRequest),
527}
528
529#[enum_dispatch::enum_dispatch]
531pub trait BluetoothL2capConnectableSyncTrait {
532 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
534}
535
536#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
538pub enum BluetoothL2capConnectableSync {
539 #[cfg(target_os = "android")]
541 Android(android::BluetoothRfcommConnectable),
542}
543
544#[enum_dispatch::enum_dispatch]
546pub trait BluetoothRfcommProfileAsyncTrait {
547 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
549}
550
551#[enum_dispatch::enum_dispatch]
553pub trait BluetoothRfcommProfileSyncTrait {
554 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
556}
557
558#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
560pub enum BluetoothRfcommProfileAsync {
561 #[cfg(target_os = "linux")]
563 Bluez(bluer::rfcomm::ProfileHandle),
564 #[cfg(target_os = "windows")]
566 Windows(windows::BluetoothRfcommProfile),
567 Dummy(Dummy),
569}
570
571#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
573pub enum BluetoothRfcommProfileSync {
574 #[cfg(target_os = "android")]
576 Android(android::BluetoothRfcommProfile),
577 Dummy(Dummy),
579}
580
581#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
583pub enum BluetoothL2capProfileAsync {
584 #[cfg(target_os = "linux")]
586 Bluez(bluer::rfcomm::ProfileHandle),
587 Dummy(Dummy),
589}
590
591#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
593pub enum BluetoothL2capProfileSync {
594 #[cfg(target_os = "android")]
596 Android(android::BluetoothRfcommProfile),
597 Dummy(Dummy),
599}
600
601pub struct Dummy {}
603
604impl BluetoothRfcommProfileSyncTrait for Dummy {
605 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
606 unimplemented!()
607 }
608}
609
610impl BluetoothRfcommProfileAsyncTrait for Dummy {
611 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
612 unimplemented!()
613 }
614}
615
616#[enum_dispatch::enum_dispatch]
618pub trait BluetoothSocketTrait {
619 fn is_connected(&self) -> Result<bool, std::io::Error>;
621 fn connect(&mut self) -> Result<(), std::io::Error>;
623}
624
625impl<'a> std::io::Read for BluetoothSocket<'a> {
626 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
627 match self {
628 #[cfg(target_os = "android")]
629 BluetoothSocket::Android(a) => a.read(buf),
630 #[cfg(target_os = "linux")]
631 BluetoothSocket::Bluez(b) => b.read(buf),
632 #[cfg(target_os = "windows")]
633 BluetoothSocket::Windows(w) => w.read(buf),
634 }
635 }
636}
637
638impl<'a> std::io::Write for BluetoothSocket<'a> {
639 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
640 match self {
641 #[cfg(target_os = "android")]
642 BluetoothSocket::Android(a) => a.write(buf),
643 #[cfg(target_os = "linux")]
644 BluetoothSocket::Bluez(b) => b.write(buf),
645 #[cfg(target_os = "windows")]
646 BluetoothSocket::Windows(w) => w.write(buf),
647 }
648 }
649
650 fn flush(&mut self) -> std::io::Result<()> {
651 match self {
652 #[cfg(target_os = "android")]
653 BluetoothSocket::Android(a) => a.flush(),
654 #[cfg(target_os = "linux")]
655 BluetoothSocket::Bluez(b) => b.flush(),
656 #[cfg(target_os = "windows")]
657 BluetoothSocket::Windows(w) => w.flush(),
658 }
659 }
660}
661
662#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
664pub enum BluetoothSocket<'a> {
665 #[cfg(target_os = "android")]
667 Android(&'a mut android::BluetoothSocket),
668 #[cfg(target_os = "linux")]
670 Bluez(&'a mut linux::BluetoothRfcommSocket),
671 #[cfg(target_os = "windows")]
673 Windows(&'a mut windows::BluetoothRfcommSocket),
674}