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(&self) -> crate::BluetoothDiscovery {
131        BluetoothDiscovery::new().into()
132    }
133
134    fn get_paired_devices(&self) -> Option<Vec<crate::BluetoothDevice>> {
135        let list = Vec::new();
136        for adapter in &self.adapters {
137            todo!();
138        }
139        Some(list)
140    }
141
142    async fn addresses(&self) -> Vec<[u8;6]> {
143        let mut a = Vec::new();
144        for adapter in &self.adapters {
145            if let Ok(adr) = adapter.address().await {
146                a.push(adr.0);
147            }
148        }
149        a
150    }
151
152    async fn set_discoverable(&self, d: bool) -> Result<(), ()> {
153        for adapter in &self.adapters {
154            adapter.set_discoverable(d).await.map_err(|_|())?;
155        }
156        Ok(())
157    }
158}
159
160impl BluetoothHandler {
161    /// Retrieve the bluetooth addresses for all bluetooth adapters present
162    pub async fn addresses(&self) -> Vec<bluer::Address> {
163        let mut addrs = Vec::new();
164        for a in &self.adapters {
165            if let Ok(addr) = a.address().await {
166                addrs.push(addr);
167            }
168        }
169        addrs
170    }
171
172    /// Construct a new self
173    pub async fn new(
174        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
175    ) -> Result<Self, String> {
176        let session = bluer::Session::new().await.map_err(|e| e.to_string())?;
177
178        let adapter_names = session.adapter_names().await.map_err(|e| e.to_string())?;
179        let adapters: Vec<bluer::Adapter> = adapter_names
180            .iter()
181            .filter_map(|n| session.adapter(n).ok())
182            .collect();
183
184        let blue_agent = Self::build_agent(s);
185        let blue_agent_handle = session.register_agent(blue_agent).await;
186        println!("Registered a bluetooth agent {}", blue_agent_handle.is_ok());
187        Ok(Self {
188            session,
189            adapters,
190            _blue_agent_handle: blue_agent_handle.map_err(|e| e.to_string())?,
191        })
192    }
193
194    /// Enable all bluetooth adapters
195    async fn enable(&mut self) {
196        for adapter in &self.adapters {
197            adapter.set_powered(true).await.unwrap();
198            adapter.set_pairable(true).await.unwrap();
199        }
200    }
201
202    /// Disable all bluetooth adapters
203    async fn disable(&mut self) {
204        self.set_discoverable(false).await;
205        for adapter in &self.adapters {
206            adapter.set_powered(false).await.unwrap();
207            adapter.set_pairable(false).await.unwrap();
208        }
209    }
210
211    /// Enable or disable the discoverable of all bluetooth adapters
212    pub async fn set_discoverable(&mut self, d: bool) {
213        for adapter in &self.adapters {
214            adapter.set_discoverable(d).await.unwrap();
215        }
216    }
217
218    /// Register a profile with the bluetooth session
219    pub async fn register_rfcomm_profile(
220        &mut self,
221        profile: bluer::rfcomm::Profile,
222    ) -> Result<bluer::rfcomm::ProfileHandle, bluer::Error> {
223        self.session.register_profile(profile).await
224    }
225
226    /// Build a bluetooth agent for the handler
227    fn build_agent(
228        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
229    ) -> bluer::agent::Agent {
230        let mut blue_agent = bluer::agent::Agent::default();
231        blue_agent.request_default = true;
232        blue_agent.request_pin_code = None;
233        blue_agent.request_passkey = None;
234        let s2 = s.clone();
235        blue_agent.display_passkey = Some(Box::new(move |mut a| {
236            println!("Running process for display_passkey: {:?}", a);
237            let s3 = s2.clone();
238            async move {
239                let mut chan = tokio::sync::mpsc::channel(5);
240                let _ = s3.send(super::MessageToBluetoothHost::DisplayPasskey(a.passkey, chan.0)).await;
241                loop {
242                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
243                    tokio::select! {
244                        asdf = f => {
245                            match asdf {
246                                Ok(Some(m)) => {
247                                    match m {
248                                        super::ResponseToPasskey::Yes => {
249                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
250                                            return Ok(());
251                                        }
252                                        super::ResponseToPasskey::No => {
253                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
254                                            return Err(bluer::agent::ReqError::Rejected);
255                                        }
256                                        super::ResponseToPasskey::Cancel => {
257                                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
258                                            return Err(bluer::agent::ReqError::Canceled);
259                                        }
260                                        super::ResponseToPasskey::Waiting => {}
261                                    }
262                                }
263                                Ok(None) => {}
264                                _ => {
265                                    let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
266                                    return Err(bluer::agent::ReqError::Canceled);
267                                }
268                            }
269                        }
270                        _ = &mut a.cancel => {
271                            let _ = s3.send(super::MessageToBluetoothHost::CancelDisplayPasskey).await;
272                            break Err(bluer::agent::ReqError::Canceled);
273                        }
274                    }
275                }
276            }
277            .boxed()
278        }));
279        blue_agent.display_pin_code = Some(Box::new(|a| {
280            async move {
281                println!("Need to display pin code {:?}", a);
282                a.cancel.await.unwrap();
283                Ok(())
284            }
285            .boxed()
286        }));
287        let s2 = s.clone();
288        blue_agent.request_confirmation = Some(Box::new(move |a| {
289            println!("Need to confirm {:?}", a);
290            let s3 = s2.clone();
291            async move {
292                let mut chan = tokio::sync::mpsc::channel(5);
293                let _ = s3
294                    .send(super::MessageToBluetoothHost::ConfirmPasskey(
295                        a.passkey, chan.0,
296                    ))
297                    .await;
298                loop {
299                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
300                    let asdf = f.await;
301                    match asdf {
302                        Ok(Some(m)) => match m {
303                            super::ResponseToPasskey::Yes => {
304                                let _ = s3
305                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
306                                    .await;
307                                return Ok(());
308                            }
309                            super::ResponseToPasskey::No => {
310                                let _ = s3
311                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
312                                    .await;
313                                return Err(bluer::agent::ReqError::Rejected);
314                            }
315                            super::ResponseToPasskey::Cancel => {
316                                let _ = s3
317                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
318                                    .await;
319                                return Err(bluer::agent::ReqError::Canceled);
320                            }
321                            super::ResponseToPasskey::Waiting => {}
322                        },
323                        Ok(None) => {}
324                        _ => {
325                            let _ = s3
326                                .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
327                                .await;
328                            return Err(bluer::agent::ReqError::Canceled);
329                        }
330                    }
331                }
332            }
333            .boxed()
334        }));
335        blue_agent.request_authorization = Some(Box::new(|a| {
336            async move {
337                println!("Need to authorize {:?}", a);
338                Ok(())
339            }
340            .boxed()
341        }));
342        blue_agent.authorize_service = Some(Box::new(|a| {
343            async move {
344                println!("Need to authorize service {:?}", a);
345                Ok(())
346            }
347            .boxed()
348        }));
349        blue_agent
350    }
351
352    /// Issues the specified bluetooth command, with an optional response for the command
353    pub async fn issue_command(
354        &mut self,
355        cmd: super::BluetoothCommand,
356    ) -> Option<super::BluetoothResponse> {
357        match cmd {
358            super::BluetoothCommand::QueryNumAdapters => {
359                Some(super::BluetoothResponse::Adapters(0))
360            }
361            _ => None,
362        }
363    }
364
365    /// run a scan on all the bluetooth adapters
366    pub async fn scan<'a>(
367        &'a mut self,
368        bluetooth_devices: &mut HashMap<
369            bluer::Address,
370            (&'a bluer::Adapter, Option<bluer::Device>),
371        >,
372    ) {
373        let mut adapter_scanner = Vec::new();
374        for a in &self.adapters {
375            let da = a.discover_devices_with_changes().await.unwrap();
376            adapter_scanner.push((a, da));
377        }
378
379        for (adapt, da) in &mut adapter_scanner {
380            if let Some(e) = da.next().await {
381                match e {
382                    AdapterEvent::DeviceAdded(addr) => {
383                        println!("Device added {:?}", addr);
384                        bluetooth_devices.insert(addr, (adapt, None));
385                    }
386                    AdapterEvent::DeviceRemoved(addr) => {
387                        println!("Device removed {:?}", addr);
388                        bluetooth_devices.remove_entry(&addr);
389                    }
390                    AdapterEvent::PropertyChanged(prop) => {
391                        println!("Property changed {:?}", prop);
392                    }
393                }
394            }
395        }
396        for (addr, (adapter, dev)) in bluetooth_devices {
397            if dev.is_none() {
398                if let Ok(d) = adapter.device(*addr) {
399                    if let Ok(ps) = d.all_properties().await {
400                        for p in ps {}
401                    }
402                    *dev = Some(d);
403                }
404            }
405        }
406    }
407}