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(
369 self: std::pin::Pin<&mut Self>,
370 ) -> Option<&mut dyn tokio::io::AsyncRead> {
371 match self.get_mut() {
372 #[cfg(target_os = "linux")]
373 BluetoothStream::Bluez(pin) => Some(pin),
374 #[cfg(target_os = "android")]
375 BluetoothStream::Android(_pin) => None,
376 #[cfg(target_os = "windows")]
377 BluetoothStream::Windows(_pin) => None,
378 }
379 }
380
381 pub fn supports_async_write(
383 self: std::pin::Pin<&mut Self>,
384 ) -> Option<&mut dyn tokio::io::AsyncWrite> {
385 match self.get_mut() {
386 #[cfg(target_os = "linux")]
387 BluetoothStream::Bluez(pin) => Some(pin),
388 #[cfg(target_os = "android")]
389 BluetoothStream::Android(_pin) => None,
390 #[cfg(target_os = "windows")]
391 BluetoothStream::Windows(_pin) => None,
392 }
393 }
394
395 pub fn supports_sync_read(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Read> {
397 match self.get_mut() {
398 #[cfg(target_os = "linux")]
399 BluetoothStream::Bluez(_pin) => None,
400 #[cfg(target_os = "android")]
401 BluetoothStream::Android(pin) => Some(pin),
402 #[cfg(target_os = "windows")]
403 BluetoothStream::Windows(pin) => Some(pin),
404 }
405 }
406
407 pub fn supports_sync_write(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Write> {
409 match self.get_mut() {
410 #[cfg(target_os = "linux")]
411 BluetoothStream::Bluez(_pin) => None,
412 #[cfg(target_os = "android")]
413 BluetoothStream::Android(pin) => Some(pin),
414 #[cfg(target_os = "windows")]
415 BluetoothStream::Windows(pin) => Some(pin),
416 }
417 }
418}
419
420#[enum_dispatch::enum_dispatch]
422pub trait BluetoothRfcommConnectableAsyncTrait {
423 async fn accept(self) -> Result<BluetoothStream, String>;
425}
426
427#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
429pub enum BluetoothRfcommConnectableAsync {
430 #[cfg(target_os = "android")]
432 Android(android::BluetoothRfcommConnectable),
433 #[cfg(target_os = "linux")]
435 Bluez(bluer::rfcomm::ConnectRequest),
436 #[cfg(target_os = "windows")]
438 Windows(windows::BluetoothRfcommConnectable),
439}
440
441#[enum_dispatch::enum_dispatch]
443pub trait BluetoothRfcommConnectableSyncTrait {
444 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
446}
447
448#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
450pub enum BluetoothRfcommConnectableSync {
451 #[cfg(target_os = "android")]
453 Android(android::BluetoothRfcommConnectable),
454}
455
456#[enum_dispatch::enum_dispatch]
458pub trait BluetoothL2capConnectableAsyncTrait {
459 async fn accept(self) -> Result<BluetoothStream, String>;
461}
462
463#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
465pub enum BluetoothL2capConnectableAsync {
466 #[cfg(target_os = "android")]
468 Android(android::BluetoothRfcommConnectable),
469 #[cfg(target_os = "linux")]
471 Bluez(bluer::rfcomm::ConnectRequest),
472}
473
474#[enum_dispatch::enum_dispatch]
476pub trait BluetoothL2capConnectableSyncTrait {
477 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
479}
480
481#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
483pub enum BluetoothL2capConnectableSync {
484 #[cfg(target_os = "android")]
486 Android(android::BluetoothRfcommConnectable),
487}
488
489#[enum_dispatch::enum_dispatch]
491pub trait BluetoothRfcommProfileAsyncTrait {
492 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
494}
495
496#[enum_dispatch::enum_dispatch]
498pub trait BluetoothRfcommProfileSyncTrait {
499 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
501}
502
503#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
505pub enum BluetoothRfcommProfileAsync {
506 #[cfg(target_os = "linux")]
508 Bluez(bluer::rfcomm::ProfileHandle),
509 #[cfg(target_os = "windows")]
511 Windows(windows::BluetoothRfcommProfile),
512 Dummy(Dummy),
514}
515
516#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
518pub enum BluetoothRfcommProfileSync {
519 #[cfg(target_os = "android")]
521 Android(android::BluetoothRfcommProfile),
522 Dummy(Dummy),
524}
525
526#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
528pub enum BluetoothL2capProfileAsync {
529 #[cfg(target_os = "linux")]
531 Bluez(bluer::rfcomm::ProfileHandle),
532 Dummy(Dummy),
534}
535
536#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
538pub enum BluetoothL2capProfileSync {
539 #[cfg(target_os = "android")]
541 Android(android::BluetoothRfcommProfile),
542 Dummy(Dummy),
544}
545
546pub struct Dummy {}
548
549impl BluetoothRfcommProfileSyncTrait for Dummy {
550 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
551 unimplemented!()
552 }
553}
554
555impl BluetoothRfcommProfileAsyncTrait for Dummy {
556 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
557 unimplemented!()
558 }
559}
560
561#[enum_dispatch::enum_dispatch]
563pub trait BluetoothSocketTrait {
564 fn is_connected(&self) -> Result<bool, std::io::Error>;
566 fn connect(&mut self) -> Result<(), std::io::Error>;
568}
569
570impl<'a> std::io::Read for BluetoothSocket<'a> {
571 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
572 match self {
573 #[cfg(target_os = "android")]
574 BluetoothSocket::Android(a) => a.read(buf),
575 #[cfg(target_os = "linux")]
576 BluetoothSocket::Bluez(b) => b.read(buf),
577 #[cfg(target_os = "windows")]
578 BluetoothSocket::Windows(w) => w.read(buf),
579 }
580 }
581}
582
583impl<'a> std::io::Write for BluetoothSocket<'a> {
584 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
585 match self {
586 #[cfg(target_os = "android")]
587 BluetoothSocket::Android(a) => a.write(buf),
588 #[cfg(target_os = "linux")]
589 BluetoothSocket::Bluez(b) => b.write(buf),
590 #[cfg(target_os = "windows")]
591 BluetoothSocket::Windows(w) => w.write(buf),
592 }
593 }
594
595 fn flush(&mut self) -> std::io::Result<()> {
596 match self {
597 #[cfg(target_os = "android")]
598 BluetoothSocket::Android(a) => a.flush(),
599 #[cfg(target_os = "linux")]
600 BluetoothSocket::Bluez(b) => b.flush(),
601 #[cfg(target_os = "windows")]
602 BluetoothSocket::Windows(w) => w.flush(),
603 }
604 }
605}
606
607#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
609pub enum BluetoothSocket<'a> {
610 #[cfg(target_os = "android")]
612 Android(&'a mut android::BluetoothSocket),
613 #[cfg(target_os = "linux")]
615 Bluez(&'a mut linux::BluetoothRfcommSocket),
616 #[cfg(target_os = "windows")]
618 Windows(&'a mut windows::BluetoothRfcommSocket),
619}