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