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
24mod bluetooth_uuid;
25pub use bluetooth_uuid::BluetoothUuid;
26
27/// Commands issued to the library
28#[derive(Debug, serde::Deserialize, serde::Serialize)]
29pub enum BluetoothCommand {
30    /// Detect all bluetooth adapters present on the system
31    DetectAdapters,
32    /// Find out how many bluetooth adapters are detected
33    QueryNumAdapters,
34}
35
36/// Messages that can be sent specifically to the app user hosting the bluetooth controls
37pub enum MessageToBluetoothHost {
38    /// The passkey used for pairing devices
39    DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
40    /// The passkey to confirm for pairing
41    ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
42    /// Cancal the passkey display
43    CancelDisplayPasskey,
44}
45
46#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
47/// Messages that are send directly from the bluetooth host
48pub enum MessageFromBluetoothHost {
49    /// A response about the active pairing passkey
50    PasskeyMessage(ResponseToPasskey),
51}
52
53#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
54/// The user response to a bluetooth passkey
55pub enum ResponseToPasskey {
56    /// The passkey is accepted
57    Yes,
58    /// The passkey is not accepted
59    No,
60    /// The process is canceled by the user
61    Cancel,
62    /// Waiting on the user to decide
63    Waiting,
64}
65
66/// Responses issued by the library
67pub enum BluetoothResponse {
68    /// The number of bluetooth adapters detected
69    Adapters(usize),
70}
71
72/// Settings for an rfcomm profile
73#[derive(Clone)]
74pub struct BluetoothRfcommProfileSettings {
75    /// The uuid for the profile
76    pub uuid: String,
77    /// User readable name for the profile
78    pub name: Option<String>,
79    /// The service uuid for the profile (can be the same as service)
80    pub service_uuid: Option<String>,
81    /// The channel to use
82    pub channel: Option<u16>,
83    /// PSM number used for UUIDS and SDP (if applicable)
84    pub psm: Option<u16>,
85    /// Is authentication required for a connection
86    pub authenticate: Option<bool>,
87    /// Is authorization required for a connection
88    pub authorize: Option<bool>,
89    /// For client profiles, This will force connection of the channel when a remote device is connected
90    pub auto_connect: Option<bool>,
91    /// manual SDP record
92    pub sdp_record: Option<String>,
93    /// SDP version
94    pub sdp_version: Option<u16>,
95    /// SDP profile features
96    pub sdp_features: Option<u16>,
97}
98
99/// Settings for an rfcomm profile
100#[derive(Clone)]
101pub struct BluetoothL2capProfileSettings {
102    /// The uuid for the profile
103    pub uuid: String,
104    /// User readable name for the profile
105    pub name: Option<String>,
106    /// The service uuid for the profile (can be the same as service)
107    pub service_uuid: Option<String>,
108    /// The channel to use
109    pub channel: Option<u16>,
110    /// PSM number used for UUIDS and SDP (if applicable)
111    pub psm: Option<u16>,
112    /// Is authentication required for a connection
113    pub authenticate: Option<bool>,
114    /// Is authorization required for a connection
115    pub authorize: Option<bool>,
116    /// For client profiles, This will force connection of the channel when a remote device is connected
117    pub auto_connect: Option<bool>,
118    /// manual SDP record
119    pub sdp_record: Option<String>,
120    /// SDP version
121    pub sdp_version: Option<u16>,
122    /// SDP profile features
123    pub sdp_features: Option<u16>,
124}
125
126/// The trait that implements managing when bluetooth discovery is enabled
127#[enum_dispatch::enum_dispatch]
128pub trait BluetoothDiscoveryTrait {}
129
130/// The trait for the object that manages bluetooth discovery
131#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
132pub enum BluetoothDiscovery {
133    /// The android version
134    #[cfg(target_os = "android")]
135    Android(android::BluetoothDiscovery),
136    /// Linux bluez library implementation
137    #[cfg(target_os = "linux")]
138    Bluez(linux::BluetoothDiscovery),
139}
140
141/// The address of a bluetooth adapter
142pub enum BluetoothAdapterAddress {
143    /// The address in string form
144    String(String),
145    /// The address in byte form
146    Byte([u8; 6]),
147}
148
149/// Common async functionality for the bluetooth adapter
150#[enum_dispatch::enum_dispatch]
151#[async_trait::async_trait]
152pub trait AsyncBluetoothAdapterTrait {
153    /// Attempt to register a new rfcomm profile
154    async fn register_rfcomm_profile(
155        &self,
156        settings: BluetoothRfcommProfileSettings,
157    ) -> Result<BluetoothRfcommProfileAsync, String>;
158    /// Attempt to register a new l2cap profile
159    async fn register_l2cap_profile(
160        &self,
161        settings: BluetoothL2capProfileSettings,
162    ) -> Result<BluetoothL2capProfileAsync, String>;
163    ///Get a list of paired bluetooth devices
164    fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
165    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
166    fn start_discovery(&self) -> BluetoothDiscovery;
167    /// Get the mac addresses of all bluetooth adapters for the system
168    async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
169    /// Set the discoverable property
170    async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
171}
172
173/// Common sync functionality for the bluetooth adapter
174#[enum_dispatch::enum_dispatch]
175pub trait SyncBluetoothAdapterTrait {
176    /// Attempt to register a new rfcomm profile
177    fn register_rfcomm_profile(
178        &self,
179        settings: BluetoothRfcommProfileSettings,
180    ) -> Result<BluetoothRfcommProfileSync, String>;
181    /// Attempt to register a new lc2ap profile
182    fn register_l2cap_profile(
183        &self,
184        settings: BluetoothL2capProfileSettings,
185    ) -> Result<BluetoothL2capProfileAsync, String>;
186    ///Get a list of paired bluetooth devices
187    fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
188    /// Start discovery of bluetooth devices. Run this and drop the result to cancel discovery
189    fn start_discovery(&self) -> BluetoothDiscovery;
190    /// Get the mac addresses of all bluetooth adapters for the system
191    fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
192    /// Set the discoverable property
193    fn set_discoverable(&self, d: bool) -> Result<(), ()>;
194}
195
196/// Common functionality for the bluetooth adapter
197#[enum_dispatch::enum_dispatch]
198pub trait BluetoothAdapterTrait {
199    /// Returns Some when the async interface is supported
200    fn supports_async(&mut self) -> Option<&mut dyn AsyncBluetoothAdapterTrait>;
201    /// Returns Some when the sync interface is supported
202    fn supports_sync(&mut self) -> Option<&mut dyn SyncBluetoothAdapterTrait>;
203}
204
205/// The pairing status of a bluetooth device
206pub enum PairingStatus {
207    /// The device is not paired
208    NotPaired,
209    /// The device is in the pairing process
210    Pairing,
211    /// The device is paired
212    Paired,
213    /// The status is unknown or invalid
214    Unknown,
215}
216
217/// The trait that all bluetooth devices must implement
218#[enum_dispatch::enum_dispatch]
219pub trait BluetoothDeviceTrait {
220    /// Get all known uuids for this device
221    fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
222
223    /// Retrieve the device name
224    fn get_name(&self) -> Result<String, std::io::Error>;
225
226    /// Retrieve the device address
227    fn get_address(&mut self) -> Result<String, std::io::Error>;
228
229    /// Retrieve the device pairing status
230    fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
231
232    /// Attempt to get an rfcomm socket for the given uuid and security setting
233    fn get_rfcomm_socket(
234        &mut self,
235        uuid: BluetoothUuid,
236        is_secure: bool,
237    ) -> Result<BluetoothSocket, String>;
238
239    /// Attempt to get an l2cap socket for the given uuid and security setting
240    fn get_l2cap_socket(
241        &mut self,
242        uuid: BluetoothUuid,
243        is_secure: bool,
244    ) -> Result<BluetoothSocket, String>;
245
246    /// Run the service discovery protocol
247    fn run_sdp(&mut self);
248}
249
250/// A bluetooth device
251#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
252pub enum BluetoothDevice {
253    /// Bluetooth device on android
254    #[cfg(target_os = "android")]
255    Android(android::BluetoothDevice),
256    /// Bluetooth device on linux using the bluez library
257    #[cfg(target_os = "linux")]
258    Bluez(bluer::Device),
259}
260
261/// Represents a bluetooth adapter that communicates to bluetooth devices
262#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
263pub enum BluetoothAdapter {
264    /// The bluetooth adapter for android systems
265    #[cfg(target_os = "android")]
266    Android(android::Bluetooth),
267    /// On linux, bluetooth adapter using the bluez library
268    #[cfg(target_os = "linux")]
269    Bluez(linux::BluetoothHandler),
270}
271
272/// A builder for `BluetoothAdapter`
273pub struct BluetoothAdapterBuilder {
274    /// The androidapp object
275    #[cfg(target_os = "android")]
276    app: Option<AndroidApp>,
277    /// The sender to send messages to the bluetooth host
278    s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
279}
280
281impl Default for BluetoothAdapterBuilder {
282    fn default() -> Self {
283        Self::new()
284    }
285}
286
287impl BluetoothAdapterBuilder {
288    /// Construct a new self
289    pub fn new() -> Self {
290        Self {
291            #[cfg(target_os = "android")]
292            app: None,
293            s: None,
294        }
295    }
296
297    /// Put the required `AndroidApp` object into the builder
298    #[cfg(target_os = "android")]
299    pub fn with_android_app(&mut self, app: AndroidApp) {
300        self.app = Some(app);
301    }
302
303    /// Add the sender to the builder
304    pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
305        self.s = Some(s);
306    }
307
308    /// Do the build
309    pub async fn build(self) -> Result<BluetoothAdapter, String> {
310        #[cfg(target_os = "android")]
311        {
312            let java = android::Java::make(self.app.unwrap());
313            return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
314                Arc::new(Mutex::new(java)),
315            )));
316        }
317        #[cfg(target_os = "linux")]
318        {
319            return Ok(BluetoothAdapter::Bluez(
320                linux::BluetoothHandler::new(self.s.unwrap()).await?,
321            ));
322        }
323        Err("No builders available".to_string())
324    }
325}
326
327/// An active stream for bluetooth communications
328pub enum BluetoothStream {
329    /// On linux, a stream using the bluez library
330    #[cfg(target_os = "linux")]
331    Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
332    /// Android code for a bluetooth stream
333    #[cfg(target_os = "android")]
334    Android(android::RfcommStream),
335}
336
337impl BluetoothStream {
338    /// Used to check to see if the object supports async read, and then use the functionality
339    pub fn supports_async_read(
340        self: std::pin::Pin<&mut Self>,
341    ) -> Option<&mut dyn tokio::io::AsyncRead> {
342        match self.get_mut() {
343            #[cfg(target_os = "linux")]
344            BluetoothStream::Bluez(pin) => Some(pin),
345            #[cfg(target_os = "android")]
346            BluetoothStream::Android(_pin) => None,
347        }
348    }
349
350    /// Used to check to see if the object supports async write, and then use the functionality
351    pub fn supports_async_write(
352        self: std::pin::Pin<&mut Self>,
353    ) -> Option<&mut dyn tokio::io::AsyncWrite> {
354        match self.get_mut() {
355            #[cfg(target_os = "linux")]
356            BluetoothStream::Bluez(pin) => Some(pin),
357            #[cfg(target_os = "android")]
358            BluetoothStream::Android(_pin) => None,
359        }
360    }
361
362    /// Used to try to use synchronous read functionality
363    pub fn supports_sync_read(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Read> {
364        match self.get_mut() {
365            #[cfg(target_os = "linux")]
366            BluetoothStream::Bluez(pin) => None,
367            #[cfg(target_os = "android")]
368            BluetoothStream::Android(pin) => Some(pin),
369        }
370    }
371
372    /// Used to try to use synchronous write functionality
373    pub fn supports_sync_write(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Write> {
374        match self.get_mut() {
375            #[cfg(target_os = "linux")]
376            BluetoothStream::Bluez(pin) => None,
377            #[cfg(target_os = "android")]
378            BluetoothStream::Android(pin) => Some(pin),
379        }
380    }
381}
382
383/// The trait for bluetooth rfcomm objects that can be connected or accepted
384#[enum_dispatch::enum_dispatch]
385pub trait BluetoothRfcommConnectableAsyncTrait {
386    /// Accept a connection from a bluetooth peer
387    async fn accept(self) -> Result<BluetoothStream, String>;
388}
389
390/// A bluetooth profile for rfcomm channels
391#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
392pub enum BluetoothRfcommConnectableAsync {
393    /// The android object for the profile
394    #[cfg(target_os = "android")]
395    Android(android::BluetoothRfcommConnectable),
396    /// The bluez library in linux is responsible for the profile
397    #[cfg(target_os = "linux")]
398    Bluez(bluer::rfcomm::ConnectRequest),
399}
400
401/// The trait for bluetooth rfcomm objects that can be connected or accepted
402#[enum_dispatch::enum_dispatch]
403pub trait BluetoothRfcommConnectableSyncTrait {
404    /// Accept a connection from a bluetooth peer
405    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
406}
407
408/// A bluetooth profile for rfcomm channels
409#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
410pub enum BluetoothRfcommConnectableSync {
411    /// The android object for the profile
412    #[cfg(target_os = "android")]
413    Android(android::BluetoothRfcommConnectable),
414}
415
416/// The trait for bluetooth rfcomm objects that can be connected or accepted
417#[enum_dispatch::enum_dispatch]
418pub trait BluetoothL2capConnectableAsyncTrait {
419    /// Accept a connection from a bluetooth peer
420    async fn accept(self) -> Result<BluetoothStream, String>;
421}
422
423/// A bluetooth profile for rfcomm channels
424#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
425pub enum BluetoothL2capConnectableAsync {
426    /// The android object for the profile
427    #[cfg(target_os = "android")]
428    Android(android::BluetoothRfcommConnectable),
429    /// The bluez library in linux is responsible for the profile
430    #[cfg(target_os = "linux")]
431    Bluez(bluer::rfcomm::ConnectRequest),
432}
433
434/// The trait for bluetooth rfcomm objects that can be connected or accepted
435#[enum_dispatch::enum_dispatch]
436pub trait BluetoothL2capConnectableSyncTrait {
437    /// Accept a connection from a bluetooth peer
438    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
439}
440
441/// A bluetooth profile for rfcomm channels
442#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
443pub enum BluetoothL2capConnectableSync {
444    /// The android object for the profile
445    #[cfg(target_os = "android")]
446    Android(android::BluetoothRfcommConnectable),
447}
448
449/// Allows building an object to connect to bluetooth devices
450#[enum_dispatch::enum_dispatch]
451pub trait BluetoothRfcommProfileAsyncTrait {
452    /// Get an object in order to accept a connection from or connect to a bluetooth peer
453    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
454}
455
456/// Allows building an object to connect to bluetooth devices
457#[enum_dispatch::enum_dispatch]
458pub trait BluetoothRfcommProfileSyncTrait {
459    /// Get an object in order to accept a connection from or connect to a bluetooth peer
460    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
461}
462
463/// A bluetooth profile for rfcomm channels
464#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
465pub enum BluetoothRfcommProfileAsync {
466    /// The bluez library in linux is responsible for the profile
467    #[cfg(target_os = "linux")]
468    Bluez(bluer::rfcomm::ProfileHandle),
469    /// A dummy handler
470    Dummy(Dummy),
471}
472
473/// A bluetooth profile for rfcomm channels
474#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
475pub enum BluetoothRfcommProfileSync {
476    /// Android rfcomm profile
477    #[cfg(target_os = "android")]
478    Android(android::BluetoothRfcommProfile),
479    /// A dummy handler
480    Dummy(Dummy),
481}
482
483/// A bluetooth profile for rfcomm channels
484#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
485pub enum BluetoothL2capProfileAsync {
486    /// The bluez library in linux is responsible for the profile
487    #[cfg(target_os = "linux")]
488    Bluez(bluer::rfcomm::ProfileHandle),
489    /// A dummy handler
490    Dummy(Dummy),
491}
492
493/// A bluetooth profile for rfcomm channels
494#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
495pub enum BluetoothL2capProfileSync {
496    /// Android rfcomm profile
497    #[cfg(target_os = "android")]
498    Android(android::BluetoothRfcommProfile),
499    /// A dummy handler
500    Dummy(Dummy),
501}
502
503/// A dummy struct for ensuring enums are not empty
504pub struct Dummy {}
505
506impl BluetoothRfcommProfileSyncTrait for Dummy {
507    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
508        unimplemented!()
509    }
510}
511
512impl BluetoothRfcommProfileAsyncTrait for Dummy {
513    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
514        unimplemented!()
515    }
516}
517
518/// The common functions for all bluetooth rfcomm sockets
519#[enum_dispatch::enum_dispatch]
520pub trait BluetoothSocketTrait {}
521
522/// A bluetooth rfcomm socket
523#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
524pub enum BluetoothSocket<'a> {
525    /// The android based rfcomm socket
526    #[cfg(target_os = "android")]
527    Android(&'a mut android::BluetoothSocket),
528    /// Linux using bluez library
529    #[cfg(target_os = "linux")]
530    Bluez(&'a mut linux::BluetoothRfcommSocket),
531}