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
247/// A bluetooth device
248#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
249pub enum BluetoothDevice {
250    /// Bluetooth device on android
251    #[cfg(target_os = "android")]
252    Android(android::BluetoothDevice),
253    /// Bluetooth device on linux using the bluez library
254    #[cfg(target_os = "linux")]
255    Bluez(bluer::Device),
256}
257
258/// Represents a bluetooth adapter that communicates to bluetooth devices
259#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
260pub enum BluetoothAdapter {
261    /// The bluetooth adapter for android systems
262    #[cfg(target_os = "android")]
263    Android(android::Bluetooth),
264    /// On linux, bluetooth adapter using the bluez library
265    #[cfg(target_os = "linux")]
266    Bluez(linux::BluetoothHandler),
267}
268
269/// A builder for `BluetoothAdapter`
270pub struct BluetoothAdapterBuilder {
271    /// The androidapp object
272    #[cfg(target_os = "android")]
273    app: Option<AndroidApp>,
274    /// The sender to send messages to the bluetooth host
275    s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
276}
277
278impl Default for BluetoothAdapterBuilder {
279    fn default() -> Self {
280        Self::new()
281    }
282}
283
284impl BluetoothAdapterBuilder {
285    /// Construct a new self
286    pub fn new() -> Self {
287        Self {
288            #[cfg(target_os = "android")]
289            app: None,
290            s: None,
291        }
292    }
293
294    /// Put the required `AndroidApp` object into the builder
295    #[cfg(target_os = "android")]
296    pub fn with_android_app(&mut self, app: AndroidApp) {
297        self.app = Some(app);
298    }
299
300    /// Add the sender to the builder
301    pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
302        self.s = Some(s);
303    }
304
305    /// Do the build
306    pub async fn build(self) -> Result<BluetoothAdapter, String> {
307        #[cfg(target_os = "android")]
308        {
309            let java = android::Java::make(self.app.unwrap());
310            return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
311                Arc::new(Mutex::new(java)),
312            )));
313        }
314        #[cfg(target_os = "linux")]
315        {
316            return Ok(BluetoothAdapter::Bluez(
317                linux::BluetoothHandler::new(self.s.unwrap()).await?,
318            ));
319        }
320        Err("No builders available".to_string())
321    }
322}
323
324/// An active stream for bluetooth communications
325pub enum BluetoothStream {
326    /// On linux, a stream using the bluez library
327    #[cfg(target_os = "linux")]
328    Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
329    /// Android code for a bluetooth stream
330    #[cfg(target_os = "android")]
331    Android(android::RfcommStream),
332}
333
334impl BluetoothStream {
335    /// Used to check to see if the object supports async read, and then use the functionality
336    pub fn supports_async_read(
337        self: std::pin::Pin<&mut Self>,
338    ) -> Option<&mut dyn tokio::io::AsyncRead> {
339        match self.get_mut() {
340            #[cfg(target_os = "linux")]
341            BluetoothStream::Bluez(pin) => Some(pin),
342            #[cfg(target_os = "android")]
343            BluetoothStream::Android(_pin) => None,
344        }
345    }
346
347    /// Used to check to see if the object supports async write, and then use the functionality
348    pub fn supports_async_write(
349        self: std::pin::Pin<&mut Self>,
350    ) -> Option<&mut dyn tokio::io::AsyncWrite> {
351        match self.get_mut() {
352            #[cfg(target_os = "linux")]
353            BluetoothStream::Bluez(pin) => Some(pin),
354            #[cfg(target_os = "android")]
355            BluetoothStream::Android(_pin) => None,
356        }
357    }
358
359    /// Used to try to use synchronous read functionality
360    pub fn supports_sync_read(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Read> {
361        match self.get_mut() {
362            #[cfg(target_os = "linux")]
363            BluetoothStream::Bluez(pin) => None,
364            #[cfg(target_os = "android")]
365            BluetoothStream::Android(pin) => Some(pin),
366        }
367    }
368
369    /// Used to try to use synchronous write functionality
370    pub fn supports_sync_write(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Write> {
371        match self.get_mut() {
372            #[cfg(target_os = "linux")]
373            BluetoothStream::Bluez(pin) => None,
374            #[cfg(target_os = "android")]
375            BluetoothStream::Android(pin) => Some(pin),
376        }
377    }
378}
379
380/// The trait for bluetooth rfcomm objects that can be connected or accepted
381#[enum_dispatch::enum_dispatch]
382pub trait BluetoothRfcommConnectableAsyncTrait {
383    /// Accept a connection from a bluetooth peer
384    async fn accept(self) -> Result<BluetoothStream, String>;
385}
386
387/// A bluetooth profile for rfcomm channels
388#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
389pub enum BluetoothRfcommConnectableAsync {
390    /// The android object for the profile
391    #[cfg(target_os = "android")]
392    Android(android::BluetoothRfcommConnectable),
393    /// The bluez library in linux is responsible for the profile
394    #[cfg(target_os = "linux")]
395    Bluez(bluer::rfcomm::ConnectRequest),
396}
397
398/// The trait for bluetooth rfcomm objects that can be connected or accepted
399#[enum_dispatch::enum_dispatch]
400pub trait BluetoothRfcommConnectableSyncTrait {
401    /// Accept a connection from a bluetooth peer
402    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
403}
404
405/// A bluetooth profile for rfcomm channels
406#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
407pub enum BluetoothRfcommConnectableSync {
408    /// The android object for the profile
409    #[cfg(target_os = "android")]
410    Android(android::BluetoothRfcommConnectable),
411}
412
413
414/// The trait for bluetooth rfcomm objects that can be connected or accepted
415#[enum_dispatch::enum_dispatch]
416pub trait BluetoothL2capConnectableAsyncTrait {
417    /// Accept a connection from a bluetooth peer
418    async fn accept(self) -> Result<BluetoothStream, String>;
419}
420
421/// A bluetooth profile for rfcomm channels
422#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
423pub enum BluetoothL2capConnectableAsync {
424    /// The android object for the profile
425    #[cfg(target_os = "android")]
426    Android(android::BluetoothRfcommConnectable),
427    /// The bluez library in linux is responsible for the profile
428    #[cfg(target_os = "linux")]
429    Bluez(bluer::rfcomm::ConnectRequest),
430}
431
432/// The trait for bluetooth rfcomm objects that can be connected or accepted
433#[enum_dispatch::enum_dispatch]
434pub trait BluetoothL2capConnectableSyncTrait {
435    /// Accept a connection from a bluetooth peer
436    fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
437}
438
439/// A bluetooth profile for rfcomm channels
440#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
441pub enum BluetoothL2capConnectableSync {
442    /// The android object for the profile
443    #[cfg(target_os = "android")]
444    Android(android::BluetoothRfcommConnectable),
445}
446
447/// Allows building an object to connect to bluetooth devices
448#[enum_dispatch::enum_dispatch]
449pub trait BluetoothRfcommProfileAsyncTrait {
450    /// Get an object in order to accept a connection from or connect to a bluetooth peer
451    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
452}
453
454/// Allows building an object to connect to bluetooth devices
455#[enum_dispatch::enum_dispatch]
456pub trait BluetoothRfcommProfileSyncTrait {
457    /// Get an object in order to accept a connection from or connect to a bluetooth peer
458    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
459}
460
461/// A bluetooth profile for rfcomm channels
462#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
463pub enum BluetoothRfcommProfileAsync {
464    /// The bluez library in linux is responsible for the profile
465    #[cfg(target_os = "linux")]
466    Bluez(bluer::rfcomm::ProfileHandle),
467    /// A dummy handler
468    Dummy(Dummy),
469}
470
471/// A bluetooth profile for rfcomm channels
472#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
473pub enum BluetoothRfcommProfileSync {
474    /// Android rfcomm profile
475    #[cfg(target_os = "android")]
476    Android(android::BluetoothRfcommProfile),
477    /// A dummy handler
478    Dummy(Dummy),
479}
480
481/// A bluetooth profile for rfcomm channels
482#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
483pub enum BluetoothL2capProfileAsync {
484    /// The bluez library in linux is responsible for the profile
485    #[cfg(target_os = "linux")]
486    Bluez(bluer::rfcomm::ProfileHandle),
487    /// A dummy handler
488    Dummy(Dummy),
489}
490
491/// A bluetooth profile for rfcomm channels
492#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
493pub enum BluetoothL2capProfileSync {
494    /// Android rfcomm profile
495    #[cfg(target_os = "android")]
496    Android(android::BluetoothRfcommProfile),
497    /// A dummy handler
498    Dummy(Dummy),
499}
500
501/// A dummy struct for ensuring enums are not empty
502pub struct Dummy {}
503
504impl BluetoothRfcommProfileSyncTrait for Dummy {
505    fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync,String> {
506        unimplemented!()
507    }
508}
509
510impl BluetoothRfcommProfileAsyncTrait for Dummy {
511    async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync,String> {
512        unimplemented!()
513    }
514}
515
516/// The common functions for all bluetooth rfcomm sockets
517#[enum_dispatch::enum_dispatch]
518pub trait BluetoothSocketTrait {}
519
520/// A bluetooth rfcomm socket
521#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
522pub enum BluetoothSocket<'a> {
523    /// The android based rfcomm socket
524    #[cfg(target_os = "android")]
525    Android(&'a mut android::BluetoothSocket),
526    /// Linux using bluez library
527    #[cfg(target_os = "linux")]
528    Bluez(&'a mut linux::BluetoothRfcommSocket),
529}
530