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
30mod sdp;
31
32#[derive(Debug, serde::Deserialize, serde::Serialize)]
34pub enum BluetoothCommand {
35 DetectAdapters,
37 QueryNumAdapters,
39}
40
41pub enum MessageToBluetoothHost {
43 DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
45 ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
47 CancelDisplayPasskey,
49}
50
51#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
52pub enum MessageFromBluetoothHost {
54 PasskeyMessage(ResponseToPasskey),
56}
57
58#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
59pub enum ResponseToPasskey {
61 Yes,
63 No,
65 Cancel,
67 Waiting,
69}
70
71pub enum BluetoothResponse {
73 Adapters(usize),
75}
76
77#[derive(Clone, Debug)]
79pub struct BluetoothRfcommProfileSettings {
80 pub uuid: String,
82 pub name: Option<String>,
84 pub service_uuid: Option<String>,
86 pub channel: Option<u16>,
88 pub psm: Option<u16>,
90 pub authenticate: Option<bool>,
92 pub authorize: Option<bool>,
94 pub auto_connect: Option<bool>,
96 pub sdp_record: Option<String>,
98 pub sdp_version: Option<u16>,
100 pub sdp_features: Option<u16>,
102}
103
104#[derive(Clone)]
106pub struct BluetoothL2capProfileSettings {
107 pub uuid: String,
109 pub name: Option<String>,
111 pub service_uuid: Option<String>,
113 pub channel: Option<u16>,
115 pub psm: Option<u16>,
117 pub authenticate: Option<bool>,
119 pub authorize: Option<bool>,
121 pub auto_connect: Option<bool>,
123 pub sdp_record: Option<String>,
125 pub sdp_version: Option<u16>,
127 pub sdp_features: Option<u16>,
129}
130
131#[enum_dispatch::enum_dispatch]
133pub trait BluetoothDiscoveryTrait {}
134
135#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
137pub enum BluetoothDiscovery {
138 #[cfg(target_os = "android")]
140 Android(android::BluetoothDiscovery),
141 #[cfg(target_os = "linux")]
143 Bluez(linux::BluetoothDiscovery),
144 #[cfg(target_os = "windows")]
146 Windows(windows::BluetoothDiscovery),
147}
148
149pub enum BluetoothAdapterAddress {
151 String(String),
153 Byte([u8; 6]),
155}
156
157#[enum_dispatch::enum_dispatch]
159#[async_trait::async_trait]
160pub trait AsyncBluetoothAdapterTrait {
161 async fn register_rfcomm_profile(
163 &self,
164 settings: BluetoothRfcommProfileSettings,
165 ) -> Result<BluetoothRfcommProfileAsync, String>;
166 async fn register_l2cap_profile(
168 &self,
169 settings: BluetoothL2capProfileSettings,
170 ) -> Result<BluetoothL2capProfileAsync, String>;
171 async fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
173 fn start_discovery(&self) -> BluetoothDiscovery;
175 async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
177 async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
179}
180
181#[enum_dispatch::enum_dispatch]
183pub trait SyncBluetoothAdapterTrait {
184 fn register_rfcomm_profile(
186 &self,
187 settings: BluetoothRfcommProfileSettings,
188 ) -> Result<BluetoothRfcommProfileSync, String>;
189 fn register_l2cap_profile(
191 &self,
192 settings: BluetoothL2capProfileSettings,
193 ) -> Result<BluetoothL2capProfileAsync, String>;
194 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
196 fn start_discovery(&self) -> BluetoothDiscovery;
198 fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
200 fn set_discoverable(&self, d: bool) -> Result<(), ()>;
202}
203
204#[enum_dispatch::enum_dispatch]
206pub trait BluetoothAdapterTrait {
207 fn supports_async(&self) -> Option<&dyn AsyncBluetoothAdapterTrait>;
209 fn supports_sync(&self) -> Option<&dyn SyncBluetoothAdapterTrait>;
211}
212
213pub enum PairingStatus {
215 NotPaired,
217 Pairing,
219 Paired,
221 Unknown,
223}
224
225fn uuid16(uuid: u16) -> Vec<u8> {
226 let mut v = Vec::new();
227 v.push(0x19); v.extend_from_slice(&uuid.to_be_bytes());
229 v
230}
231
232fn build_sdp_request(uuid: u16, transaction_id: u16) -> Vec<u8> {
233 let mut pdu = Vec::new();
234
235 pdu.push(0x06);
237
238 let mut params = Vec::new();
240
241 params.extend_from_slice(&transaction_id.to_be_bytes());
243
244 let uuid = uuid16(uuid);
248 let mut search = Vec::new();
249 search.push(0x35); search.push(uuid.len() as u8);
251 search.extend_from_slice(&uuid);
252 params.extend_from_slice(&search);
253
254 let mut attrs = Vec::new();
256 attrs.push(0x35);
257 attrs.push(7);
258 attrs.extend_from_slice(&[
259 0x09, 0x00, 0x00, 0x09, 0xFF, 0xFF, ]);
262 params.extend_from_slice(&attrs);
263
264 params.extend_from_slice(&0xFFFFu16.to_be_bytes());
266
267 params.push(0x00);
269
270 let len = params.len() as u16;
272 let mut out = Vec::new();
273 out.push(0x06);
274 out.extend_from_slice(&transaction_id.to_be_bytes());
275 out.extend_from_slice(&len.to_be_bytes());
276 out.extend_from_slice(¶ms);
277
278 out
279}
280
281#[async_trait::async_trait]
282#[enum_dispatch::enum_dispatch]
283pub trait BluetoothDeviceAsyncTrait {
285 async fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
287 async fn get_name(&self) -> Result<String, std::io::Error>;
289 async fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
291}
292
293#[enum_dispatch::enum_dispatch]
294pub trait BluetoothDeviceSyncTrait {
296 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
298 fn get_name(&self) -> Result<String, std::io::Error>;
300 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
302}
303
304#[enum_dispatch::enum_dispatch]
306pub trait BluetoothDeviceTrait {
307 fn supports_async(&mut self) -> Option<&mut dyn BluetoothDeviceAsyncTrait>;
309 fn supports_sync(&mut self) -> Option<&mut dyn BluetoothDeviceSyncTrait>;
311 fn get_address(&mut self) -> Result<String, std::io::Error>;
313 fn get_rfcomm_socket(
315 &mut self,
316 channel: u8,
317 is_secure: bool,
318 ) -> Result<BluetoothSocket, String>;
319
320 fn get_l2cap_socket(&mut self, psm: u16, is_secure: bool) -> Result<BluetoothSocket, String>;
322
323 fn run_sdp(&mut self, uuid: BluetoothUuid) -> Result<sdp::ServiceRecord, String> {
325 if let Ok(a) = self.get_address() {
326 return sdp::run_sdp(&a, uuid.get_16_bit_id()).map_err(|e| e.to_string());
327 }
328 Err("Sdp failed".to_string())
329 }
330}
331
332#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
334pub enum BluetoothDevice {
335 #[cfg(target_os = "android")]
337 Android(android::BluetoothDevice),
338 #[cfg(target_os = "linux")]
340 Bluez(linux::LinuxBluetoothDevice),
341 #[cfg(target_os = "windows")]
343 Windows(windows::BluetoothDevice),
344}
345
346#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
348pub enum BluetoothAdapter {
349 #[cfg(target_os = "android")]
351 Android(android::Bluetooth),
352 #[cfg(target_os = "linux")]
354 Bluez(linux::BluetoothHandler),
355 #[cfg(target_os = "windows")]
357 Windows(windows::BluetoothHandler),
358}
359
360pub struct BluetoothAdapterBuilder {
362 #[cfg(target_os = "android")]
364 app: Option<AndroidApp>,
365 s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
367}
368
369impl Default for BluetoothAdapterBuilder {
370 fn default() -> Self {
371 Self::new()
372 }
373}
374
375impl BluetoothAdapterBuilder {
376 pub fn new() -> Self {
378 Self {
379 #[cfg(target_os = "android")]
380 app: None,
381 s: None,
382 }
383 }
384
385 #[cfg(target_os = "android")]
387 pub fn with_android_app(&mut self, app: AndroidApp) {
388 self.app = Some(app);
389 }
390
391 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
393 self.s = Some(s);
394 }
395
396 pub fn build(self) -> Result<BluetoothAdapter, String> {
398 #[cfg(target_os = "android")]
399 {
400 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
401 self.app.unwrap(),
402 )));
403 }
404 Err("No synchronous builders available".to_string())
405 }
406
407 pub async fn async_build(self) -> Result<BluetoothAdapter, String> {
409 #[cfg(target_os = "android")]
410 {
411 return self.build();
412 }
413 #[cfg(target_os = "linux")]
414 {
415 return Ok(BluetoothAdapter::Bluez(
416 linux::BluetoothHandler::new(self.s.unwrap()).await?,
417 ));
418 }
419 #[cfg(target_os = "windows")]
420 {
421 return Ok(BluetoothAdapter::Windows(
422 windows::BluetoothHandler::new(self.s.unwrap()).await?,
423 ));
424 }
425 Err("No async builders available".to_string())
426 }
427}
428
429pub enum BluetoothStream {
431 #[cfg(target_os = "linux")]
433 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
434 #[cfg(target_os = "android")]
436 Android(android::RfcommStream),
437 #[cfg(target_os = "windows")]
439 Windows(windows::WindowsRfcommStream),
440}
441
442macro_rules! pin_match {
443 ($this:expr, $s:ident => $body:expr) => {
444 match $this.get_mut() {
445 #[cfg(target_os = "linux")]
446 BluetoothStream::Bluez($s) => $body,
447
448 #[cfg(target_os = "android")]
449 BluetoothStream::Android($s) => $body,
450
451 #[cfg(target_os = "windows")]
452 BluetoothStream::Windows($s) => $body,
453 }
454 };
455}
456
457impl tokio::io::AsyncWrite for BluetoothStream {
458 fn poll_write(
459 self: std::pin::Pin<&mut Self>,
460 cx: &mut std::task::Context<'_>,
461 buf: &[u8],
462 ) -> std::task::Poll<std::io::Result<usize>> {
463 pin_match!(self, s => {
464 tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf)
466 })
467 }
468
469 fn poll_flush(
470 self: std::pin::Pin<&mut Self>,
471 cx: &mut std::task::Context<'_>,
472 ) -> std::task::Poll<std::io::Result<()>> {
473 pin_match!(self, s => {
474 tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx)
475 })
476 }
477
478 fn poll_shutdown(
479 self: std::pin::Pin<&mut Self>,
480 cx: &mut std::task::Context<'_>,
481 ) -> std::task::Poll<std::io::Result<()>> {
482 pin_match!(self, s => {
483 tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx)
484 })
485 }
486}
487
488impl tokio::io::AsyncRead for BluetoothStream {
489 fn poll_read(
490 self: std::pin::Pin<&mut Self>,
491 cx: &mut std::task::Context<'_>,
492 buf: &mut tokio::io::ReadBuf<'_>,
493 ) -> std::task::Poll<std::io::Result<()>> {
494 pin_match!(self, s => {
495 tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
496 })
497 }
498}
499
500impl BluetoothStream {
501 pub fn supports_async_read(&mut self) -> Option<&mut dyn tokio::io::AsyncRead> {
503 match self {
504 #[cfg(target_os = "linux")]
505 BluetoothStream::Bluez(pin) => Some(pin),
506 #[cfg(target_os = "android")]
507 BluetoothStream::Android(_pin) => None,
508 #[cfg(target_os = "windows")]
509 BluetoothStream::Windows(_pin) => None,
510 }
511 }
512
513 pub fn supports_async_write(&mut self) -> Option<&mut dyn tokio::io::AsyncWrite> {
515 match self {
516 #[cfg(target_os = "linux")]
517 BluetoothStream::Bluez(pin) => Some(pin),
518 #[cfg(target_os = "android")]
519 BluetoothStream::Android(_pin) => None,
520 #[cfg(target_os = "windows")]
521 BluetoothStream::Windows(_pin) => None,
522 }
523 }
524
525 pub fn supports_sync_read(&mut self) -> Option<&mut dyn std::io::Read> {
527 match self {
528 #[cfg(target_os = "linux")]
529 BluetoothStream::Bluez(_pin) => None,
530 #[cfg(target_os = "android")]
531 BluetoothStream::Android(pin) => Some(pin),
532 #[cfg(target_os = "windows")]
533 BluetoothStream::Windows(pin) => Some(pin),
534 }
535 }
536
537 pub fn supports_sync_write(&mut self) -> Option<&mut dyn std::io::Write> {
539 match self {
540 #[cfg(target_os = "linux")]
541 BluetoothStream::Bluez(_pin) => None,
542 #[cfg(target_os = "android")]
543 BluetoothStream::Android(pin) => Some(pin),
544 #[cfg(target_os = "windows")]
545 BluetoothStream::Windows(pin) => Some(pin),
546 }
547 }
548}
549
550#[async_trait::async_trait]
552#[enum_dispatch::enum_dispatch]
553pub trait BluetoothRfcommConnectableAsyncTrait {
554 async fn accept(self) -> Result<(BluetoothStream, [u8; 6], u8), String>;
556}
557
558#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableAsyncTrait)]
560pub enum BluetoothRfcommConnectableAsync {
561 #[cfg(target_os = "android")]
563 Android(android::BluetoothRfcommConnectable),
564 #[cfg(target_os = "linux")]
566 Bluez(bluer::rfcomm::ConnectRequest),
567 #[cfg(target_os = "windows")]
569 Windows(windows::BluetoothRfcommConnectable),
570}
571
572#[enum_dispatch::enum_dispatch]
574pub trait BluetoothRfcommConnectableSyncTrait {
575 fn accept(self, timeout: std::time::Duration)
577 -> Result<(BluetoothStream, [u8; 6], u8), String>;
578}
579
580#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
582pub enum BluetoothRfcommConnectableSync {
583 #[cfg(target_os = "android")]
585 Android(android::BluetoothRfcommConnectable),
586}
587
588#[enum_dispatch::enum_dispatch]
590pub trait BluetoothL2capConnectableAsyncTrait {
591 async fn accept(self) -> Result<BluetoothStream, String>;
593}
594
595#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
597pub enum BluetoothL2capConnectableAsync {
598 #[cfg(target_os = "android")]
600 Android(android::BluetoothRfcommConnectable),
601 #[cfg(target_os = "linux")]
603 Bluez(bluer::rfcomm::ConnectRequest),
604}
605
606#[enum_dispatch::enum_dispatch]
608pub trait BluetoothL2capConnectableSyncTrait {
609 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
611}
612
613#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
615pub enum BluetoothL2capConnectableSync {
616 #[cfg(target_os = "android")]
618 Android(android::BluetoothRfcommConnectable),
619}
620
621#[enum_dispatch::enum_dispatch]
623pub trait BluetoothRfcommProfileAsyncTrait {
624 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
626}
627
628#[enum_dispatch::enum_dispatch]
630pub trait BluetoothRfcommProfileSyncTrait {
631 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
633}
634
635#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
637pub enum BluetoothRfcommProfileAsync {
638 #[cfg(target_os = "linux")]
640 Bluez(bluer::rfcomm::ProfileHandle),
641 #[cfg(target_os = "windows")]
643 Windows(windows::BluetoothRfcommProfile),
644 Dummy(Dummy),
646}
647
648#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
650pub enum BluetoothRfcommProfileSync {
651 #[cfg(target_os = "android")]
653 Android(android::BluetoothRfcommProfile),
654 Dummy(Dummy),
656}
657
658#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
660pub enum BluetoothL2capProfileAsync {
661 #[cfg(target_os = "linux")]
663 Bluez(bluer::rfcomm::ProfileHandle),
664 Dummy(Dummy),
666}
667
668#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
670pub enum BluetoothL2capProfileSync {
671 #[cfg(target_os = "android")]
673 Android(android::BluetoothRfcommProfile),
674 Dummy(Dummy),
676}
677
678pub struct Dummy {}
680
681impl BluetoothRfcommProfileSyncTrait for Dummy {
682 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
683 unimplemented!()
684 }
685}
686
687impl BluetoothRfcommProfileAsyncTrait for Dummy {
688 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
689 unimplemented!()
690 }
691}
692
693pub trait AsyncReadWrite: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send {}
695impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send> AsyncReadWrite for T {}
696
697pub trait SyncReadWrite: std::io::Read + std::io::Write + Send {}
699impl<T: std::io::Read + std::io::Write + Unpin + Send> SyncReadWrite for T {}
700
701
702#[async_trait::async_trait]
704#[enum_dispatch::enum_dispatch]
705pub trait BluetoothSocketTrait {
706 fn is_connected(&self) -> Result<bool, std::io::Error>;
708 async fn async_connect(&mut self) -> Result<(), std::io::Error>;
710 fn sync_connect(&mut self) -> Result<(), std::io::Error>;
712 fn supports_async(&mut self) -> Option<&mut dyn AsyncReadWrite> {
714 None
715 }
716 fn supports_sync(&mut self) -> Option<&mut dyn SyncReadWrite> {
718 None
719 }
720}
721
722#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
724pub enum BluetoothSocket {
725 #[cfg(target_os = "android")]
727 Android(android::BluetoothSocket),
728 #[cfg(target_os = "linux")]
730 Bluez(linux::BluetoothRfcommSocket),
731 #[cfg(target_os = "windows")]
733 Windows(windows::BluetoothRfcommSocket),
734}