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
538    async fn accept(self) -> Result<BluetoothStream, 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) -> Result<BluetoothStream, String>;
560}
561
562/// A bluetooth profile for rfcomm channels
563#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
564pub enum BluetoothRfcommConnectableSync {
565    /// The android object for the profile
566    #[cfg(target_os = "android")]
567    Android(android::BluetoothRfcommConnectable),
568}
569
570/// The trait for bluetooth rfcomm objects that can be connected or accepted
571#[enum_dispatch::enum_dispatch]
572pub trait BluetoothL2capConnectableAsyncTrait {
573    /// Accept a connection from a bluetooth peer
574    async fn accept(self) -> Result<BluetoothStream, String>;
575}
576
577/// A bluetooth profile for rfcomm channels
578#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
579pub enum BluetoothL2capConnectableAsync {
580    /// The android object for the profile
581    #[cfg(target_os = "android")]
582    Android(android::BluetoothRfcommConnectable),
583    /// The bluez library in linux is responsible for the profile
584    #[cfg(target_os = "linux")]
585    Bluez(bluer::rfcomm::ConnectRequest),
586}
587
588/// The trait for bluetooth rfcomm objects that can be connected or accepted
589#[enum_dispatch::enum_dispatch]
590pub trait BluetoothL2capConnectableSyncTrait {
591    /// Accept a connection from a bluetooth peer
592    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
593}
594
595/// A bluetooth profile for rfcomm channels
596#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
597pub enum BluetoothL2capConnectableSync {
598    /// The android object for the profile
599    #[cfg(target_os = "android")]
600    Android(android::BluetoothRfcommConnectable),
601}
602
603/// Allows building an object to connect to bluetooth devices
604#[enum_dispatch::enum_dispatch]
605pub trait BluetoothRfcommProfileAsyncTrait {
606    /// Get an object in order to accept a connection from or connect to a bluetooth peer
607    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
608}
609
610/// Allows building an object to connect to bluetooth devices
611#[enum_dispatch::enum_dispatch]
612pub trait BluetoothRfcommProfileSyncTrait {
613    /// Get an object in order to accept a connection from or connect to a bluetooth peer
614    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
615}
616
617/// A bluetooth profile for rfcomm channels
618#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
619pub enum BluetoothRfcommProfileAsync {
620    /// The bluez library in linux is responsible for the profile
621    #[cfg(target_os = "linux")]
622    Bluez(bluer::rfcomm::ProfileHandle),
623    /// Windows RFCOMM profile
624    #[cfg(target_os = "windows")]
625    Windows(windows::BluetoothRfcommProfile),
626    /// A dummy handler
627    Dummy(Dummy),
628}
629
630/// A bluetooth profile for rfcomm channels
631#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
632pub enum BluetoothRfcommProfileSync {
633    /// Android rfcomm profile
634    #[cfg(target_os = "android")]
635    Android(android::BluetoothRfcommProfile),
636    /// A dummy handler
637    Dummy(Dummy),
638}
639
640/// A bluetooth profile for rfcomm channels
641#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
642pub enum BluetoothL2capProfileAsync {
643    /// The bluez library in linux is responsible for the profile
644    #[cfg(target_os = "linux")]
645    Bluez(bluer::rfcomm::ProfileHandle),
646    /// A dummy handler
647    Dummy(Dummy),
648}
649
650/// A bluetooth profile for rfcomm channels
651#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
652pub enum BluetoothL2capProfileSync {
653    /// Android rfcomm profile
654    #[cfg(target_os = "android")]
655    Android(android::BluetoothRfcommProfile),
656    /// A dummy handler
657    Dummy(Dummy),
658}
659
660/// A dummy struct for ensuring enums are not empty
661pub struct Dummy {}
662
663impl BluetoothRfcommProfileSyncTrait for Dummy {
664    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
665        unimplemented!()
666    }
667}
668
669impl BluetoothRfcommProfileAsyncTrait for Dummy {
670    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
671        unimplemented!()
672    }
673}
674
675/// The common functions for all bluetooth rfcomm sockets
676#[enum_dispatch::enum_dispatch]
677pub trait BluetoothSocketTrait {
678    /// Is the socket connected
679    fn is_connected(&self) -> Result<bool, std::io::Error>;
680    /// connect the socket
681    fn connect(&mut self) -> Result<(), std::io::Error>;
682}
683
684impl std::io::Read for BluetoothSocket {
685    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
686        match self {
687            #[cfg(target_os = "android")]
688            BluetoothSocket::Android(a) => a.read(buf),
689            #[cfg(target_os = "linux")]
690            BluetoothSocket::Bluez(b) => b.read(buf),
691            #[cfg(target_os = "windows")]
692            BluetoothSocket::Windows(w) => w.read(buf),
693        }
694    }
695}
696
697impl std::io::Write for BluetoothSocket {
698    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
699        match self {
700            #[cfg(target_os = "android")]
701            BluetoothSocket::Android(a) => a.write(buf),
702            #[cfg(target_os = "linux")]
703            BluetoothSocket::Bluez(b) => b.write(buf),
704            #[cfg(target_os = "windows")]
705            BluetoothSocket::Windows(w) => w.write(buf),
706        }
707    }
708
709    fn flush(&mut self) -> std::io::Result<()> {
710        match self {
711            #[cfg(target_os = "android")]
712            BluetoothSocket::Android(a) => a.flush(),
713            #[cfg(target_os = "linux")]
714            BluetoothSocket::Bluez(b) => b.flush(),
715            #[cfg(target_os = "windows")]
716            BluetoothSocket::Windows(w) => w.flush(),
717        }
718    }
719}
720
721/// A bluetooth rfcomm socket
722#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
723pub enum BluetoothSocket {
724    /// The android based rfcomm socket
725    #[cfg(target_os = "android")]
726    Android(android::BluetoothSocket),
727    /// Linux using bluez library
728    #[cfg(target_os = "linux")]
729    Bluez(linux::BluetoothRfcommSocket),
730    /// Windows bluetooth socket
731    #[cfg(target_os = "windows")]
732    Windows(windows::BluetoothRfcommSocket),
733}