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