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 async fn set_discoverable(&self, d: bool) -> Result<(), ()>;
132}
133
134pub enum PairingStatus {
136 NotPaired,
138 Pairing,
140 Paired,
142 Unknown,
144}
145
146#[enum_dispatch::enum_dispatch]
148pub trait BluetoothDeviceTrait {
149 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
151
152 fn get_name(&self) -> Result<String, std::io::Error>;
154
155 fn get_address(&mut self) -> Result<String, std::io::Error>;
157
158 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
160
161 fn get_rfcomm_socket(
163 &mut self,
164 uuid: BluetoothUuid,
165 is_secure: bool,
166 ) -> Result<BluetoothRfcommSocket, String>;
167}
168
169#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
171pub enum BluetoothDevice {
172 #[cfg(target_os = "android")]
174 Android(android::BluetoothDevice),
175 #[cfg(target_os = "linux")]
177 Bluez(bluer::Device),
178}
179
180#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
182pub enum BluetoothAdapter {
183 #[cfg(target_os = "android")]
185 Android(android::Bluetooth),
186 #[cfg(target_os = "linux")]
188 Bluez(linux::BluetoothHandler),
189}
190
191pub struct BluetoothAdapterBuilder {
193 #[cfg(target_os = "android")]
195 app: Option<AndroidApp>,
196 s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
198}
199
200impl Default for BluetoothAdapterBuilder {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206impl BluetoothAdapterBuilder {
207 pub fn new() -> Self {
209 Self {
210 #[cfg(target_os = "android")]
211 app: None,
212 s: None,
213 }
214 }
215
216 #[cfg(target_os = "android")]
218 pub fn with_android_app(&mut self, app: AndroidApp) {
219 self.app = Some(app);
220 }
221
222 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
224 self.s = Some(s);
225 }
226
227 pub async fn build(self) -> Result<BluetoothAdapter, String> {
229 #[cfg(target_os = "android")]
230 {
231 let java = android::Java::make(self.app.unwrap());
232 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
233 Arc::new(Mutex::new(java)),
234 )));
235 }
236 #[cfg(target_os = "linux")]
237 {
238 return Ok(BluetoothAdapter::Bluez(
239 linux::BluetoothHandler::new(self.s.unwrap()).await?,
240 ));
241 }
242 Err("No builders available".to_string())
243 }
244}
245
246#[cfg(target_os = "android")]
247impl<'a> BluetoothSocket<'a> {
248 pub fn connect(&mut self) -> Result<(), std::io::Error> {
254 self.0.connect()
255 }
256}
257
258pub enum BluetoothStream {
260 #[cfg(target_os = "linux")]
262 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
263 #[cfg(target_os = "android")]
265 Android(std::pin::Pin<Box<android::RfcommStream>>),
266}
267
268impl tokio::io::AsyncRead for BluetoothStream {
269 fn poll_read(
270 self: std::pin::Pin<&mut Self>,
271 cx: &mut std::task::Context<'_>,
272 buf: &mut tokio::io::ReadBuf<'_>,
273 ) -> std::task::Poll<std::io::Result<()>> {
274 match self.get_mut() {
275 #[cfg(target_os = "linux")]
276 BluetoothStream::Bluez(s) => s.as_mut().poll_read(cx, buf),
277 #[cfg(target_os = "android")]
278 BluetoothStream::Android(s) => s.as_mut().poll_read(cx, buf),
279 }
280 }
281}
282
283impl tokio::io::AsyncWrite for BluetoothStream {
284 fn poll_write(
285 self: std::pin::Pin<&mut Self>,
286 cx: &mut std::task::Context<'_>,
287 buf: &[u8],
288 ) -> std::task::Poll<Result<usize, std::io::Error>> {
289 match self.get_mut() {
290 #[cfg(target_os = "linux")]
291 BluetoothStream::Bluez(s) => s.as_mut().poll_write(cx, buf),
292 #[cfg(target_os = "android")]
293 BluetoothStream::Android(s) => s.as_mut().poll_write(cx, buf),
294 }
295 }
296
297 fn poll_flush(
298 self: std::pin::Pin<&mut Self>,
299 cx: &mut std::task::Context<'_>,
300 ) -> std::task::Poll<Result<(), std::io::Error>> {
301 match self.get_mut() {
302 #[cfg(target_os = "linux")]
303 BluetoothStream::Bluez(s) => s.as_mut().poll_flush(cx),
304 #[cfg(target_os = "android")]
305 BluetoothStream::Android(s) => s.as_mut().poll_flush(cx),
306 }
307 }
308
309 fn poll_shutdown(
310 self: std::pin::Pin<&mut Self>,
311 cx: &mut std::task::Context<'_>,
312 ) -> std::task::Poll<Result<(), std::io::Error>> {
313 match self.get_mut() {
314 #[cfg(target_os = "linux")]
315 BluetoothStream::Bluez(s) => s.as_mut().poll_shutdown(cx),
316 #[cfg(target_os = "android")]
317 BluetoothStream::Android(s) => s.as_mut().poll_shutdown(cx),
318 }
319 }
320}
321
322#[enum_dispatch::enum_dispatch]
324pub trait BluetoothRfcommConnectableTrait {
325 async fn accept(self) -> Result<BluetoothStream, String>;
327}
328
329#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
331pub enum BluetoothRfcommConnectable {
332 #[cfg(target_os = "linux")]
334 Bluez(bluer::rfcomm::ConnectRequest),
335}
336
337#[enum_dispatch::enum_dispatch]
339pub trait BluetoothRfcommProfileTrait {
340 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectable, String>;
342}
343
344#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileTrait)]
346pub enum BluetoothRfcommProfile {
347 #[cfg(target_os = "android")]
349 Android(android::BluetoothRfcommProfile),
350 #[cfg(target_os = "linux")]
352 Bluez(bluer::rfcomm::ProfileHandle),
353}
354
355#[enum_dispatch::enum_dispatch]
357pub trait BluetoothRfcommSocketTrait {}
358
359#[enum_dispatch::enum_dispatch(BluetoothRfcommSocketTrait)]
361pub enum BluetoothRfcommSocket<'a> {
362 #[cfg(target_os = "android")]
364 Android(&'a mut android::BluetoothSocket),
365 #[cfg(target_os = "linux")]
367 Bluez(&'a mut linux::BluetoothRfcommSocket),
368}