Skip to main content

bluetooth_rust/
linux.rs

1//! Linux specific bluetooth code
2
3use std::collections::HashMap;
4
5use bluer::AdapterEvent;
6use futures::FutureExt;
7use futures::StreamExt;
8
9// ────────────────────────────────────────────────────────────────────────────
10// BluetoothRfcommConnectableAsyncTrait for bluer::rfcomm::ConnectRequest
11// ────────────────────────────────────────────────────────────────────────────
12
13#[async_trait::async_trait]
14impl super::BluetoothRfcommConnectableAsyncTrait for bluer::rfcomm::ConnectRequest {
15    async fn accept(self) -> Result<(crate::BluetoothStream, [u8; 6], u8), String> {
16        let s = bluer::rfcomm::ConnectRequest::accept(self);
17        match s {
18            Ok(s) => {
19                let addr = s.peer_addr().map_err(|e| e.to_string())?;
20                Ok((
21                    crate::BluetoothStream::Bluez(Box::pin(s)),
22                    *addr.addr,
23                    addr.channel,
24                ))
25            }
26            Err(e) => Err(e.to_string()),
27        }
28    }
29}
30
31// ────────────────────────────────────────────────────────────────────────────
32// BluetoothRfcommProfileAsyncTrait for bluer::rfcomm::ProfileHandle
33// ────────────────────────────────────────────────────────────────────────────
34
35impl super::BluetoothRfcommProfileAsyncTrait for bluer::rfcomm::ProfileHandle {
36    async fn connectable(&mut self) -> Result<crate::BluetoothRfcommConnectableAsync, String> {
37        self.next()
38            .await
39            .map(|a| crate::BluetoothRfcommConnectableAsync::Bluez(a))
40            .ok_or_else(|| "Failed to get bluetooth connection".to_string())
41    }
42}
43
44// ────────────────────────────────────────────────────────────────────────────
45// Internal active-connection holder (RFCOMM or L2CAP stream)
46// ────────────────────────────────────────────────────────────────────────────
47
48/// Holds the active stream for either an RFCOMM or L2CAP connection.
49enum BluetoothConnection {
50    /// An active RFCOMM stream
51    Rfcomm(bluer::rfcomm::Stream),
52    /// An active L2CAP stream
53    L2cap(bluer::l2cap::Stream),
54}
55
56impl tokio::io::AsyncRead for BluetoothConnection {
57    fn poll_read(
58        self: std::pin::Pin<&mut Self>,
59        cx: &mut std::task::Context<'_>,
60        buf: &mut tokio::io::ReadBuf<'_>,
61    ) -> std::task::Poll<std::io::Result<()>> {
62        match self.get_mut() {
63            Self::Rfcomm(s) => {
64                tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
65            }
66            Self::L2cap(s) => {
67                tokio::io::AsyncRead::poll_read(std::pin::Pin::new(s), cx, buf)
68            }
69        }
70    }
71}
72
73impl tokio::io::AsyncWrite for BluetoothConnection {
74    fn poll_write(
75        self: std::pin::Pin<&mut Self>,
76        cx: &mut std::task::Context<'_>,
77        buf: &[u8],
78    ) -> std::task::Poll<std::io::Result<usize>> {
79        match self.get_mut() {
80            Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf),
81            Self::L2cap(s) => tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(s), cx, buf),
82        }
83    }
84
85    fn poll_flush(
86        self: std::pin::Pin<&mut Self>,
87        cx: &mut std::task::Context<'_>,
88    ) -> std::task::Poll<std::io::Result<()>> {
89        match self.get_mut() {
90            Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx),
91            Self::L2cap(s) => tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(s), cx),
92        }
93    }
94
95    fn poll_shutdown(
96        self: std::pin::Pin<&mut Self>,
97        cx: &mut std::task::Context<'_>,
98    ) -> std::task::Poll<std::io::Result<()>> {
99        match self.get_mut() {
100            Self::Rfcomm(s) => tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx),
101            Self::L2cap(s) => tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(s), cx),
102        }
103    }
104}
105
106// ────────────────────────────────────────────────────────────────────────────
107// BluetoothRfcommSocket – used for both RFCOMM and L2CAP outgoing connections
108// ────────────────────────────────────────────────────────────────────────────
109
110/// An outgoing bluetooth socket that may carry either an RFCOMM or L2CAP
111/// connection.  The socket is created lazily; call `connect()` before doing
112/// any I/O.
113pub struct BluetoothRfcommSocket {
114    /// Address of the remote device
115    device_addr: bluer::Address,
116    /// RFCOMM channel to connect on (mutually exclusive with `l2cap_psm`).
117    ///
118    /// BlueZ does not expose a direct SDP query API through the bluer crate, so
119    /// callers are responsible for resolving the correct channel via
120    /// `run_sdp` / `get_uuids` before creating the socket, or by relying on
121    /// a well-known channel number for the service.  Channel 1 is used as a
122    /// default placeholder when the channel is not otherwise known.
123    rfcomm_channel: Option<u8>,
124    /// L2CAP PSM to connect on (mutually exclusive with `rfcomm_channel`).
125    l2cap_psm: Option<u16>,
126    /// Whether to request an encrypted / authenticated link
127    is_secure: bool,
128    /// The live connection, present after a successful `connect()` call
129    connection: Option<BluetoothConnection>,
130}
131
132impl BluetoothRfcommSocket {
133    /// Create a new (unconnected) RFCOMM socket.
134    fn new_rfcomm(device_addr: bluer::Address, channel: u8, is_secure: bool) -> Self {
135        Self {
136            device_addr,
137            rfcomm_channel: Some(channel),
138            l2cap_psm: None,
139            is_secure,
140            connection: None,
141        }
142    }
143
144    /// Create a new (unconnected) L2CAP socket.
145    fn new_l2cap(device_addr: bluer::Address, psm: u16, is_secure: bool) -> Self {
146        Self {
147            device_addr,
148            rfcomm_channel: None,
149            l2cap_psm: Some(psm),
150            is_secure,
151            connection: None,
152        }
153    }
154}
155
156impl tokio::io::AsyncRead for BluetoothRfcommSocket {
157    fn poll_read(
158        self: std::pin::Pin<&mut Self>,
159        cx: &mut std::task::Context<'_>,
160        buf: &mut tokio::io::ReadBuf<'_>,
161    ) -> std::task::Poll<std::io::Result<()>> {
162        if let Some(conn) = &mut self.get_mut().connection {
163            tokio::io::AsyncRead::poll_read(std::pin::Pin::new(conn), cx, buf)
164        } else {
165            std::task::Poll::Ready(Err(std::io::Error::new(
166                std::io::ErrorKind::NotConnected,
167                "not connected",
168            )))
169        }
170    }
171}
172
173impl tokio::io::AsyncWrite for BluetoothRfcommSocket {
174    fn poll_write(
175        self: std::pin::Pin<&mut Self>,
176        cx: &mut std::task::Context<'_>,
177        buf: &[u8],
178    ) -> std::task::Poll<std::io::Result<usize>> {
179        if let Some(conn) = &mut self.get_mut().connection {
180            tokio::io::AsyncWrite::poll_write(std::pin::Pin::new(conn), cx, buf)
181        } else {
182            std::task::Poll::Ready(Err(std::io::Error::new(
183                std::io::ErrorKind::NotConnected,
184                "not connected",
185            )))
186        }
187    }
188
189    fn poll_flush(
190        self: std::pin::Pin<&mut Self>,
191        cx: &mut std::task::Context<'_>,
192    ) -> std::task::Poll<std::io::Result<()>> {
193        if let Some(conn) = &mut self.get_mut().connection {
194            tokio::io::AsyncWrite::poll_flush(std::pin::Pin::new(conn), cx)
195        } else {
196            std::task::Poll::Ready(Ok(()))
197        }
198    }
199
200    fn poll_shutdown(
201        self: std::pin::Pin<&mut Self>,
202        cx: &mut std::task::Context<'_>,
203    ) -> std::task::Poll<std::io::Result<()>> {
204        if let Some(conn) = &mut self.get_mut().connection {
205            tokio::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(conn), cx)
206        } else {
207            std::task::Poll::Ready(Ok(()))
208        }
209    }
210}
211
212#[async_trait::async_trait]
213impl crate::BluetoothSocketTrait for BluetoothRfcommSocket {
214    fn supports_async(&mut self) -> Option<&mut (dyn super::AsyncReadWrite)> {
215        Some(self)
216    }
217
218    fn is_connected(&self) -> Result<bool, std::io::Error> {
219        Ok(self.connection.is_some())
220    }
221
222    fn sync_connect(&mut self) -> Result<(), std::io::Error> {
223        Err(std::io::Error::new(std::io::ErrorKind::Other, "sync not supported"))
224    }
225
226    async fn async_connect(&mut self) -> Result<(), std::io::Error> {
227        if self.connection.is_some() {
228            return Ok(());
229        }
230        if let Some(channel) = self.rfcomm_channel {
231            let addr = bluer::rfcomm::SocketAddr::new(self.device_addr, channel);
232            let socket = bluer::rfcomm::Socket::new()
233                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
234            if self.is_secure {
235                socket
236                    .set_security(bluer::rfcomm::Security {
237                        level: bluer::rfcomm::SecurityLevel::Medium,
238                        key_size: 0,
239                    })
240                    .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
241            }
242            let stream = socket
243                .connect(addr)
244                .await
245                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
246            log::info!("STREAM {:?} to {:?}", stream.as_ref().local_addr(), stream.peer_addr());
247            self.connection = Some(BluetoothConnection::Rfcomm(stream));
248            log::info!("Got an rfcomm stream");
249        } else if let Some(psm) = self.l2cap_psm {
250            let addr = bluer::l2cap::SocketAddr::new(
251                self.device_addr,
252                bluer::AddressType::BrEdr,
253                psm,
254            );
255            let socket = bluer::l2cap::Socket::<bluer::l2cap::Stream>::new_stream()
256                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
257            if self.is_secure {
258                socket
259                    .set_security(bluer::l2cap::Security {
260                        level: bluer::l2cap::SecurityLevel::Medium,
261                        key_size: 0,
262                    })
263                    .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
264            }
265            let stream = socket
266                .connect(addr)
267                .await
268                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
269            self.connection = Some(BluetoothConnection::L2cap(stream));
270        } else {
271            return Err(std::io::Error::new(
272                std::io::ErrorKind::InvalidInput,
273                "BluetoothRfcommSocket has neither an RFCOMM channel nor an L2CAP PSM configured",
274            ));
275        }
276        Ok(())
277    }
278}
279
280// ────────────────────────────────────────────────────────────────────────────
281// LinuxBluetoothDevice – wraps bluer::Device and owns its open sockets
282// ────────────────────────────────────────────────────────────────────────────
283
284/// A Linux bluetooth device backed by bluer.  Wraps a `bluer::Device` and
285/// stores open RFCOMM / L2CAP sockets so that returned `BluetoothSocket`
286/// references remain valid for the lifetime of this struct.
287pub struct LinuxBluetoothDevice {
288    /// The underlying bluer device handle
289    device: bluer::Device,
290}
291
292impl LinuxBluetoothDevice {
293    /// Wrap a `bluer::Device`.
294    pub fn new(device: bluer::Device) -> Self {
295        Self { device }
296    }
297}
298
299#[async_trait::async_trait]
300impl super::BluetoothDeviceAsyncTrait for LinuxBluetoothDevice {
301    async fn get_uuids(&mut self) -> Result<Vec<crate::BluetoothUuid>, std::io::Error> {
302        let uuids =
303            self.device.uuids().await.map_err(|e| {
304                std::io::Error::new(std::io::ErrorKind::Other, e.to_string())
305            })?;
306        Ok(uuids
307            .unwrap_or_default()
308            .into_iter()
309            .map(|u| {
310                use std::str::FromStr;
311                crate::BluetoothUuid::from_str(&u.to_string())
312                    .unwrap_or_else(|_| crate::BluetoothUuid::Unknown(u.to_string()))
313            })
314            .collect())
315    }
316
317    /// Returns the alias (display name) of the device, falling back to the
318    /// hardware name when no alias is set.
319    async fn get_name(&self) -> Result<String, std::io::Error> {
320        let device = self.device.clone();
321        device
322            .alias()
323            .await
324            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
325    }
326        
327    async fn get_pair_state(&self) -> Result<crate::PairingStatus, std::io::Error> {
328        let device = self.device.clone();
329        let paired = device
330            .is_paired()
331            .await
332            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
333        Ok(if paired {
334            crate::PairingStatus::Paired
335        } else {
336            crate::PairingStatus::NotPaired
337        })
338    }
339}
340
341impl super::BluetoothDeviceTrait for LinuxBluetoothDevice {
342    fn supports_async(&mut self) -> Option<&mut dyn super::BluetoothDeviceAsyncTrait> {
343        Some(self)
344    }
345
346    fn supports_sync(&mut self) -> Option<&mut dyn super::BluetoothDeviceSyncTrait> {
347        None
348    }
349
350    fn get_address(&mut self) -> Result<String, std::io::Error> {
351        Ok(self.device.address().to_string())
352    }
353
354    /// Return a socket suitable for an outgoing L2CAP connection to the given
355    /// service UUID.
356    ///
357    /// The PSM for the service must be known in advance.  Because bluer does
358    /// not expose a raw SDP query API, a proper PSM lookup is left as a
359    /// `// TODO` below; the default PSM of 1 (SDP channel itself) is a
360    /// placeholder and will need to be replaced with the actual dynamic PSM
361    /// discovered via SDP for real services.
362    fn get_l2cap_socket(
363        &mut self,
364        psm: u16,
365        is_secure: bool,
366    ) -> Result<crate::BluetoothSocket, String> {
367        let addr = self.device.address();
368        let socket = BluetoothRfcommSocket::new_l2cap(addr, psm, is_secure);
369        Ok(crate::BluetoothSocket::Bluez(socket))
370    }
371
372    /// Return a socket suitable for an outgoing RFCOMM connection to the given
373    /// service UUID.
374    ///
375    /// The RFCOMM channel for the service must be known in advance.  Because
376    /// bluer does not expose a raw SDP query API, a proper channel lookup is
377    /// left as a `// TODO` below; channel 1 is used as a placeholder and will
378    /// need to be replaced with the channel discovered via SDP for real
379    /// services (e.g. by using `sdptool` or an external SDP library).
380    fn get_rfcomm_socket(
381        &mut self,
382        channel: u8,
383        is_secure: bool,
384    ) -> Result<crate::BluetoothSocket, String> {
385        let addr = self.device.address();
386        let socket = BluetoothRfcommSocket::new_rfcomm(addr, channel, is_secure);
387        Ok(crate::BluetoothSocket::Bluez(socket))
388    }
389}
390
391// ────────────────────────────────────────────────────────────────────────────
392// BluetoothDiscovery
393// ────────────────────────────────────────────────────────────────────────────
394
395/// A struct for managing discovery of bluetooth devices
396pub struct BluetoothDiscovery {}
397
398impl BluetoothDiscovery {
399    /// Construct a new self
400    fn new() -> Self {
401        Self {}
402    }
403}
404
405impl super::BluetoothDiscoveryTrait for BluetoothDiscovery {}
406
407impl Drop for BluetoothDiscovery {
408    fn drop(&mut self) {}
409}
410
411// ────────────────────────────────────────────────────────────────────────────
412// TryFrom conversions for profile settings → bluer::rfcomm::Profile
413// ────────────────────────────────────────────────────────────────────────────
414
415impl TryFrom<super::BluetoothRfcommProfileSettings> for bluer::rfcomm::Profile {
416    type Error = String;
417    fn try_from(value: super::BluetoothRfcommProfileSettings) -> Result<Self, Self::Error> {
418        let service = if let Some(v) = value.service_uuid {
419            Some(bluer::Uuid::parse_str(&v).map_err(|e| e.to_string())?)
420        } else {
421            None
422        };
423        Ok(Self {
424            uuid: bluer::Uuid::parse_str(&value.uuid).map_err(|e| e.to_string())?,
425            name: value.name,
426            service,
427            role: if value.channel.is_some() {
428                Some(bluer::rfcomm::Role::Server)
429            } else {
430                None
431            },
432            channel: value.channel,
433            psm: value.psm,
434            require_authentication: value.authenticate,
435            require_authorization: value.authorize,
436            auto_connect: value.auto_connect,
437            service_record: value.sdp_record,
438            version: value.sdp_version,
439            features: value.sdp_features,
440            ..Default::default()
441        })
442    }
443}
444
445/// L2CAP profiles are registered through the same BlueZ D-Bus
446/// ProfileManager1 mechanism as RFCOMM profiles, but with the `psm` field
447/// set instead of `channel`.
448impl TryFrom<super::BluetoothL2capProfileSettings> for bluer::rfcomm::Profile {
449    type Error = String;
450    fn try_from(value: super::BluetoothL2capProfileSettings) -> Result<Self, Self::Error> {
451        let service = if let Some(v) = value.service_uuid {
452            Some(bluer::Uuid::parse_str(&v).map_err(|e| e.to_string())?)
453        } else {
454            None
455        };
456        Ok(Self {
457            uuid: bluer::Uuid::parse_str(&value.uuid).map_err(|e| e.to_string())?,
458            name: value.name,
459            service,
460            role: None,
461            channel: None,
462            psm: value.psm,
463            require_authentication: value.authenticate,
464            require_authorization: value.authorize,
465            auto_connect: value.auto_connect,
466            service_record: value.sdp_record,
467            version: value.sdp_version,
468            features: value.sdp_features,
469            ..Default::default()
470        })
471    }
472}
473
474// ────────────────────────────────────────────────────────────────────────────
475// BluetoothHandler – main adapter / session manager
476// ────────────────────────────────────────────────────────────────────────────
477
478/// The general bluetooth handler for the library. There should be only one per application on linux.
479pub struct BluetoothHandler {
480    /// The current bluetooth session
481    session: bluer::Session,
482    /// The list of bluetooth adapters for the system
483    adapters: Vec<bluer::Adapter>,
484    /// The agent for the handler
485    _blue_agent_handle: bluer::agent::AgentHandle,
486}
487
488impl super::BluetoothAdapterTrait for BluetoothHandler {
489    fn supports_async(&self) -> Option<&dyn super::AsyncBluetoothAdapterTrait> {
490        Some(self)
491    }
492
493    fn supports_sync(&self) -> Option<&dyn super::SyncBluetoothAdapterTrait> {
494        None
495    }
496}
497
498#[async_trait::async_trait]
499impl super::AsyncBluetoothAdapterTrait for BluetoothHandler {
500    async fn register_rfcomm_profile(
501        &self,
502        settings: super::BluetoothRfcommProfileSettings,
503    ) -> Result<crate::BluetoothRfcommProfileAsync, String> {
504        self.session
505            .register_profile(settings.try_into()?)
506            .await
507            .map(|a| super::BluetoothRfcommProfileAsync::Bluez(a.into()))
508            .map_err(|e| e.to_string())
509    }
510
511    /// Register an L2CAP profile with BlueZ.
512    ///
513    /// BlueZ exposes a single `RegisterProfile` D-Bus method (ProfileManager1)
514    /// that handles both RFCOMM and L2CAP profiles.  The `psm` field in the
515    /// settings selects L2CAP; the RFCOMM `channel` field is left as `None`.
516    async fn register_l2cap_profile(
517        &self,
518        settings: super::BluetoothL2capProfileSettings,
519    ) -> Result<crate::BluetoothL2capProfileAsync, String> {
520        self.session
521            .register_profile(settings.try_into()?)
522            .await
523            .map(|a| super::BluetoothL2capProfileAsync::Bluez(a.into()))
524            .map_err(|e| e.to_string())
525    }
526
527    fn start_discovery(&self) -> crate::BluetoothDiscovery {
528        BluetoothDiscovery::new().into()
529    }
530
531    /// Return all paired devices across every adapter.
532    async fn get_paired_devices(&self) -> Option<Vec<crate::BluetoothDevice>> {
533        let mut list = Vec::new();
534        for adapter in &self.adapters {
535            let result = {
536                let addrs = adapter.device_addresses().await.ok()?;
537                let mut paired = Vec::new();
538                for addr in addrs {
539                    if let Ok(dev) = adapter.device(addr) {
540                        if dev.is_paired().await.unwrap_or(false) {
541                            paired.push(dev);
542                        }
543                    }
544                }
545                Ok::<Vec<bluer::Device>, bluer::Error>(paired)
546            };
547            if let Ok(devices) = result {
548                for dev in devices {
549                    list.push(crate::BluetoothDevice::Bluez(LinuxBluetoothDevice::new(
550                        dev,
551                    )));
552                }
553            }
554        }
555        Some(list)
556    }
557
558    async fn addresses(&self) -> Vec<super::BluetoothAdapterAddress> {
559        let mut a = Vec::new();
560        for adapter in &self.adapters {
561            if let Ok(adr) = adapter.address().await {
562                a.push(super::BluetoothAdapterAddress::Byte(adr.0));
563            }
564        }
565        a
566    }
567
568    async fn set_discoverable(&self, d: bool) -> Result<(), ()> {
569        for adapter in &self.adapters {
570            adapter.set_discoverable(d).await.map_err(|_| ())?;
571        }
572        Ok(())
573    }
574}
575
576impl BluetoothHandler {
577    /// Retrieve the bluetooth addresses for all bluetooth adapters present
578    pub async fn addresses(&self) -> Vec<bluer::Address> {
579        let mut addrs = Vec::new();
580        for a in &self.adapters {
581            if let Ok(addr) = a.address().await {
582                addrs.push(addr);
583            }
584        }
585        addrs
586    }
587
588    /// Construct a new self
589    pub async fn new(
590        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
591    ) -> Result<Self, String> {
592        let session = bluer::Session::new().await.map_err(|e| e.to_string())?;
593
594        let adapter_names = session.adapter_names().await.map_err(|e| e.to_string())?;
595        let adapters: Vec<bluer::Adapter> = adapter_names
596            .iter()
597            .filter_map(|n| session.adapter(n).ok())
598            .collect();
599
600        let blue_agent = Self::build_agent(s);
601        let blue_agent_handle = session.register_agent(blue_agent).await;
602        println!("Registered a bluetooth agent {}", blue_agent_handle.is_ok());
603        Ok(Self {
604            session,
605            adapters,
606            _blue_agent_handle: blue_agent_handle.map_err(|e| e.to_string())?,
607        })
608    }
609
610    /// Enable all bluetooth adapters
611    async fn enable(&mut self) {
612        for adapter in &self.adapters {
613            adapter.set_powered(true).await.unwrap();
614            adapter.set_pairable(true).await.unwrap();
615        }
616    }
617
618    /// Disable all bluetooth adapters
619    async fn disable(&mut self) {
620        self.set_discoverable(false).await;
621        for adapter in &self.adapters {
622            adapter.set_powered(false).await.unwrap();
623            adapter.set_pairable(false).await.unwrap();
624        }
625    }
626
627    /// Enable or disable discoverable mode on all bluetooth adapters
628    pub async fn set_discoverable(&mut self, d: bool) {
629        for adapter in &self.adapters {
630            adapter.set_discoverable(d).await.unwrap();
631        }
632    }
633
634    /// Register an RFCOMM profile with the bluetooth session
635    pub async fn register_rfcomm_profile(
636        &mut self,
637        profile: bluer::rfcomm::Profile,
638    ) -> Result<bluer::rfcomm::ProfileHandle, bluer::Error> {
639        self.session.register_profile(profile).await
640    }
641
642    /// Build a bluetooth agent for the handler
643    fn build_agent(
644        s: tokio::sync::mpsc::Sender<super::MessageToBluetoothHost>,
645    ) -> bluer::agent::Agent {
646        let mut blue_agent = bluer::agent::Agent::default();
647        blue_agent.request_default = true;
648        blue_agent.request_pin_code = None;
649        blue_agent.request_passkey = None;
650        let s2 = s.clone();
651        blue_agent.display_passkey = Some(Box::new(move |mut a| {
652            println!("Running process for display_passkey: {:?}", a);
653            let s3 = s2.clone();
654            async move {
655                let mut chan = tokio::sync::mpsc::channel(5);
656                let _ = s3
657                    .send(super::MessageToBluetoothHost::DisplayPasskey(a.passkey, chan.0))
658                    .await;
659                loop {
660                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
661                    tokio::select! {
662                        asdf = f => {
663                            match asdf {
664                                Ok(Some(m)) => match m {
665                                    super::ResponseToPasskey::Yes => {
666                                        let _ = s3
667                                            .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
668                                            .await;
669                                        return Ok(());
670                                    }
671                                    super::ResponseToPasskey::No => {
672                                        let _ = s3
673                                            .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
674                                            .await;
675                                        return Err(bluer::agent::ReqError::Rejected);
676                                    }
677                                    super::ResponseToPasskey::Cancel => {
678                                        let _ = s3
679                                            .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
680                                            .await;
681                                        return Err(bluer::agent::ReqError::Canceled);
682                                    }
683                                    super::ResponseToPasskey::Waiting => {}
684                                },
685                                Ok(None) => {}
686                                _ => {
687                                    let _ = s3
688                                        .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
689                                        .await;
690                                    return Err(bluer::agent::ReqError::Canceled);
691                                }
692                            }
693                        }
694                        _ = &mut a.cancel => {
695                            let _ = s3
696                                .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
697                                .await;
698                            break Err(bluer::agent::ReqError::Canceled);
699                        }
700                    }
701                }
702            }
703            .boxed()
704        }));
705        blue_agent.display_pin_code = Some(Box::new(|a| {
706            async move {
707                println!("Need to display pin code {:?}", a);
708                a.cancel.await.unwrap();
709                Ok(())
710            }
711            .boxed()
712        }));
713        let s2 = s.clone();
714        blue_agent.request_confirmation = Some(Box::new(move |a| {
715            println!("Need to confirm {:?}", a);
716            let s3 = s2.clone();
717            async move {
718                let mut chan = tokio::sync::mpsc::channel(5);
719                let _ = s3
720                    .send(super::MessageToBluetoothHost::ConfirmPasskey(
721                        a.passkey, chan.0,
722                    ))
723                    .await;
724                loop {
725                    let f = tokio::time::timeout(std::time::Duration::from_secs(5), chan.1.recv());
726                    let asdf = f.await;
727                    match asdf {
728                        Ok(Some(m)) => match m {
729                            super::ResponseToPasskey::Yes => {
730                                let _ = s3
731                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
732                                    .await;
733                                return Ok(());
734                            }
735                            super::ResponseToPasskey::No => {
736                                let _ = s3
737                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
738                                    .await;
739                                return Err(bluer::agent::ReqError::Rejected);
740                            }
741                            super::ResponseToPasskey::Cancel => {
742                                let _ = s3
743                                    .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
744                                    .await;
745                                return Err(bluer::agent::ReqError::Canceled);
746                            }
747                            super::ResponseToPasskey::Waiting => {}
748                        },
749                        Ok(None) => {}
750                        _ => {
751                            let _ = s3
752                                .send(super::MessageToBluetoothHost::CancelDisplayPasskey)
753                                .await;
754                            return Err(bluer::agent::ReqError::Canceled);
755                        }
756                    }
757                }
758            }
759            .boxed()
760        }));
761        blue_agent.request_authorization = Some(Box::new(|a| {
762            async move {
763                println!("Need to authorize {:?}", a);
764                Ok(())
765            }
766            .boxed()
767        }));
768        blue_agent.authorize_service = Some(Box::new(|a| {
769            async move {
770                println!("Need to authorize service {:?}", a);
771                Ok(())
772            }
773            .boxed()
774        }));
775        blue_agent
776    }
777
778    /// Issues the specified bluetooth command, with an optional response for the command
779    pub async fn issue_command(
780        &mut self,
781        cmd: super::BluetoothCommand,
782    ) -> Option<super::BluetoothResponse> {
783        match cmd {
784            super::BluetoothCommand::QueryNumAdapters => {
785                Some(super::BluetoothResponse::Adapters(self.adapters.len()))
786            }
787            _ => None,
788        }
789    }
790
791    /// Run a scan on all the bluetooth adapters, updating `bluetooth_devices`
792    /// with newly discovered or removed devices.
793    pub async fn scan<'a>(
794        &'a mut self,
795        bluetooth_devices: &mut HashMap<
796            bluer::Address,
797            (&'a bluer::Adapter, Option<bluer::Device>),
798        >,
799    ) {
800        let mut adapter_scanner = Vec::new();
801        for a in &self.adapters {
802            let da = a.discover_devices_with_changes().await.unwrap();
803            adapter_scanner.push((a, da));
804        }
805
806        for (adapt, da) in &mut adapter_scanner {
807            if let Some(e) = da.next().await {
808                match e {
809                    AdapterEvent::DeviceAdded(addr) => {
810                        log::debug!("Device added {:?}", addr);
811                        bluetooth_devices.insert(addr, (adapt, None));
812                    }
813                    AdapterEvent::DeviceRemoved(addr) => {
814                        log::debug!("Device removed {:?}", addr);
815                        bluetooth_devices.remove_entry(&addr);
816                    }
817                    AdapterEvent::PropertyChanged(prop) => {
818                        log::debug!("Adapter property changed {:?}", prop);
819                    }
820                }
821            }
822        }
823
824        for (addr, (adapter, dev)) in bluetooth_devices.iter_mut() {
825            if dev.is_none() {
826                if let Ok(d) = adapter.device(*addr) {
827                    if let Ok(ps) = d.all_properties().await {
828                        for p in ps {
829                            log::debug!("Device {:?} property: {:?}", addr, p);
830                        }
831                    }
832                    *dev = Some(d);
833                }
834            }
835        }
836    }
837}