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 fn build(self) -> Result<BluetoothAdapter, String> {
310 #[cfg(target_os = "android")]
311 {
312 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
313 self.app.unwrap(),
314 )));
315 }
316 Err("No synchronous builders available".to_string())
317 }
318
319 pub async fn async_build(self) -> Result<BluetoothAdapter, String> {
321 #[cfg(target_os = "android")]
322 {
323 return self.build();
324 }
325 #[cfg(target_os = "linux")]
326 {
327 return Ok(BluetoothAdapter::Bluez(
328 linux::BluetoothHandler::new(self.s.unwrap()).await?,
329 ));
330 }
331 Err("No async builders available".to_string())
332 }
333}
334
335pub enum BluetoothStream {
337 #[cfg(target_os = "linux")]
339 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
340 #[cfg(target_os = "android")]
342 Android(android::RfcommStream),
343}
344
345impl BluetoothStream {
346 pub fn supports_async_read(
348 self: std::pin::Pin<&mut Self>,
349 ) -> Option<&mut dyn tokio::io::AsyncRead> {
350 match self.get_mut() {
351 #[cfg(target_os = "linux")]
352 BluetoothStream::Bluez(pin) => Some(pin),
353 #[cfg(target_os = "android")]
354 BluetoothStream::Android(_pin) => None,
355 }
356 }
357
358 pub fn supports_async_write(
360 self: std::pin::Pin<&mut Self>,
361 ) -> Option<&mut dyn tokio::io::AsyncWrite> {
362 match self.get_mut() {
363 #[cfg(target_os = "linux")]
364 BluetoothStream::Bluez(pin) => Some(pin),
365 #[cfg(target_os = "android")]
366 BluetoothStream::Android(_pin) => None,
367 }
368 }
369
370 pub fn supports_sync_read(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Read> {
372 match self.get_mut() {
373 #[cfg(target_os = "linux")]
374 BluetoothStream::Bluez(pin) => None,
375 #[cfg(target_os = "android")]
376 BluetoothStream::Android(pin) => Some(pin),
377 }
378 }
379
380 pub fn supports_sync_write(self: std::pin::Pin<&mut Self>) -> Option<&mut dyn std::io::Write> {
382 match self.get_mut() {
383 #[cfg(target_os = "linux")]
384 BluetoothStream::Bluez(pin) => None,
385 #[cfg(target_os = "android")]
386 BluetoothStream::Android(pin) => Some(pin),
387 }
388 }
389}
390
391#[enum_dispatch::enum_dispatch]
393pub trait BluetoothRfcommConnectableAsyncTrait {
394 async fn accept(self) -> Result<BluetoothStream, String>;
396}
397
398#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
400pub enum BluetoothRfcommConnectableAsync {
401 #[cfg(target_os = "android")]
403 Android(android::BluetoothRfcommConnectable),
404 #[cfg(target_os = "linux")]
406 Bluez(bluer::rfcomm::ConnectRequest),
407}
408
409#[enum_dispatch::enum_dispatch]
411pub trait BluetoothRfcommConnectableSyncTrait {
412 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
414}
415
416#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableSyncTrait)]
418pub enum BluetoothRfcommConnectableSync {
419 #[cfg(target_os = "android")]
421 Android(android::BluetoothRfcommConnectable),
422}
423
424#[enum_dispatch::enum_dispatch]
426pub trait BluetoothL2capConnectableAsyncTrait {
427 async fn accept(self) -> Result<BluetoothStream, String>;
429}
430
431#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableTrait)]
433pub enum BluetoothL2capConnectableAsync {
434 #[cfg(target_os = "android")]
436 Android(android::BluetoothRfcommConnectable),
437 #[cfg(target_os = "linux")]
439 Bluez(bluer::rfcomm::ConnectRequest),
440}
441
442#[enum_dispatch::enum_dispatch]
444pub trait BluetoothL2capConnectableSyncTrait {
445 fn accept(self, timeout: std::time::Duration) -> Result<BluetoothStream, String>;
447}
448
449#[enum_dispatch::enum_dispatch(BluetoothL2capConnectableSyncTrait)]
451pub enum BluetoothL2capConnectableSync {
452 #[cfg(target_os = "android")]
454 Android(android::BluetoothRfcommConnectable),
455}
456
457#[enum_dispatch::enum_dispatch]
459pub trait BluetoothRfcommProfileAsyncTrait {
460 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String>;
462}
463
464#[enum_dispatch::enum_dispatch]
466pub trait BluetoothRfcommProfileSyncTrait {
467 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String>;
469}
470
471#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileAsyncTrait)]
473pub enum BluetoothRfcommProfileAsync {
474 #[cfg(target_os = "linux")]
476 Bluez(bluer::rfcomm::ProfileHandle),
477 Dummy(Dummy),
479}
480
481#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileSyncTrait)]
483pub enum BluetoothRfcommProfileSync {
484 #[cfg(target_os = "android")]
486 Android(android::BluetoothRfcommProfile),
487 Dummy(Dummy),
489}
490
491#[enum_dispatch::enum_dispatch(BluetoothL2capProfileAsyncTrait)]
493pub enum BluetoothL2capProfileAsync {
494 #[cfg(target_os = "linux")]
496 Bluez(bluer::rfcomm::ProfileHandle),
497 Dummy(Dummy),
499}
500
501#[enum_dispatch::enum_dispatch(BluetoothL2capProfileSyncTrait)]
503pub enum BluetoothL2capProfileSync {
504 #[cfg(target_os = "android")]
506 Android(android::BluetoothRfcommProfile),
507 Dummy(Dummy),
509}
510
511pub struct Dummy {}
513
514impl BluetoothRfcommProfileSyncTrait for Dummy {
515 fn connectable(&mut self) -> Result<BluetoothRfcommConnectableSync, String> {
516 unimplemented!()
517 }
518}
519
520impl BluetoothRfcommProfileAsyncTrait for Dummy {
521 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectableAsync, String> {
522 unimplemented!()
523 }
524}
525
526#[enum_dispatch::enum_dispatch]
528pub trait BluetoothSocketTrait {
529 fn is_connected(&self) -> Result<bool, std::io::Error>;
531 fn connect(&mut self) -> Result<(), std::io::Error>;
533}
534
535impl<'a> std::io::Read for BluetoothSocket<'a> {
536 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
537 match self {
538 #[cfg(target_os = "android")]
539 BluetoothSocket::Android(a) => a.read(buf),
540 #[cfg(target_os = "linux")]
541 BluetoothSocket::Bluez(b) => {
542 todo!()
543 }
544 }
545 }
546}
547
548impl<'a> std::io::Write for BluetoothSocket<'a> {
549 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
550 match self {
551 #[cfg(target_os = "android")]
552 BluetoothSocket::Android(a) => a.write(buf),
553 #[cfg(target_os = "linux")]
554 BluetoothSocket::Bluez(b) => {
555 todo!()
556 }
557 }
558 }
559
560 fn flush(&mut self) -> std::io::Result<()> {
561 match self {
562 #[cfg(target_os = "android")]
563 BluetoothSocket::Android(a) => a.flush(),
564 #[cfg(target_os = "linux")]
565 BluetoothSocket::Bluez(b) => {
566 todo!()
567 }
568 }
569 }
570}
571
572#[enum_dispatch::enum_dispatch(BluetoothSocketTrait)]
574pub enum BluetoothSocket<'a> {
575 #[cfg(target_os = "android")]
577 Android(&'a mut android::BluetoothSocket),
578 #[cfg(target_os = "linux")]
580 Bluez(&'a mut linux::BluetoothRfcommSocket),
581}