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")]
15use android::Java;
16#[cfg(target_os = "android")]
17use winit::platform::android::activity::AndroidApp;
18
19#[cfg(target_os = "linux")]
20mod linux;
21
22mod bluetooth_uuid;
23pub use bluetooth_uuid::BluetoothUuid;
24
25#[derive(Debug, serde::Deserialize, serde::Serialize)]
27pub enum BluetoothCommand {
28 DetectAdapters,
30 QueryNumAdapters,
32}
33
34pub enum MessageToBluetoothHost {
36 DisplayPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
38 ConfirmPasskey(u32, tokio::sync::mpsc::Sender<ResponseToPasskey>),
40 CancelDisplayPasskey,
42}
43
44#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
45pub enum MessageFromBluetoothHost {
47 PasskeyMessage(ResponseToPasskey),
49}
50
51#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
52pub enum ResponseToPasskey {
54 Yes,
56 No,
58 Cancel,
60 Waiting,
62}
63
64pub enum BluetoothResponse {
66 Adapters(usize),
68}
69
70#[cfg(target_os = "android")]
72pub struct BluetoothSocket<'a>(&'a mut android::BluetoothSocket);
73
74#[derive(Clone)]
76pub struct BluetoothRfcommProfileSettings {
77 pub uuid: String,
79 pub name: Option<String>,
81 pub service_uuid: Option<String>,
83 pub channel: Option<u16>,
85 pub psm: Option<u16>,
87 pub authenticate: Option<bool>,
89 pub authorize: Option<bool>,
91 pub auto_connect: Option<bool>,
93 pub sdp_record: Option<String>,
95 pub sdp_version: Option<u16>,
97 pub sdp_features: Option<u16>,
99}
100
101#[enum_dispatch::enum_dispatch]
103pub trait BluetoothDiscoveryTrait {}
104
105#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
107pub enum BluetoothDiscovery<'a> {
108 #[cfg(target_os = "android")]
110 Android(android::BluetoothDiscovery<'a>),
111 #[cfg(target_os = "linux")]
113 Bluez(linux::BluetoothDiscovery<'a>),
114}
115
116#[enum_dispatch::enum_dispatch]
118pub trait BluetoothAdapterTrait {
119 async fn register_rfcomm_profile(
121 &self,
122 settings: BluetoothRfcommProfileSettings,
123 ) -> Result<BluetoothRfcommProfile, String>;
124 fn get_paired_devices(&self) -> Option<Vec<BluetoothDevice>>;
126 fn start_discovery(&self) -> BluetoothDiscovery;
128 async fn addresses(&self) -> Vec<[u8;6]>;
130}
131
132pub enum PairingStatus {
134 NotPaired,
136 Pairing,
138 Paired,
140 Unknown,
142}
143
144#[enum_dispatch::enum_dispatch]
146pub trait BluetoothDeviceTrait {
147 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
149
150 fn get_name(&self) -> Result<String, std::io::Error>;
152
153 fn get_address(&mut self) -> Result<String, std::io::Error>;
155
156 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
158
159 fn get_rfcomm_socket(
161 &mut self,
162 uuid: BluetoothUuid,
163 is_secure: bool,
164 ) -> Result<BluetoothRfcommSocket, String>;
165}
166
167#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
169pub enum BluetoothDevice {
170 #[cfg(target_os = "android")]
172 Android(android::BluetoothDevice),
173 #[cfg(target_os = "linux")]
175 Bluez(bluer::Device),
176}
177
178#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
180pub enum BluetoothAdapter {
181 #[cfg(target_os = "android")]
183 Android(android::Bluetooth),
184 #[cfg(target_os = "linux")]
186 Bluez(linux::BluetoothHandler),
187}
188
189pub struct BluetoothAdapterBuilder {
191 #[cfg(target_os = "android")]
193 app: Option<AndroidApp>,
194 s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
196}
197
198impl Default for BluetoothAdapterBuilder {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl BluetoothAdapterBuilder {
205 pub fn new() -> Self {
207 Self {
208 #[cfg(target_os = "android")]
209 app: None,
210 s: None,
211 }
212 }
213
214 #[cfg(target_os = "android")]
216 pub fn with_android_app(&mut self, app: AndroidApp) {
217 self.app = Some(app);
218 }
219
220 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
222 self.s = Some(s);
223 }
224
225 pub async fn build(self) -> Result<BluetoothAdapter, String> {
227 #[cfg(target_os = "android")]
228 {
229 let java = android::Java::make(self.app.unwrap());
230 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
231 Arc::new(Mutex::new(java)),
232 )));
233 }
234 #[cfg(target_os = "linux")]
235 {
236 return Ok(BluetoothAdapter::Bluez(
237 linux::BluetoothHandler::new(self.s.unwrap()).await?,
238 ));
239 }
240 Err("No builders available".to_string())
241 }
242}
243
244#[cfg(target_os = "android")]
245impl<'a> BluetoothSocket<'a> {
246 pub fn connect(&mut self) -> Result<(), std::io::Error> {
252 self.0.connect()
253 }
254}
255
256pub enum BluetoothStream {
258 #[cfg(target_os = "linux")]
260 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
261 #[cfg(target_os = "android")]
263 Android(std::pin::Pin<Box<android::RfcommStream>>),
264}
265
266impl tokio::io::AsyncRead for BluetoothStream {
267 fn poll_read(
268 self: std::pin::Pin<&mut Self>,
269 cx: &mut std::task::Context<'_>,
270 buf: &mut tokio::io::ReadBuf<'_>,
271 ) -> std::task::Poll<std::io::Result<()>> {
272 match self.get_mut() {
273 #[cfg(target_os = "linux")]
274 BluetoothStream::Bluez(s) => s.as_mut().poll_read(cx, buf),
275 #[cfg(target_os = "android")]
276 BluetoothStream::Android(s) => s.as_mut().poll_read(cx, buf),
277 }
278 }
279}
280
281impl tokio::io::AsyncWrite for BluetoothStream {
282 fn poll_write(
283 self: std::pin::Pin<&mut Self>,
284 cx: &mut std::task::Context<'_>,
285 buf: &[u8],
286 ) -> std::task::Poll<Result<usize, std::io::Error>> {
287 match self.get_mut() {
288 #[cfg(target_os = "linux")]
289 BluetoothStream::Bluez(s) => s.as_mut().poll_write(cx, buf),
290 #[cfg(target_os = "android")]
291 BluetoothStream::Android(s) => s.as_mut().poll_write(cx, buf),
292 }
293 }
294
295 fn poll_flush(
296 self: std::pin::Pin<&mut Self>,
297 cx: &mut std::task::Context<'_>,
298 ) -> std::task::Poll<Result<(), std::io::Error>> {
299 match self.get_mut() {
300 #[cfg(target_os = "linux")]
301 BluetoothStream::Bluez(s) => s.as_mut().poll_flush(cx),
302 #[cfg(target_os = "android")]
303 BluetoothStream::Android(s) => s.as_mut().poll_flush(cx),
304 }
305 }
306
307 fn poll_shutdown(
308 self: std::pin::Pin<&mut Self>,
309 cx: &mut std::task::Context<'_>,
310 ) -> std::task::Poll<Result<(), std::io::Error>> {
311 match self.get_mut() {
312 #[cfg(target_os = "linux")]
313 BluetoothStream::Bluez(s) => s.as_mut().poll_shutdown(cx),
314 #[cfg(target_os = "android")]
315 BluetoothStream::Android(s) => s.as_mut().poll_shutdown(cx),
316 }
317 }
318}
319
320#[enum_dispatch::enum_dispatch]
322pub trait BluetoothRfcommConnectableTrait {
323 async fn accept(self) -> Result<BluetoothStream, String>;
325}
326
327#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
329pub enum BluetoothRfcommConnectable {
330 #[cfg(target_os = "linux")]
332 Bluez(bluer::rfcomm::ConnectRequest),
333}
334
335#[enum_dispatch::enum_dispatch]
337pub trait BluetoothRfcommProfileTrait {
338 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectable, String>;
340}
341
342#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileTrait)]
344pub enum BluetoothRfcommProfile {
345 #[cfg(target_os = "android")]
347 Android(android::BluetoothRfcommProfile),
348 #[cfg(target_os = "linux")]
350 Bluez(bluer::rfcomm::ProfileHandle),
351}
352
353#[enum_dispatch::enum_dispatch]
355pub trait BluetoothRfcommSocketTrait {}
356
357#[enum_dispatch::enum_dispatch(BluetoothRfcommSocketTrait)]
359pub enum BluetoothRfcommSocket<'a> {
360 #[cfg(target_os = "android")]
362 Android(&'a mut android::BluetoothSocket),
363 #[cfg(target_os = "linux")]
365 Bluez(&'a mut linux::BluetoothRfcommSocket),
366}