Skip to main content

bluetooth_rust/
lib.rs

1#![deny(missing_docs)]
2#![deny(clippy::missing_docs_in_private_items)]
3#![warn(unused_extern_crates)]
4
5//! This library is intended to eventually be a cross-platform bluetooth handling platform
6//! Android portions adapted from <https://github.com/wuwbobo2021/android-bluetooth-serial-rs>
7
8#[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/// Commands issued to the library
33#[derive(Debug, serde::Deserialize, serde::Serialize)]
34pub enum BluetoothCommand {
35    /// Detect all bluetooth adapters present on the system
36    DetectAdapters,
37    /// Find out how many bluetooth adapters are detected
38    QueryNumAdapters,
39}
40
41/// Messages that can be sent specifically to the app user hosting the bluetooth controls
42pub enum MessageToBluetoothHost {
43    /// The passkey used for pairing devices
44    DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
45    /// The passkey to confirm for pairing
46    ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
47    /// Cancal the passkey display
48    CancelDisplayPasskey,
49}
50
51#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
52/// Messages that are send directly from the bluetooth host
53pub enum MessageFromBluetoothHost {
54    /// A response about the active pairing passkey
55    PasskeyMessage(ResponseToPasskey),
56}
57
58#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
59/// The user response to a bluetooth passkey
60pub enum ResponseToPasskey {
61    /// The passkey is accepted
62    Yes,
63    /// The passkey is not accepted
64    No,
65    /// The process is canceled by the user
66    Cancel,
67    /// Waiting on the user to decide
68    Waiting,
69}
70
71/// Responses issued by the library
72pub enum BluetoothResponse {
73    /// The number of bluetooth adapters detected
74    Adapters(usize),
75}
76
77/// Settings for an rfcomm profile
78#[derive(Clone, Debug)]
79pub struct BluetoothRfcommProfileSettings {
80    /// The uuid for the profile
81    pub uuid: String,
82    /// User readable name for the profile
83    pub name: Option<String>,
84    /// The service uuid for the profile (can be the same as service)
85    pub service_uuid: Option<String>,
86    /// The channel to use
87    pub channel: Option<u16>,
88    /// PSM number used for UUIDS and SDP (if applicable)
89    pub psm: Option<u16>,
90    /// Is authentication required for a connection
91    pub authenticate: Option<bool>,
92    /// Is authorization required for a connection
93    pub authorize: Option<bool>,
94    /// For client profiles, This will force connection of the channel when a remote device is connected
95    pub auto_connect: Option<bool>,
96    /// manual SDP record
97    pub sdp_record: Option<String>,
98    /// SDP version
99    pub sdp_version: Option<u16>,
100    /// SDP profile features
101    pub sdp_features: Option<u16>,
102}
103
104/// Settings for an rfcomm profile
105#[derive(Clone)]
106pub struct BluetoothL2capProfileSettings {
107    /// The uuid for the profile
108    pub uuid: String,
109    /// User readable name for the profile
110    pub name: Option<String>,
111    /// The service uuid for the profile (can be the same as service)
112    pub service_uuid: Option<String>,
113    /// The channel to use
114    pub channel: Option<u16>,
115    /// PSM number used for UUIDS and SDP (if applicable)
116    pub psm: Option<u16>,
117    /// Is authentication required for a connection
118    pub authenticate: Option<bool>,
119    /// Is authorization required for a connection
120    pub authorize: Option<bool>,
121    /// For client profiles, This will force connection of the channel when a remote device is connected
122    pub auto_connect: Option<bool>,
123    /// manual SDP record
124    pub sdp_record: Option<String>,
125    /// SDP version
126    pub sdp_version: Option<u16>,
127    /// SDP profile features
128    pub sdp_features: Option<u16>,
129}
130
131/// The trait that implements managing when bluetooth discovery is enabled
132#[enum_dispatch::enum_dispatch]
133pub trait BluetoothDiscoveryTrait {}
134
135/// The trait for the object that manages bluetooth discovery
136#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
137pub enum BluetoothDiscovery {
138    /// The android version
139    #[cfg(target_os = "android")]
140    Android(android::BluetoothDiscovery),
141    /// Linux bluez library implementation
142    #[cfg(target_os = "linux")]
143    Bluez(linux::BluetoothDiscovery),
144    /// Windows implementation
145    #[cfg(target_os = "windows")]
146    Windows(windows::BluetoothDiscovery),
147}
148
149/// The address of a bluetooth adapter
150pub enum BluetoothAdapterAddress {
151    /// The address in string form
152    String(String),
153    /// The address in byte form
154    Byte([u8; 6]),
155}
156
157/// Common async functionality for the bluetooth adapter
158#[enum_dispatch::enum_dispatch]
159#[async_trait::async_trait]
160pub trait AsyncBluetoothAdapterTrait {
161    /// Attempt to register a new rfcomm profile
162    async fn register_rfcomm_profile(
163        &self,
164        settings: BluetoothRfcommProfileSettings,
165    ) -> Result<BluetoothRfcommProfileAsync, String>;
166    /// Attempt to register a new l2cap profile
167    async fn register_l2cap_profile(
168        &self,
169        settings: BluetoothL2capProfileSettings,
170    ) -> Result<BluetoothL2capProfileAsync, String>;
171    ///Get a list of paired bluetooth devices
172    async fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
173    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
174    fn start_discovery(&self) -> BluetoothDiscovery;
175    /// Get the mac addresses of all bluetooth adapters for the system
176    async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
177    /// Set the discoverable property
178    async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
179}
180
181/// Common sync functionality for the bluetooth adapter
182#[enum_dispatch::enum_dispatch]
183pub trait SyncBluetoothAdapterTrait {
184    /// Attempt to register a new rfcomm profile
185    fn register_rfcomm_profile(
186        &self,
187        settings: BluetoothRfcommProfileSettings,
188    ) -> Result<BluetoothRfcommProfileSync, String>;
189    /// Attempt to register a new lc2ap profile
190    fn register_l2cap_profile(
191        &self,
192        settings: BluetoothL2capProfileSettings,
193    ) -> Result<BluetoothL2capProfileAsync, String>;
194    ///Get a list of paired bluetooth devices
195    fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
196    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
197    fn start_discovery(&self) -> BluetoothDiscovery;
198    /// Get the mac addresses of all bluetooth adapters for the system
199    fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
200    /// Set the discoverable property
201    fn set_discoverable(&self, d: bool) -> Result<(), ()>;
202}
203
204/// Common functionality for the bluetooth adapter
205#[enum_dispatch::enum_dispatch]
206pub trait BluetoothAdapterTrait {
207    /// Returns Some when the async interface is supported
208    fn supports_async(&self) -> Option<&dyn AsyncBluetoothAdapterTrait>;
209    /// Returns Some when the sync interface is supported
210    fn supports_sync(&self) -> Option<&dyn SyncBluetoothAdapterTrait>;
211}
212
213/// The pairing status of a bluetooth device
214pub enum PairingStatus {
215    /// The device is not paired
216    NotPaired,
217    /// The device is in the pairing process
218    Pairing,
219    /// The device is paired
220    Paired,
221    /// The status is unknown or invalid
222    Unknown,
223}
224
225fn uuid16(uuid: u16) -> Vec<u8> {
226    let mut v = Vec::new();
227    v.push(0x19); // UUID-16 type
228    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 ID
236    pdu.push(0x06);
237
238    // placeholder length
239    let mut params = Vec::new();
240
241    // Transaction ID
242    params.extend_from_slice(&transaction_id.to_be_bytes());
243
244    // Parameter length placeholder (filled later)
245
246    // ServiceSearchPattern: UUID 0x1101 (SPP)
247    let uuid = uuid16(uuid);
248    let mut search = Vec::new();
249    search.push(0x35); // sequence
250    search.push(uuid.len() as u8);
251    search.extend_from_slice(&uuid);
252    params.extend_from_slice(&search);
253
254    // Attribute ID list (0x0000 - 0xFFFF for everything)
255    let mut attrs = Vec::new();
256    attrs.push(0x35);
257    attrs.push(7);
258    attrs.extend_from_slice(&[
259        0x09, 0x00, 0x00, // uint16 0x0000
260        0x09, 0xFF, 0xFF, // uint16 0xFFFF
261    ]);
262    params.extend_from_slice(&attrs);
263
264    // Max attribute bytes
265    params.extend_from_slice(&0xFFFFu16.to_be_bytes());
266
267    // Continuation state
268    params.push(0x00);
269
270    // Now patch length
271    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(&params);
277
278    out
279}
280
281#[async_trait::async_trait]
282#[enum_dispatch::enum_dispatch]
283/// Holds the async functions for a bluetooth device
284pub trait BluetoothDeviceAsyncTrait {
285    /// Get all known uuids for this device
286    async fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
287    /// Retrieve the device name
288    async fn get_name(&self) -> Result<String, std::io::Error>;
289    /// Retrieve the device pairing status
290    async fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
291}
292
293#[enum_dispatch::enum_dispatch]
294/// Holds the sync functions for a bluetooth device
295pub trait BluetoothDeviceSyncTrait {
296    /// Get all known uuids for this device
297    fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
298    /// Retrieve the device name
299    fn get_name(&self) -> Result<String, std::io::Error>;
300    /// Retrieve the device pairing status
301    fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
302}
303
304/// The trait that all bluetooth devices must implement
305#[enum_dispatch::enum_dispatch]
306pub trait BluetoothDeviceTrait {
307    /// Does the device support async?
308    fn supports_async(&mut self) -> Option<&mut dyn BluetoothDeviceAsyncTrait>;
309    /// Does the device support sync?
310    fn supports_sync(&mut self) -> Option<&mut dyn BluetoothDeviceSyncTrait>;
311    /// Retrieve the device address
312    fn get_address(&mut self) -> Result<String, std::io::Error>;
313    /// Attempt to get an rfcomm socket for the given uuid and security setting
314    fn get_rfcomm_socket(
315        &mut self,
316        channel: u8,
317        is_secure: bool,
318    ) -> Result<BluetoothSocket, String>;
319
320    /// Attempt to get an l2cap socket for the given uuid and security setting
321    fn get_l2cap_socket(&mut self, psm: u16, is_secure: bool) -> Result<BluetoothSocket, String>;
322
323    /// Run the service discovery protocol
324    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/// A bluetooth device
333#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
334pub enum BluetoothDevice {
335    /// Bluetooth device on android
336    #[cfg(target_os = "android")]
337    Android(android::BluetoothDevice),
338    /// Bluetooth device on linux using the bluez library
339    #[cfg(target_os = "linux")]
340    Bluez(linux::LinuxBluetoothDevice),
341    /// Bluetooth device on Windows
342    #[cfg(target_os = "windows")]
343    Windows(windows::BluetoothDevice),
344}
345
346/// Represents a bluetooth adapter that communicates to bluetooth devices
347#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
348pub enum BluetoothAdapter {
349    /// The bluetooth adapter for android systems
350    #[cfg(target_os = "android")]
351    Android(android::Bluetooth),
352    /// On linux, bluetooth adapter using the bluez library
353    #[cfg(target_os = "linux")]
354    Bluez(linux::BluetoothHandler),
355    /// On Windows, bluetooth adapter using the windows crate
356    #[cfg(target_os = "windows")]
357    Windows(windows::BluetoothHandler),
358}
359
360/// A builder for `BluetoothAdapter`
361pub struct BluetoothAdapterBuilder {
362    /// The androidapp object
363    #[cfg(target_os = "android")]
364    app: Option<AndroidApp>,
365    /// The sender to send messages to the bluetooth host
366    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    /// Construct a new self
377    pub fn new() -> Self {
378        Self {
379            #[cfg(target_os = "android")]
380            app: None,
381            s: None,
382        }
383    }
384
385    /// Put the required `AndroidApp` object into the builder
386    #[cfg(target_os = "android")]
387    pub fn with_android_app(&mut self, app: AndroidApp) {
388        self.app = Some(app);
389    }
390
391    /// Add the sender to the builder
392    pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
393        self.s = Some(s);
394    }
395
396    /// Do the build
397    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    /// Do the build
408    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
429/// An active stream for bluetooth communications
430pub enum BluetoothStream {
431    /// On linux, a stream using the bluez library
432    #[cfg(target_os = "linux")]
433    Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
434    /// Android code for a bluetooth stream
435    #[cfg(target_os = "android")]
436    Android(android::RfcommStream),
437    /// Windows RFCOMM stream
438    #[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            // SAFETY: we delegate to inner stream directly
465            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    /// Used to check to see if the object supports async read, and then use the functionality
502    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    /// Used to check to see if the object supports async write, and then use the functionality
514    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    /// Used to try to use synchronous read functionality
526    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    /// Used to try to use synchronous write functionality
538    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/// The trait for bluetooth rfcomm objects that can be connected or accepted
551#[async_trait::async_trait]
552#[enum_dispatch::enum_dispatch]
553pub trait BluetoothRfcommConnectableAsyncTrait {
554    /// Accept a connection from a bluetooth peer, returns the stream, bluetooth address, and port
555    async fn accept(self) -> Result<(BluetoothStream, [u8; 6], u8), String>;
556}
557
558/// A bluetooth profile for rfcomm channels
559#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableAsyncTrait)]
560pub enum BluetoothRfcommConnectableAsync {
561    /// The android object for the profile
562    #[cfg(target_os = "android")]
563    Android(android::BluetoothRfcommConnectable),
564    /// The bluez library in linux is responsible for the profile
565    #[cfg(target_os = "linux")]
566    Bluez(bluer::rfcomm::ConnectRequest),
567    /// Windows RFCOMM connectable
568    #[cfg(target_os = "windows")]
569    Windows(windows::BluetoothRfcommConnectable),
570}
571
572/// The trait for bluetooth rfcomm objects that can be connected or accepted
573#[enum_dispatch::enum_dispatch]
574pub trait BluetoothRfcommConnectableSyncTrait {
575    /// Accept a connection from a bluetooth peer
576    fn accept(self, timeout: std::time::Duration)
577    -> Result<(BluetoothStream, [u8; 6], u8), String>;
578}
579
580/// A bluetooth profile for rfcomm channels
581#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
582pub enum BluetoothRfcommConnectableSync {
583    /// The android object for the profile
584    #[cfg(target_os = "android")]
585    Android(android::BluetoothRfcommConnectable),
586}
587
588/// The trait for bluetooth rfcomm objects that can be connected or accepted
589#[enum_dispatch::enum_dispatch]
590pub trait BluetoothL2capConnectableAsyncTrait {
591    /// Accept a connection from a bluetooth peer
592    async fn accept(self) -> Result<BluetoothStream, String>;
593}
594
595/// A bluetooth profile for rfcomm channels
596#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
597pub enum BluetoothL2capConnectableAsync {
598    /// The android object for the profile
599    #[cfg(target_os = "android")]
600    Android(android::BluetoothRfcommConnectable),
601    /// The bluez library in linux is responsible for the profile
602    #[cfg(target_os = "linux")]
603    Bluez(bluer::rfcomm::ConnectRequest),
604}
605
606/// The trait for bluetooth rfcomm objects that can be connected or accepted
607#[enum_dispatch::enum_dispatch]
608pub trait BluetoothL2capConnectableSyncTrait {
609    /// Accept a connection from a bluetooth peer
610    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
611}
612
613/// A bluetooth profile for rfcomm channels
614#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
615pub enum BluetoothL2capConnectableSync {
616    /// The android object for the profile
617    #[cfg(target_os = "android")]
618    Android(android::BluetoothRfcommConnectable),
619}
620
621/// Allows building an object to connect to bluetooth devices
622#[enum_dispatch::enum_dispatch]
623pub trait BluetoothRfcommProfileAsyncTrait {
624    /// Get an object in order to accept a connection from or connect to a bluetooth peer
625    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
626}
627
628/// Allows building an object to connect to bluetooth devices
629#[enum_dispatch::enum_dispatch]
630pub trait BluetoothRfcommProfileSyncTrait {
631    /// Get an object in order to accept a connection from or connect to a bluetooth peer
632    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
633}
634
635/// A bluetooth profile for rfcomm channels
636#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
637pub enum BluetoothRfcommProfileAsync {
638    /// The bluez library in linux is responsible for the profile
639    #[cfg(target_os = "linux")]
640    Bluez(bluer::rfcomm::ProfileHandle),
641    /// Windows RFCOMM profile
642    #[cfg(target_os = "windows")]
643    Windows(windows::BluetoothRfcommProfile),
644    /// A dummy handler
645    Dummy(Dummy),
646}
647
648/// A bluetooth profile for rfcomm channels
649#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
650pub enum BluetoothRfcommProfileSync {
651    /// Android rfcomm profile
652    #[cfg(target_os = "android")]
653    Android(android::BluetoothRfcommProfile),
654    /// A dummy handler
655    Dummy(Dummy),
656}
657
658/// A bluetooth profile for rfcomm channels
659#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
660pub enum BluetoothL2capProfileAsync {
661    /// The bluez library in linux is responsible for the profile
662    #[cfg(target_os = "linux")]
663    Bluez(bluer::rfcomm::ProfileHandle),
664    /// A dummy handler
665    Dummy(Dummy),
666}
667
668/// A bluetooth profile for rfcomm channels
669#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
670pub enum BluetoothL2capProfileSync {
671    /// Android rfcomm profile
672    #[cfg(target_os = "android")]
673    Android(android::BluetoothRfcommProfile),
674    /// A dummy handler
675    Dummy(Dummy),
676}
677
678/// A dummy struct for ensuring enums are not empty
679pub 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
693/// A trait combining read and write functionality
694pub trait AsyncReadWrite: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send {}
695impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send> AsyncReadWrite for T {}
696
697/// A trait combining read and write functionality
698pub 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/// The common functions for all bluetooth rfcomm sockets
703#[async_trait::async_trait]
704#[enum_dispatch::enum_dispatch]
705pub trait BluetoothSocketTrait {
706    /// Is the socket connected
707    fn is_connected(&self) -> Result<bool, std::io::Error>;
708    /// connect the socket
709    async fn async_connect(&mut self) -> Result<(), std::io::Error>;
710    /// connect the socket
711    fn sync_connect(&mut self) -> Result<(), std::io::Error>;
712    /// Does the socket support async?
713    fn supports_async(&mut self) -> Option<&mut dyn AsyncReadWrite> {
714        None
715    }
716    /// Does the socket support sync?
717    fn supports_sync(&mut self) -> Option<&mut dyn SyncReadWrite> {
718        None
719    }
720}
721
722/// A bluetooth rfcomm socket
723#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
724pub enum BluetoothSocket {
725    /// The android based rfcomm socket
726    #[cfg(target_os = "android")]
727    Android(android::BluetoothSocket),
728    /// Linux using bluez library
729    #[cfg(target_os = "linux")]
730    Bluez(linux::BluetoothRfcommSocket),
731    /// Windows bluetooth socket
732    #[cfg(target_os = "windows")]
733    Windows(windows::BluetoothRfcommSocket),
734}