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 fn run_sdp(&mut self);
248}
249
250#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
252pub enum BluetoothDevice {
253 #[cfg(target_os = "android")]
255 Android(android::BluetoothDevice),
256 #[cfg(target_os = "linux")]
258 Bluez(bluer::Device),
259}
260
261#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
263pub enum BluetoothAdapter {
264 #[cfg(target_os = "android")]
266 Android(android::Bluetooth),
267 #[cfg(target_os = "linux")]
269 Bluez(linux::BluetoothHandler),
270}
271
272pub struct BluetoothAdapterBuilder {
274 #[cfg(target_os = "android")]
276 app: Option<AndroidApp>,
277 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 pub fn new() -> Self {
290 Self {
291 #[cfg(target_os = "android")]
292 app: None,
293 s: None,
294 }
295 }
296
297 #[cfg(target_os = "android")]
299 pub fn with_android_app(&mut self, app: AndroidApp) {
300 self.app = Some(app);
301 }
302
303 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
305 self.s = Some(s);
306 }
307
308 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
327pub enum BluetoothStream {
329 #[cfg(target_os = "linux")]
331 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
332 #[cfg(target_os = "android")]
334 Android(android::RfcommStream),
335}
336
337impl BluetoothStream {
338 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 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 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 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#[enum_dispatch::enum_dispatch]
385pub trait BluetoothRfcommConnectableAsyncTrait {
386 async fn accept(self) -> Result<BluetoothStream, String>;
388}
389
390#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
392pub enum BluetoothRfcommConnectableAsync {
393 #[cfg(target_os = "android")]
395 Android(android::BluetoothRfcommConnectable),
396 #[cfg(target_os = "linux")]
398 Bluez(bluer::rfcomm::ConnectRequest),
399}
400
401#[enum_dispatch::enum_dispatch]
403pub trait BluetoothRfcommConnectableSyncTrait {
404 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
406}
407
408#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
410pub enum BluetoothRfcommConnectableSync {
411 #[cfg(target_os = "android")]
413 Android(android::BluetoothRfcommConnectable),
414}
415
416#[enum_dispatch::enum_dispatch]
418pub trait BluetoothL2capConnectableAsyncTrait {
419 async fn accept(self) -> Result<BluetoothStream, String>;
421}
422
423#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
425pub enum BluetoothL2capConnectableAsync {
426 #[cfg(target_os = "android")]
428 Android(android::BluetoothRfcommConnectable),
429 #[cfg(target_os = "linux")]
431 Bluez(bluer::rfcomm::ConnectRequest),
432}
433
434#[enum_dispatch::enum_dispatch]
436pub trait BluetoothL2capConnectableSyncTrait {
437 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
439}
440
441#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
443pub enum BluetoothL2capConnectableSync {
444 #[cfg(target_os = "android")]
446 Android(android::BluetoothRfcommConnectable),
447}
448
449#[enum_dispatch::enum_dispatch]
451pub trait BluetoothRfcommProfileAsyncTrait {
452 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
454}
455
456#[enum_dispatch::enum_dispatch]
458pub trait BluetoothRfcommProfileSyncTrait {
459 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
461}
462
463#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
465pub enum BluetoothRfcommProfileAsync {
466 #[cfg(target_os = "linux")]
468 Bluez(bluer::rfcomm::ProfileHandle),
469 Dummy(Dummy),
471}
472
473#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
475pub enum BluetoothRfcommProfileSync {
476 #[cfg(target_os = "android")]
478 Android(android::BluetoothRfcommProfile),
479 Dummy(Dummy),
481}
482
483#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
485pub enum BluetoothL2capProfileAsync {
486 #[cfg(target_os = "linux")]
488 Bluez(bluer::rfcomm::ProfileHandle),
489 Dummy(Dummy),
491}
492
493#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
495pub enum BluetoothL2capProfileSync {
496 #[cfg(target_os = "android")]
498 Android(android::BluetoothRfcommProfile),
499 Dummy(Dummy),
501}
502
503pub 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#[enum_dispatch::enum_dispatch]
520pub trait BluetoothSocketTrait {}
521
522#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
524pub enum BluetoothSocket<'a> {
525 #[cfg(target_os = "android")]
527 Android(&'a mut android::BluetoothSocket),
528 #[cfg(target_os = "linux")]
530 Bluez(&'a mut linux::BluetoothRfcommSocket),
531}