bluetooth_rust/
linux.rs

1//! Linux specific bluetooth code
2
3use std::collections::{HashMap, HashSet};
4use std::marker::PhantomData;
5
6use bluer::{AdapterEvent, DeviceProperty};
7use futures::FutureExt;
8use futures::StreamExt;
9
10impl super::BluetoothRfcommConnectableTrait for bluer::rfcomm::ConnectRequest {
11    async fn accept(self) -> Result<crate::BluetoothStream, String> {
12        bluer::rfcomm::ConnectRequest::accept(self)
13            .map(|a| crate::BluetoothStream::Bluez(Box::pin(a)))
14            .map_err(|e| e.to_string())
15    }
16}
17
18impl super::BluetoothRfcommProfileTrait for bluer::rfcomm::ProfileHandle {
19    async fn connectable(&mut self) -> Result<crate::BluetoothRfcommConnectable,String> {
20        self.next().await.map(|a|a.into()).ok_or("Failed to get bluetooth connection".to_string())
21    }
22}
23
24impl super::BluetoothDeviceTrait for bluer::Device {
25    #[doc = " Get all known uuids for this device"]
26    fn get_uuids(&mut self) -> Result<Vec<crate::BluetoothUuid>, std::io::Error> {
27        todo!()
28    }
29
30    #[doc = " Retrieve the device name"]
31    fn get_name(&self) -> Result<String, std::io::Error> {
32        todo!()
33    }
34
35    #[doc = " Retrieve the device address"]
36    fn get_address(&mut self) -> Result<String, std::io::Error> {
37        todo!()
38    }
39
40    #[doc = " Retrieve the device pairing (bonding) status"]
41    fn get_pair_state(&self) -> Result<crate::PairingStatus, std::io::Error> {
42        todo!()
43    }
44
45    #[doc = " Attempt to get an rfcomm socket for the given uuid and seciruty setting"]
46    fn get_rfcomm_socket(
47        &mut self,
48        uuid: crate::BluetoothUuid,
49        is_secure: bool,
50    ) -> Result<crate::BluetoothRfcommSocket, String> {
51        todo!()
52    }
53}
54
55/// An rfcomm socket with a bluetooth peer
56pub struct BluetoothRfcommSocket {
57    /// The rfcomm socket data, TODO
58    inner: u32,
59}
60
61/// A struct for managing discovery of bluetooth devices
62pub struct BluetoothDiscovery<'a> {
63    /// phantom so the struct acts like it has a lifetime
64    _dummy: PhantomData<&'a ()>,
65}
66
67impl<'a> BluetoothDiscovery<'a> {
68    /// construct a new self
69    fn new() -> Self {
70        Self {
71            _dummy: PhantomData,
72        }
73    }
74}
75
76impl<'a> super::BluetoothDiscoveryTrait for BluetoothDiscovery<'a> {}
77
78impl<'a> Drop for BluetoothDiscovery<'a> {
79    fn drop(&mut self) {}
80}
81
82/// The general bluetooth handler for the library. There should be only one per application on linux.
83pub struct BluetoothHandler {
84    /// The current bluetooth session
85    session: bluer::Session,
86    /// The list of bluetooth adapters for the system
87    adapters: Vec<bluer::Adapter>,
88    /// The agent for the handler
89    _blue_agent_handle: bluer::agent::AgentHandle,
90}
91
92impl TryFrom<super::BluetoothRfcommProfileSettings> for bluer::rfcomm::Profile {
93    type Error = String;
94    fn try_from(value: super::BluetoothRfcommProfileSettings) -> Result<Self, Self::Error> {
95        let service = if let Some(v) = value.service_uuid {
96            Some(bluer::Uuid::parse_str(&v).map_err(|e| e.to_string())?)
97        } else {
98            None
99        };
100        Ok(Self {
101            uuid: bluer::Uuid::parse_str(&value.uuid).map_err(|e| e.to_string())?,
102            name: value.name,
103            service,
104            role: None,
105            channel: value.channel,
106            psm: value.psm,
107            require_authentication: value.authenticate,
108            require_authorization: value.authorize,
109            auto_connect: value.auto_connect,
110            service_record: value.sdp_record,
111            version: value.sdp_version,
112            features: value.sdp_features,
113            ..Default::default()
114        })
115    }
116}
117
118impl super::BluetoothAdapterTrait for BluetoothHandler {
119    async fn register_rfcomm_profile(
120        &self,
121        settings: super::BluetoothRfcommProfileSettings,
122    ) -> Result<crate::BluetoothRfcommProfile, String> {
123        self.session
124            .register_profile(settings.try_into()?)
125            .await
126            .map(|a| super::BluetoothRfcommProfile::Bluez(a.into()))
127            .map_err(|e| e.to_string())
128    }
129
130    fn start_discovery(&mut self) -> crate::BluetoothDiscovery {
131        BluetoothDiscovery::new().into()
132    }
133
134    fn get_paired_devices(&mut self) -> Option<Vec<crate::BluetoothDevice>> {
135        let list = Vec::new();
136        for adapter in &mut self.adapters {
137            todo!();
138        }
139        Some(list)
140    }
141}
142
143impl BluetoothHandler {
144    /// Retrieve the bluetooth addresses for all bluetooth adapters present
145    pub async fn addresses(&self) -> Vec<bluer::Address> {
146        let mut addrs = Vec::new();
147        for a in &self.adapters {
148            if let Ok(addr) = a.address().await {
149                addrs.push(addr);
150            }
151        }
152        addrs
153    }
154
155    /// Construct a new self
156    pub async fn new(
157        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
158    ) -> Result<Self, String> {
159        let session = bluer::Session::new().await.map_err(|e| e.to_string())?;
160
161        let adapter_names = session.adapter_names().await.map_err(|e| e.to_string())?;
162        let adapters: Vec<bluer::Adapter> = adapter_names
163            .iter()
164            .filter_map(|n| session.adapter(n).ok())
165            .collect();
166
167        let blue_agent = Self::build_agent(s);
168        let blue_agent_handle = session.register_agent(blue_agent).await;
169        println!("Registered a bluetooth agent {}", blue_agent_handle.is_ok());
170        Ok(Self {
171            session,
172            adapters,
173            _blue_agent_handle: blue_agent_handle.map_err(|e| e.to_string())?,
174        })
175    }
176
177    /// Enable all bluetooth adapters
178    async fn enable(&mut self) {
179        for adapter in &self.adapters {
180            adapter.set_powered(true).await.unwrap();
181            adapter.set_pairable(true).await.unwrap();
182        }
183    }
184
185    /// Disable all bluetooth adapters
186    async fn disable(&mut self) {
187        self.set_discoverable(false).await;
188        for adapter in &self.adapters {
189            adapter.set_powered(false).await.unwrap();
190            adapter.set_pairable(false).await.unwrap();
191        }
192    }
193
194    /// Enable or disable the discoverable of all bluetooth adapters
195    pub async fn set_discoverable(&mut self, d: bool) {
196        for adapter in &self.adapters {
197            adapter.set_discoverable(d).await.unwrap();
198        }
199    }
200
201    /// Register a profile with the bluetooth session
202    pub async fn register_rfcomm_profile(
203        &mut self,
204        profile: bluer::rfcomm::Profile,
205    ) -> Result<bluer::rfcomm::ProfileHandle, bluer::Error> {
206        self.session.register_profile(profile).await
207    }
208
209    /// Build a bluetooth agent for the handler
210    fn build_agent(
211        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
212    ) -> bluer::agent::Agent {
213        let mut blue_agent = bluer::agent::Agent::default();
214        blue_agent.request_default = true;
215        blue_agent.request_pin_code = None;
216        blue_agent.request_passkey = None;
217        let s2 = s.clone();
218        blue_agent.display_passkey = Some(Box::new(move |mut a| {
219            println!("Running process for display_passkey: {:?}", a);
220            let s3 = s2.clone();
221            async move {
222                let mut chan = tokio::sync::mpsc::channel(5);
223                let _ = s3.send(super::MessageToBluetoothHost::DisplayPasskey(a.passkey, chan.0)).await;
224                loop {
225                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
226                    tokio::select! {
227                        asdf = f => {
228                            match asdf {
229                                Ok(Some(m)) => {
230                                    match m {
231                                        super::ResponseToPasskey::Yes => {
232                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
233                                            return Ok(());
234                                        }
235                                        super::ResponseToPasskey::No => {
236                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
237                                            return Err(bluer::agent::ReqError::Rejected);
238                                        }
239                                        super::ResponseToPasskey::Cancel => {
240                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
241                                            return Err(bluer::agent::ReqError::Canceled);
242                                        }
243                                        super::ResponseToPasskey::Waiting => {}
244                                    }
245                                }
246                                Ok(None) => {}
247                                _ => {
248                                    let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
249                                    return Err(bluer::agent::ReqError::Canceled);
250                                }
251                            }
252                        }
253                        _ = &mut a.cancel => {
254                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
255                            break Err(bluer::agent::ReqError::Canceled);
256                        }
257                    }
258                }
259            }
260            .boxed()
261        }));
262        blue_agent.display_pin_code = Some(Box::new(|a| {
263            async move {
264                println!("Need to display pin code {:?}", a);
265                a.cancel.await.unwrap();
266                Ok(())
267            }
268            .boxed()
269        }));
270        let s2 = s.clone();
271        blue_agent.request_confirmation = Some(Box::new(move |a| {
272            println!("Need to confirm {:?}", a);
273            let s3 = s2.clone();
274            async move {
275                let mut chan = tokio::sync::mpsc::channel(5);
276                let _ = s3
277                    .send(super::MessageToBluetoothHost::ConfirmPasskey(
278                        a.passkey, chan.0,
279                    ))
280                    .await;
281                loop {
282                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
283                    let asdf = f.await;
284                    match asdf {
285                        Ok(Some(m)) => match m {
286                            super::ResponseToPasskey::Yes => {
287                                let _ = s3
288                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
289                                    .await;
290                                return Ok(());
291                            }
292                            super::ResponseToPasskey::No => {
293                                let _ = s3
294                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
295                                    .await;
296                                return Err(bluer::agent::ReqError::Rejected);
297                            }
298                            super::ResponseToPasskey::Cancel => {
299                                let _ = s3
300                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
301                                    .await;
302                                return Err(bluer::agent::ReqError::Canceled);
303                            }
304                            super::ResponseToPasskey::Waiting => {}
305                        },
306                        Ok(None) => {}
307                        _ => {
308                            let _ = s3
309                                .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
310                                .await;
311                            return Err(bluer::agent::ReqError::Canceled);
312                        }
313                    }
314                }
315            }
316            .boxed()
317        }));
318        blue_agent.request_authorization = Some(Box::new(|a| {
319            async move {
320                println!("Need to authorize {:?}", a);
321                Ok(())
322            }
323            .boxed()
324        }));
325        blue_agent.authorize_service = Some(Box::new(|a| {
326            async move {
327                println!("Need to authorize service {:?}", a);
328                Ok(())
329            }
330            .boxed()
331        }));
332        blue_agent
333    }
334
335    /// Issues the specified bluetooth command, with an optional response for the command
336    pub async fn issue_command(
337        &mut self,
338        cmd: super::BluetoothCommand,
339    ) -> Option<super::BluetoothResponse> {
340        match cmd {
341            super::BluetoothCommand::QueryNumAdapters => {
342                Some(super::BluetoothResponse::Adapters(0))
343            }
344            _ => None,
345        }
346    }
347
348    /// run a scan on all the bluetooth adapters
349    pub async fn scan<'a>(
350        &'a mut self,
351        bluetooth_devices: &mut HashMap<
352            bluer::Address,
353            (&'a bluer::Adapter, Option<bluer::Device>),
354        >,
355    ) {
356        let mut adapter_scanner = Vec::new();
357        for a in &self.adapters {
358            let da = a.discover_devices_with_changes().await.unwrap();
359            adapter_scanner.push((a, da));
360        }
361
362        for (adapt, da) in &mut adapter_scanner {
363            if let Some(e) = da.next().await {
364                match e {
365                    AdapterEvent::DeviceAdded(addr) => {
366                        println!("Device added {:?}", addr);
367                        bluetooth_devices.insert(addr, (adapt, None));
368                    }
369                    AdapterEvent::DeviceRemoved(addr) => {
370                        println!("Device removed {:?}", addr);
371                        bluetooth_devices.remove_entry(&addr);
372                    }
373                    AdapterEvent::PropertyChanged(prop) => {
374                        println!("Property changed {:?}", prop);
375                    }
376                }
377            }
378        }
379        for (addr, (adapter, dev)) in bluetooth_devices {
380            if dev.is_none() {
381                if let Ok(d) = adapter.device(*addr) {
382                    if let Ok(ps) = d.all_properties().await {
383                        for p in ps {}
384                    }
385                    *dev = Some(d);
386                }
387            }
388        }
389    }
390}