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
74pub struct BluetoothRfcommProfileSettings {
76 pub uuid: String,
78 pub name: Option<String>,
80 pub service_uuid: Option<String>,
82 pub channel: Option<u16>,
84 pub psm: Option<u16>,
86 pub authenticate: Option<bool>,
88 pub authorize: Option<bool>,
90 pub auto_connect: Option<bool>,
92 pub sdp_record: Option<String>,
94 pub sdp_version: Option<u16>,
96 pub sdp_features: Option<u16>,
98}
99
100#[enum_dispatch::enum_dispatch]
102pub trait BluetoothDiscoveryTrait {}
103
104#[enum_dispatch::enum_dispatch(BluetoothDiscoveryTrait)]
106pub enum BluetoothDiscovery<'a> {
107 #[cfg(target_os = "android")]
109 Android(android::BluetoothDiscovery<'a>),
110 #[cfg(target_os = "linux")]
112 Bluez(linux::BluetoothDiscovery<'a>),
113}
114
115#[enum_dispatch::enum_dispatch]
117pub trait BluetoothAdapterTrait {
118 async fn register_rfcomm_profile(
120 &self,
121 settings: BluetoothRfcommProfileSettings,
122 ) -> Result<BluetoothRfcommProfile, String>;
123 fn get_paired_devices(&mut self) -> Option<Vec<BluetoothDevice>>;
125 fn start_discovery(&mut self) -> BluetoothDiscovery;
127}
128
129pub enum PairingStatus {
131 NotPaired,
133 Pairing,
135 Paired,
137 Unknown,
139}
140
141#[enum_dispatch::enum_dispatch]
143pub trait BluetoothDeviceTrait {
144 fn get_uuids(&mut self) -> Result<Vec<BluetoothUuid>, std::io::Error>;
146
147 fn get_name(&self) -> Result<String, std::io::Error>;
149
150 fn get_address(&mut self) -> Result<String, std::io::Error>;
152
153 fn get_pair_state(&self) -> Result<PairingStatus, std::io::Error>;
155
156 fn get_rfcomm_socket(
158 &mut self,
159 uuid: BluetoothUuid,
160 is_secure: bool,
161 ) -> Result<BluetoothRfcommSocket, String>;
162}
163
164#[enum_dispatch::enum_dispatch(BluetoothDeviceTrait)]
166pub enum BluetoothDevice {
167 #[cfg(target_os = "android")]
169 Android(android::BluetoothDevice),
170 #[cfg(target_os = "linux")]
172 Bluez(bluer::Device),
173}
174
175#[enum_dispatch::enum_dispatch(BluetoothAdapterTrait)]
177pub enum BluetoothAdapter {
178 #[cfg(target_os = "android")]
180 Android(android::Bluetooth),
181 #[cfg(target_os = "linux")]
183 Bluez(linux::BluetoothHandler),
184}
185
186pub struct BluetoothAdapterBuilder {
188 #[cfg(target_os = "android")]
190 app: Option<AndroidApp>,
191 s: Option<tokio::sync::mpsc::Sender<MessageToBluetoothHost>>,
193}
194
195impl Default for BluetoothAdapterBuilder {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl BluetoothAdapterBuilder {
202 pub fn new() -> Self {
204 Self {
205 #[cfg(target_os = "android")]
206 app: None,
207 s: None,
208 }
209 }
210
211 #[cfg(target_os = "android")]
213 pub fn with_android_app(&mut self, app: AndroidApp) {
214 self.app = Some(app);
215 }
216
217 pub fn with_sender(&mut self, s: tokio::sync::mpsc::Sender<MessageToBluetoothHost>) {
219 self.s = Some(s);
220 }
221
222 pub async fn build(self) -> Result<BluetoothAdapter, String> {
224 #[cfg(target_os = "android")]
225 {
226 let java = android::Java::make(self.app.unwrap());
227 return Ok(BluetoothAdapter::Android(android::Bluetooth::new(
228 Arc::new(Mutex::new(java)),
229 )));
230 }
231 #[cfg(target_os = "linux")]
232 {
233 return Ok(BluetoothAdapter::Bluez(
234 linux::BluetoothHandler::new(self.s.unwrap()).await?,
235 ));
236 }
237 Err("No builders available".to_string())
238 }
239}
240
241#[cfg(target_os = "android")]
242impl<'a> BluetoothSocket<'a> {
243 pub fn connect(&mut self) -> Result<(), std::io::Error> {
249 self.0.connect()
250 }
251}
252
253pub enum BluetoothStream {
255 #[cfg(target_os = "linux")]
257 Bluez(std::pin::Pin<Box<bluer::rfcomm::Stream>>),
258 #[cfg(target_os = "android")]
260 Android(std::pin::Pin<Box<android::RfcommStream>>),
261}
262
263impl tokio::io::AsyncRead for BluetoothStream {
264 fn poll_read(
265 self: std::pin::Pin<&mut Self>,
266 cx: &mut std::task::Context<'_>,
267 buf: &mut tokio::io::ReadBuf<'_>,
268 ) -> std::task::Poll<std::io::Result<()>> {
269 match self.get_mut() {
270 #[cfg(target_os = "linux")]
271 BluetoothStream::Bluez(s) => s.as_mut().poll_read(cx, buf),
272 #[cfg(target_os = "android")]
273 BluetoothStream::Android(s) => s.as_mut().poll_read(cx, buf),
274 }
275 }
276}
277
278impl tokio::io::AsyncWrite for BluetoothStream {
279 fn poll_write(
280 self: std::pin::Pin<&mut Self>,
281 cx: &mut std::task::Context<'_>,
282 buf: &[u8],
283 ) -> std::task::Poll<Result<usize, std::io::Error>> {
284 match self.get_mut() {
285 #[cfg(target_os = "linux")]
286 BluetoothStream::Bluez(s) => s.as_mut().poll_write(cx, buf),
287 #[cfg(target_os = "android")]
288 BluetoothStream::Android(s) => s.as_mut().poll_write(cx, buf),
289 }
290 }
291
292 fn poll_flush(
293 self: std::pin::Pin<&mut Self>,
294 cx: &mut std::task::Context<'_>,
295 ) -> std::task::Poll<Result<(), std::io::Error>> {
296 match self.get_mut() {
297 #[cfg(target_os = "linux")]
298 BluetoothStream::Bluez(s) => s.as_mut().poll_flush(cx),
299 #[cfg(target_os = "android")]
300 BluetoothStream::Android(s) => s.as_mut().poll_flush(cx),
301 }
302 }
303
304 fn poll_shutdown(
305 self: std::pin::Pin<&mut Self>,
306 cx: &mut std::task::Context<'_>,
307 ) -> std::task::Poll<Result<(), std::io::Error>> {
308 match self.get_mut() {
309 #[cfg(target_os = "linux")]
310 BluetoothStream::Bluez(s) => s.as_mut().poll_shutdown(cx),
311 #[cfg(target_os = "android")]
312 BluetoothStream::Android(s) => s.as_mut().poll_shutdown(cx),
313 }
314 }
315}
316
317#[enum_dispatch::enum_dispatch]
319pub trait BluetoothRfcommConnectableTrait {
320 async fn accept(self) -> Result<BluetoothStream, String>;
322}
323
324#[enum_dispatch::enum_dispatch(BluetoothRfcommConnectableTrait)]
326pub enum BluetoothRfcommConnectable {
327 #[cfg(target_os = "linux")]
329 Bluez(bluer::rfcomm::ConnectRequest),
330}
331
332#[enum_dispatch::enum_dispatch]
334pub trait BluetoothRfcommProfileTrait {
335 async fn connectable(&mut self) -> Result<BluetoothRfcommConnectable, String>;
337}
338
339#[enum_dispatch::enum_dispatch(BluetoothRfcommProfileTrait)]
341pub enum BluetoothRfcommProfile {
342 #[cfg(target_os = "android")]
344 Android(android::BluetoothRfcommProfile),
345 #[cfg(target_os = "linux")]
347 Bluez(bluer::rfcomm::ProfileHandle),
348}
349
350#[enum_dispatch::enum_dispatch]
352pub trait BluetoothRfcommSocketTrait {}
353
354#[enum_dispatch::enum_dispatch(BluetoothRfcommSocketTrait)]
356pub enum BluetoothRfcommSocket<'a> {
357 #[cfg(target_os = "android")]
359 Android(&'a mut android::BluetoothSocket),
360 #[cfg(target_os = "linux")]
362 Bluez(&'a mut linux::BluetoothRfcommSocket),
363}