1#![deny(missing_docs)]
2#![deny(clippy::missing_docs_in_private_items)]
3#![warn(unused_extern_crates)]
4
5#[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#[derive(Debug, serde::Deserialize, serde::Serialize)]
29pub enum BluetoothCommand {
30 DetectAdapters,
32 QueryNumAdapters,
34}
35
36pub enum MessageToBluetoothHost {
38 DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
40 ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
42 CancelDisplayPasskey,
44}
45
46#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
47pub enum MessageFromBluetoothHost {
49 PasskeyMessage(ResponseToPasskey),
51}
52
53#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
54pub enum ResponseToPasskey {
56 Yes,
58 No,
60 Cancel,
62 Waiting,
64}
65
66pub enum BluetoothResponse {
68 Adapters(usize),
70}
71
72#[derive(Clone)]
74pub struct BluetoothRfcommProfileSettings {
75 pub uuid: String,
77 pub name: Option<String>,
79 pub service_uuid: Option<String>,
81 pub channel: Option<u16>,
83 pub psm: Option<u16>,
85 pub authenticate: Option<bool>,
87 pub authorize: Option<bool>,
89 pub auto_connect: Option<bool>,
91 pub sdp_record: Option<String>,
93 pub sdp_version: Option<u16>,
95 pub sdp_features: Option<u16>,
97}
98
99#[derive(Clone)]
101pub struct BluetoothL2capProfileSettings {
102 pub uuid: String,
104 pub name: Option<String>,
106 pub service_uuid: Option<String>,
108 pub channel: Option<u16>,
110 pub psm: Option<u16>,
112 pub authenticate: Option<bool>,
114 pub authorize: Option<bool>,
116 pub auto_connect: Option<bool>,
118 pub sdp_record: Option<String>,
120 pub sdp_version: Option<u16>,
122 pub sdp_features: Option<u16>,
124}
125
126#[enum_dispatch::enum_dispatch]
128pub trait BluetoothDiscoveryTrait {}
129
130#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
132pub enum BluetoothDiscovery {
133 #[cfg(target_os = "android")]
135 Android(android::BluetoothDiscovery),
136 #[cfg(target_os = "linux")]
138 Bluez(linux::BluetoothDiscovery),
139}
140
141pub enum BluetoothAdapterAddress {
143 String(String),
145 Byte([u8; 6]),
147}
148
149#[enum_dispatch::enum_dispatch]
151#[async_trait::async_trait]
152pub trait AsyncBluetoothAdapterTrait {
153 async fn register_rfcomm_profile(
155 &self,
156 settings: BluetoothRfcommProfileSettings,
157 ) -> Result<BluetoothRfcommProfileAsync, String>;
158 async fn register_l2cap_profile(
160 &self,
161 settings: BluetoothL2capProfileSettings,
162 ) -> Result<BluetoothL2capProfileAsync, String>;
163 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
165 fn start_discovery(&self) -> BluetoothDiscovery;
167 async fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
169 async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
171}
172
173#[enum_dispatch::enum_dispatch]
175pub trait SyncBluetoothAdapterTrait {
176 fn register_rfcomm_profile(
178 &self,
179 settings: BluetoothRfcommProfileSettings,
180 ) -> Result<BluetoothRfcommProfileSync, String>;
181 fn register_l2cap_profile(
183 &self,
184 settings: BluetoothL2capProfileSettings,
185 ) -> Result<BluetoothL2capProfileAsync, String>;
186 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
188 fn start_discovery(&self) -> BluetoothDiscovery;
190 fn addresses(&self) -> Vec<BluetoothAdapterAddress>;
192 fn set_discoverable(&self, d: bool) -> Result<(), ()>;
194}
195
196#[enum_dispatch::enum_dispatch]
198pub trait BluetoothAdapterTrait {
199 fn supports_async(&mut self) -> Option<&mut dyn AsyncBluetoothAdapterTrait>;
201 fn supports_sync(&mut self) -> Option<&mut dyn SyncBluetoothAdapterTrait>;
203}
204
205pub enum PairingStatus {
207 NotPaired,
209 Pairing,
211 Paired,
213 Unknown,
215}
216
217#[enum_dispatch::enum_dispatch]
219pub trait BluetoothDeviceTrait {
220 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
222
223 fn get_name(&self) -> Result<String, std::io::Error>;
225
226 fn get_address(&mut self) -> Result<String, std::io::Error>;
228
229 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
231
232 fn get_rfcomm_socket(
234 &mut self,
235 uuid: BluetoothUuid,
236 is_secure: bool,
237 ) -> Result<BluetoothSocket, String>;
238
239 fn get_l2cap_socket(
241 &mut self,
242 uuid: BluetoothUuid,
243 is_secure: bool,
244 ) -> Result<BluetoothSocket, String>;
245}
246
247#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
249pub enum BluetoothDevice {
250 #[cfg(target_os = "android")]
252 Android(android::BluetoothDevice),
253 #[cfg(target_os = "linux")]
255 Bluez(bluer::Device),
256}
257
258#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
260pub enum BluetoothAdapter {
261 #[cfg(target_os = "android")]
263 Android(android::Bluetooth),
264 #[cfg(target_os = "linux")]
266 Bluez(linux::BluetoothHandler),
267}
268
269pub struct BluetoothAdapterBuilder {
271 #[cfg(target_os = "android")]
273 app: Option<AndroidApp>,
274 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 pub fn new() -> Self {
287 Self {
288 #[cfg(target_os = "android")]
289 app: None,
290 s: None,
291 }
292 }
293
294 #[cfg(target_os = "android")]
296 pub fn with_android_app(&mut self, app: AndroidApp) {
297 self.app = Some(app);
298 }
299
300 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
302 self.s = Some(s);
303 }
304
305 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
324pub enum BluetoothStream {
326 #[cfg(target_os = "linux")]
328 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
329 #[cfg(target_os = "android")]
331 Android(android::RfcommStream),
332}
333
334impl BluetoothStream {
335 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 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 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 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#[enum_dispatch::enum_dispatch]
382pub trait BluetoothRfcommConnectableAsyncTrait {
383 async fn accept(self) -> Result<BluetoothStream, String>;
385}
386
387#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
389pub enum BluetoothRfcommConnectableAsync {
390 #[cfg(target_os = "android")]
392 Android(android::BluetoothRfcommConnectable),
393 #[cfg(target_os = "linux")]
395 Bluez(bluer::rfcomm::ConnectRequest),
396}
397
398#[enum_dispatch::enum_dispatch]
400pub trait BluetoothRfcommConnectableSyncTrait {
401 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
403}
404
405#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
407pub enum BluetoothRfcommConnectableSync {
408 #[cfg(target_os = "android")]
410 Android(android::BluetoothRfcommConnectable),
411}
412
413
414#[enum_dispatch::enum_dispatch]
416pub trait BluetoothL2capConnectableAsyncTrait {
417 async fn accept(self) -> Result<BluetoothStream, String>;
419}
420
421#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
423pub enum BluetoothL2capConnectableAsync {
424 #[cfg(target_os = "android")]
426 Android(android::BluetoothRfcommConnectable),
427 #[cfg(target_os = "linux")]
429 Bluez(bluer::rfcomm::ConnectRequest),
430}
431
432#[enum_dispatch::enum_dispatch]
434pub trait BluetoothL2capConnectableSyncTrait {
435 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
437}
438
439#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
441pub enum BluetoothL2capConnectableSync {
442 #[cfg(target_os = "android")]
444 Android(android::BluetoothRfcommConnectable),
445}
446
447#[enum_dispatch::enum_dispatch]
449pub trait BluetoothRfcommProfileAsyncTrait {
450 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
452}
453
454#[enum_dispatch::enum_dispatch]
456pub trait BluetoothRfcommProfileSyncTrait {
457 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
459}
460
461#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
463pub enum BluetoothRfcommProfileAsync {
464 #[cfg(target_os = "linux")]
466 Bluez(bluer::rfcomm::ProfileHandle),
467 Dummy(Dummy),
469}
470
471#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
473pub enum BluetoothRfcommProfileSync {
474 #[cfg(target_os = "android")]
476 Android(android::BluetoothRfcommProfile),
477 Dummy(Dummy),
479}
480
481#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
483pub enum BluetoothL2capProfileAsync {
484 #[cfg(target_os = "linux")]
486 Bluez(bluer::rfcomm::ProfileHandle),
487 Dummy(Dummy),
489}
490
491#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
493pub enum BluetoothL2capProfileSync {
494 #[cfg(target_os = "android")]
496 Android(android::BluetoothRfcommProfile),
497 Dummy(Dummy),
499}
500
501pub 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#[enum_dispatch::enum_dispatch]
518pub trait BluetoothSocketTrait {}
519
520#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
522pub enum BluetoothSocket<'a> {
523 #[cfg(target_os = "android")]
525 Android(&'a mut android::BluetoothSocket),
526 #[cfg(target_os = "linux")]
528 Bluez(&'a mut linux::BluetoothRfcommSocket),
529}
530