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(&mut self) -> Option<&mut dyn AsyncBluetoothAdapterTrait>;
207 fn supports_sync(&mut self) -> Option<&mut 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
366impl BluetoothStream {
367 pub fn supports_async_read(&mut self) -> Option<&mut dyn tokio::io::AsyncRead> {
369 match self {
370 #[cfg(target_os = "linux")]
371 BluetoothStream::Bluez(pin) => Some(pin),
372 #[cfg(target_os = "android")]
373 BluetoothStream::Android(_pin) => None,
374 #[cfg(target_os = "windows")]
375 BluetoothStream::Windows(_pin) => None,
376 }
377 }
378
379 pub fn supports_async_write(&mut self) -> Option<&mut dyn tokio::io::AsyncWrite> {
381 match self {
382 #[cfg(target_os = "linux")]
383 BluetoothStream::Bluez(pin) => Some(pin),
384 #[cfg(target_os = "android")]
385 BluetoothStream::Android(_pin) => None,
386 #[cfg(target_os = "windows")]
387 BluetoothStream::Windows(_pin) => None,
388 }
389 }
390
391 pub fn supports_sync_read(&mut self) -> Option<&mut dyn std::io::Read> {
393 match self {
394 #[cfg(target_os = "linux")]
395 BluetoothStream::Bluez(_pin) => None,
396 #[cfg(target_os = "android")]
397 BluetoothStream::Android(pin) => Some(pin),
398 #[cfg(target_os = "windows")]
399 BluetoothStream::Windows(pin) => Some(pin),
400 }
401 }
402
403 pub fn supports_sync_write(&mut self) -> Option<&mut dyn std::io::Write> {
405 match self {
406 #[cfg(target_os = "linux")]
407 BluetoothStream::Bluez(_pin) => None,
408 #[cfg(target_os = "android")]
409 BluetoothStream::Android(pin) => Some(pin),
410 #[cfg(target_os = "windows")]
411 BluetoothStream::Windows(pin) => Some(pin),
412 }
413 }
414}
415
416#[enum_dispatch::enum_dispatch]
418pub trait BluetoothRfcommConnectableAsyncTrait {
419 async fn accept(self) -> Result<BluetoothStream, String>;
421}
422
423#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
425pub enum BluetoothRfcommConnectableAsync {
426 #[cfg(target_os = "android")]
428 Android(android::BluetoothRfcommConnectable),
429 #[cfg(target_os = "linux")]
431 Bluez(bluer::rfcomm::ConnectRequest),
432 #[cfg(target_os = "windows")]
434 Windows(windows::BluetoothRfcommConnectable),
435}
436
437#[enum_dispatch::enum_dispatch]
439pub trait BluetoothRfcommConnectableSyncTrait {
440 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
442}
443
444#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
446pub enum BluetoothRfcommConnectableSync {
447 #[cfg(target_os = "android")]
449 Android(android::BluetoothRfcommConnectable),
450}
451
452#[enum_dispatch::enum_dispatch]
454pub trait BluetoothL2capConnectableAsyncTrait {
455 async fn accept(self) -> Result<BluetoothStream, String>;
457}
458
459#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
461pub enum BluetoothL2capConnectableAsync {
462 #[cfg(target_os = "android")]
464 Android(android::BluetoothRfcommConnectable),
465 #[cfg(target_os = "linux")]
467 Bluez(bluer::rfcomm::ConnectRequest),
468}
469
470#[enum_dispatch::enum_dispatch]
472pub trait BluetoothL2capConnectableSyncTrait {
473 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
475}
476
477#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
479pub enum BluetoothL2capConnectableSync {
480 #[cfg(target_os = "android")]
482 Android(android::BluetoothRfcommConnectable),
483}
484
485#[enum_dispatch::enum_dispatch]
487pub trait BluetoothRfcommProfileAsyncTrait {
488 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
490}
491
492#[enum_dispatch::enum_dispatch]
494pub trait BluetoothRfcommProfileSyncTrait {
495 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
497}
498
499#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
501pub enum BluetoothRfcommProfileAsync {
502 #[cfg(target_os = "linux")]
504 Bluez(bluer::rfcomm::ProfileHandle),
505 #[cfg(target_os = "windows")]
507 Windows(windows::BluetoothRfcommProfile),
508 Dummy(Dummy),
510}
511
512#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
514pub enum BluetoothRfcommProfileSync {
515 #[cfg(target_os = "android")]
517 Android(android::BluetoothRfcommProfile),
518 Dummy(Dummy),
520}
521
522#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
524pub enum BluetoothL2capProfileAsync {
525 #[cfg(target_os = "linux")]
527 Bluez(bluer::rfcomm::ProfileHandle),
528 Dummy(Dummy),
530}
531
532#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
534pub enum BluetoothL2capProfileSync {
535 #[cfg(target_os = "android")]
537 Android(android::BluetoothRfcommProfile),
538 Dummy(Dummy),
540}
541
542pub struct Dummy {}
544
545impl BluetoothRfcommProfileSyncTrait for Dummy {
546 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
547 unimplemented!()
548 }
549}
550
551impl BluetoothRfcommProfileAsyncTrait for Dummy {
552 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
553 unimplemented!()
554 }
555}
556
557#[enum_dispatch::enum_dispatch]
559pub trait BluetoothSocketTrait {
560 fn is_connected(&self) -> Result<bool, std::io::Error>;
562 fn connect(&mut self) -> Result<(), std::io::Error>;
564}
565
566impl<'a> std::io::Read for BluetoothSocket<'a> {
567 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
568 match self {
569 #[cfg(target_os = "android")]
570 BluetoothSocket::Android(a) => a.read(buf),
571 #[cfg(target_os = "linux")]
572 BluetoothSocket::Bluez(b) => b.read(buf),
573 #[cfg(target_os = "windows")]
574 BluetoothSocket::Windows(w) => w.read(buf),
575 }
576 }
577}
578
579impl<'a> std::io::Write for BluetoothSocket<'a> {
580 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
581 match self {
582 #[cfg(target_os = "android")]
583 BluetoothSocket::Android(a) => a.write(buf),
584 #[cfg(target_os = "linux")]
585 BluetoothSocket::Bluez(b) => b.write(buf),
586 #[cfg(target_os = "windows")]
587 BluetoothSocket::Windows(w) => w.write(buf),
588 }
589 }
590
591 fn flush(&mut self) -> std::io::Result<()> {
592 match self {
593 #[cfg(target_os = "android")]
594 BluetoothSocket::Android(a) => a.flush(),
595 #[cfg(target_os = "linux")]
596 BluetoothSocket::Bluez(b) => b.flush(),
597 #[cfg(target_os = "windows")]
598 BluetoothSocket::Windows(w) => w.flush(),
599 }
600 }
601}
602
603#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
605pub enum BluetoothSocket<'a> {
606 #[cfg(target_os = "android")]
608 Android(&'a mut android::BluetoothSocket),
609 #[cfg(target_os = "linux")]
611 Bluez(&'a mut linux::BluetoothRfcommSocket),
612 #[cfg(target_os = "windows")]
614 Windows(&'a mut windows::BluetoothRfcommSocket),
615}