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    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/// The trait that all bluetooth devices must implement
282#[enum_dispatch::enum_dispatch]
283pub trait BluetoothDeviceTrait {
284    /// Get all known uuids for this device
285    fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
286
287    /// Retrieve the device name
288    fn get_name(&self) -> Result<String, std::io::Error>;
289
290    /// Retrieve the device address
291    fn get_address(&mut self) -> Result<String, std::io::Error>;
292
293    /// Retrieve the device pairing status
294    fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
295
296    /// Attempt to get an rfcomm socket for the given uuid and security setting
297    fn get_rfcomm_socket(
298        &mut self,
299        channel: u8,
300        is_secure: bool,
301    ) -> Result<BluetoothSocket, String>;
302
303    /// Attempt to get an l2cap socket for the given uuid and security setting
304    fn get_l2cap_socket(&mut self, psm: u16, is_secure: bool) -> Result<BluetoothSocket, String>;
305
306    /// Run the service discovery protocol
307    fn run_sdp(&mut self, uuid: BluetoothUuid) -> Result<sdp::ServiceRecord, String> {
308        if let Ok(a) = self.get_address() {
309            return sdp::run_sdp(&a, uuid.get_16_bit_id()).map_err(|e| e.to_string());
310        }
311        Err("Sdp failed".to_string())
312    }
313}
314
315/// A bluetooth device
316#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
317pub enum BluetoothDevice {
318    /// Bluetooth device on android
319    #[cfg(target_os = "android")]
320    Android(android::BluetoothDevice),
321    /// Bluetooth device on linux using the bluez library
322    #[cfg(target_os = "linux")]
323    Bluez(linux::LinuxBluetoothDevice),
324    /// Bluetooth device on Windows
325    #[cfg(target_os = "windows")]
326    Windows(windows::BluetoothDevice),
327}
328
329/// Represents a bluetooth adapter that communicates to bluetooth devices
330#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
331pub enum BluetoothAdapter {
332    /// The bluetooth adapter for android systems
333    #[cfg(target_os = "android")]
334    Android(android::Bluetooth),
335    /// On linux, bluetooth adapter using the bluez library
336    #[cfg(target_os = "linux")]
337    Bluez(linux::BluetoothHandler),
338    /// On Windows, bluetooth adapter using the windows crate
339    #[cfg(target_os = "windows")]
340    Windows(windows::BluetoothHandler),
341}
342
343/// A builder for `BluetoothAdapter`
344pub struct BluetoothAdapterBuilder {
345    /// The androidapp object
346    #[cfg(target_os = "android")]
347    app: Option<AndroidApp>,
348    /// The sender to send messages to the bluetooth host
349    s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
350}
351
352impl Default for BluetoothAdapterBuilder {
353    fn default() -> Self {
354        Self::new()
355    }
356}
357
358impl BluetoothAdapterBuilder {
359    /// Construct a new self
360    pub fn new() -> Self {
361        Self {
362            #[cfg(target_os = "android")]
363            app: None,
364            s: None,
365        }
366    }
367
368    /// Put the required `AndroidApp` object into the builder
369    #[cfg(target_os = "android")]
370    pub fn with_android_app(&mut self, app: AndroidApp) {
371        self.app = Some(app);
372    }
373
374    /// Add the sender to the builder
375    pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
376        self.s = Some(s);
377    }
378
379    /// Do the build
380    pub fn build(self) -> Result<BluetoothAdapter, String> {
381        #[cfg(target_os = "android")]
382        {
383            return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
384                self.app.unwrap(),
385            )));
386        }
387        Err("No synchronous builders available".to_string())
388    }
389
390    /// Do the build
391    pub async fn async_build(self) -> Result<BluetoothAdapter, String> {
392        #[cfg(target_os = "android")]
393        {
394            return self.build();
395        }
396        #[cfg(target_os = "linux")]
397        {
398            return Ok(BluetoothAdapter::Bluez(
399                linux::BluetoothHandler::new(self.s.unwrap()).await?,
400            ));
401        }
402        #[cfg(target_os = "windows")]
403        {
404            return Ok(BluetoothAdapter::Windows(
405                windows::BluetoothHandler::new(self.s.unwrap()).await?,
406            ));
407        }
408        Err("No async builders available".to_string())
409    }
410}
411
412/// An active stream for bluetooth communications
413pub enum BluetoothStream {
414    /// On linux, a stream using the bluez library
415    #[cfg(target_os = "linux")]
416    Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
417    /// Android code for a bluetooth stream
418    #[cfg(target_os = "android")]
419    Android(android::RfcommStream),
420    /// Windows RFCOMM stream
421    #[cfg(target_os = "windows")]
422    Windows(windows::WindowsRfcommStream),
423}
424
425macro_rules! pin_match {
426    ($this:expr, $s:ident => $body:expr) => {
427        match $this.get_mut() {
428            #[cfg(target_os = "linux")]
429            BluetoothStream::Bluez($s) => $body,
430
431            #[cfg(target_os = "android")]
432            BluetoothStream::Android($s) => $body,
433
434            #[cfg(target_os = "windows")]
435            BluetoothStream::Windows($s) => $body,
436        }
437    };
438}
439
440impl tokio::io::AsyncWrite for BluetoothStream {
441    fn poll_write(
442        self: std::pin::Pin<&mut Self>,
443        cx: &mut std::task::Context<'_>,
444        buf: &[u8],
445    ) -> std::task::Poll<std::io::Result<usize>> {
446        pin_match!(self, s => {
447            // SAFETY: we delegate to inner stream directly
448            tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf)
449        })
450    }
451
452    fn poll_flush(
453        self: std::pin::Pin<&mut Self>,
454        cx: &mut std::task::Context<'_>,
455    ) -> std::task::Poll<std::io::Result<()>> {
456        pin_match!(self, s => {
457            tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx)
458        })
459    }
460
461    fn poll_shutdown(
462        self: std::pin::Pin<&mut Self>,
463        cx: &mut std::task::Context<'_>,
464    ) -> std::task::Poll<std::io::Result<()>> {
465        pin_match!(self, s => {
466            tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx)
467        })
468    }
469}
470
471impl tokio::io::AsyncRead for BluetoothStream {
472    fn poll_read(
473        self: std::pin::Pin<&mut Self>,
474        cx: &mut std::task::Context<'_>,
475        buf: &mut tokio::io::ReadBuf<'_>,
476    ) -> std::task::Poll<std::io::Result<()>> {
477        pin_match!(self, s => {
478            tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
479        })
480    }
481}
482
483impl BluetoothStream {
484    /// Used to check to see if the object supports async read, and then use the functionality
485    pub fn supports_async_read(&mut self) -> Option<&mut dyn tokio::io::AsyncRead> {
486        match self {
487            #[cfg(target_os = "linux")]
488            BluetoothStream::Bluez(pin) => Some(pin),
489            #[cfg(target_os = "android")]
490            BluetoothStream::Android(_pin) => None,
491            #[cfg(target_os = "windows")]
492            BluetoothStream::Windows(_pin) => None,
493        }
494    }
495
496    /// Used to check to see if the object supports async write, and then use the functionality
497    pub fn supports_async_write(&mut self) -> Option<&mut dyn tokio::io::AsyncWrite> {
498        match self {
499            #[cfg(target_os = "linux")]
500            BluetoothStream::Bluez(pin) => Some(pin),
501            #[cfg(target_os = "android")]
502            BluetoothStream::Android(_pin) => None,
503            #[cfg(target_os = "windows")]
504            BluetoothStream::Windows(_pin) => None,
505        }
506    }
507
508    /// Used to try to use synchronous read functionality
509    pub fn supports_sync_read(&mut self) -> Option<&mut dyn std::io::Read> {
510        match self {
511            #[cfg(target_os = "linux")]
512            BluetoothStream::Bluez(_pin) => None,
513            #[cfg(target_os = "android")]
514            BluetoothStream::Android(pin) => Some(pin),
515            #[cfg(target_os = "windows")]
516            BluetoothStream::Windows(pin) => Some(pin),
517        }
518    }
519
520    /// Used to try to use synchronous write functionality
521    pub fn supports_sync_write(&mut self) -> Option<&mut dyn std::io::Write> {
522        match self {
523            #[cfg(target_os = "linux")]
524            BluetoothStream::Bluez(_pin) => None,
525            #[cfg(target_os = "android")]
526            BluetoothStream::Android(pin) => Some(pin),
527            #[cfg(target_os = "windows")]
528            BluetoothStream::Windows(pin) => Some(pin),
529        }
530    }
531}
532
533/// The trait for bluetooth rfcomm objects that can be connected or accepted
534#[async_trait::async_trait]
535#[enum_dispatch::enum_dispatch]
536pub trait BluetoothRfcommConnectableAsyncTrait {
537    /// Accept a connection from a bluetooth peer, returns the stream, bluetooth address, and port
538    async fn accept(self) -> Result<(BluetoothStream, [u8; 6], u8), String>;
539}
540
541/// A bluetooth profile for rfcomm channels
542#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableAsyncTrait)]
543pub enum BluetoothRfcommConnectableAsync {
544    /// The android object for the profile
545    #[cfg(target_os = "android")]
546    Android(android::BluetoothRfcommConnectable),
547    /// The bluez library in linux is responsible for the profile
548    #[cfg(target_os = "linux")]
549    Bluez(bluer::rfcomm::ConnectRequest),
550    /// Windows RFCOMM connectable
551    #[cfg(target_os = "windows")]
552    Windows(windows::BluetoothRfcommConnectable),
553}
554
555/// The trait for bluetooth rfcomm objects that can be connected or accepted
556#[enum_dispatch::enum_dispatch]
557pub trait BluetoothRfcommConnectableSyncTrait {
558    /// Accept a connection from a bluetooth peer
559    fn accept(self, timeout: std::time::Duration)
560    -> Result<(BluetoothStream, [u8; 6], u8), String>;
561}
562
563/// A bluetooth profile for rfcomm channels
564#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
565pub enum BluetoothRfcommConnectableSync {
566    /// The android object for the profile
567    #[cfg(target_os = "android")]
568    Android(android::BluetoothRfcommConnectable),
569}
570
571/// The trait for bluetooth rfcomm objects that can be connected or accepted
572#[enum_dispatch::enum_dispatch]
573pub trait BluetoothL2capConnectableAsyncTrait {
574    /// Accept a connection from a bluetooth peer
575    async fn accept(self) -> Result<BluetoothStream, String>;
576}
577
578/// A bluetooth profile for rfcomm channels
579#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
580pub enum BluetoothL2capConnectableAsync {
581    /// The android object for the profile
582    #[cfg(target_os = "android")]
583    Android(android::BluetoothRfcommConnectable),
584    /// The bluez library in linux is responsible for the profile
585    #[cfg(target_os = "linux")]
586    Bluez(bluer::rfcomm::ConnectRequest),
587}
588
589/// The trait for bluetooth rfcomm objects that can be connected or accepted
590#[enum_dispatch::enum_dispatch]
591pub trait BluetoothL2capConnectableSyncTrait {
592    /// Accept a connection from a bluetooth peer
593    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
594}
595
596/// A bluetooth profile for rfcomm channels
597#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
598pub enum BluetoothL2capConnectableSync {
599    /// The android object for the profile
600    #[cfg(target_os = "android")]
601    Android(android::BluetoothRfcommConnectable),
602}
603
604/// Allows building an object to connect to bluetooth devices
605#[enum_dispatch::enum_dispatch]
606pub trait BluetoothRfcommProfileAsyncTrait {
607    /// Get an object in order to accept a connection from or connect to a bluetooth peer
608    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
609}
610
611/// Allows building an object to connect to bluetooth devices
612#[enum_dispatch::enum_dispatch]
613pub trait BluetoothRfcommProfileSyncTrait {
614    /// Get an object in order to accept a connection from or connect to a bluetooth peer
615    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
616}
617
618/// A bluetooth profile for rfcomm channels
619#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
620pub enum BluetoothRfcommProfileAsync {
621    /// The bluez library in linux is responsible for the profile
622    #[cfg(target_os = "linux")]
623    Bluez(bluer::rfcomm::ProfileHandle),
624    /// Windows RFCOMM profile
625    #[cfg(target_os = "windows")]
626    Windows(windows::BluetoothRfcommProfile),
627    /// A dummy handler
628    Dummy(Dummy),
629}
630
631/// A bluetooth profile for rfcomm channels
632#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
633pub enum BluetoothRfcommProfileSync {
634    /// Android rfcomm profile
635    #[cfg(target_os = "android")]
636    Android(android::BluetoothRfcommProfile),
637    /// A dummy handler
638    Dummy(Dummy),
639}
640
641/// A bluetooth profile for rfcomm channels
642#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
643pub enum BluetoothL2capProfileAsync {
644    /// The bluez library in linux is responsible for the profile
645    #[cfg(target_os = "linux")]
646    Bluez(bluer::rfcomm::ProfileHandle),
647    /// A dummy handler
648    Dummy(Dummy),
649}
650
651/// A bluetooth profile for rfcomm channels
652#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
653pub enum BluetoothL2capProfileSync {
654    /// Android rfcomm profile
655    #[cfg(target_os = "android")]
656    Android(android::BluetoothRfcommProfile),
657    /// A dummy handler
658    Dummy(Dummy),
659}
660
661/// A dummy struct for ensuring enums are not empty
662pub struct Dummy {}
663
664impl BluetoothRfcommProfileSyncTrait for Dummy {
665    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
666        unimplemented!()
667    }
668}
669
670impl BluetoothRfcommProfileAsyncTrait for Dummy {
671    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
672        unimplemented!()
673    }
674}
675
676/// The common functions for all bluetooth rfcomm sockets
677#[enum_dispatch::enum_dispatch]
678pub trait BluetoothSocketTrait {
679    /// Is the socket connected
680    fn is_connected(&self) -> Result<bool, std::io::Error>;
681    /// connect the socket
682    fn connect(&mut self) -> Result<(), std::io::Error>;
683}
684
685impl std::io::Read for BluetoothSocket {
686    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
687        match self {
688            #[cfg(target_os = "android")]
689            BluetoothSocket::Android(a) => a.read(buf),
690            #[cfg(target_os = "linux")]
691            BluetoothSocket::Bluez(b) => b.read(buf),
692            #[cfg(target_os = "windows")]
693            BluetoothSocket::Windows(w) => w.read(buf),
694        }
695    }
696}
697
698impl std::io::Write for BluetoothSocket {
699    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
700        match self {
701            #[cfg(target_os = "android")]
702            BluetoothSocket::Android(a) => a.write(buf),
703            #[cfg(target_os = "linux")]
704            BluetoothSocket::Bluez(b) => b.write(buf),
705            #[cfg(target_os = "windows")]
706            BluetoothSocket::Windows(w) => w.write(buf),
707        }
708    }
709
710    fn flush(&mut self) -> std::io::Result<()> {
711        match self {
712            #[cfg(target_os = "android")]
713            BluetoothSocket::Android(a) => a.flush(),
714            #[cfg(target_os = "linux")]
715            BluetoothSocket::Bluez(b) => b.flush(),
716            #[cfg(target_os = "windows")]
717            BluetoothSocket::Windows(w) => w.flush(),
718        }
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}