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