mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 15:16:12 -04:00
[fp-rs] Updated device scanning interface to facilitate adding more functionality to device constructor.
This commit is contained in:
@@ -25,11 +25,11 @@ pub trait Adapter: Sized {
|
||||
/// Retrieve the system-default Bluetooth adapter.
|
||||
async fn default() -> Result<Self, BluetoothError>;
|
||||
|
||||
/// Begin scanning for nearby devices.
|
||||
fn start_scan_devices(&mut self) -> Result<(), BluetoothError>;
|
||||
/// Begin scanning for nearby advertisements.
|
||||
fn start_scan(&mut self) -> Result<(), BluetoothError>;
|
||||
|
||||
/// Stop scanning for nearby devices.
|
||||
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError>;
|
||||
/// Stop scanning for nearby advertisements.
|
||||
fn stop_scan(&mut self) -> Result<(), BluetoothError>;
|
||||
|
||||
/// Poll next discovered device.
|
||||
async fn next_device(&mut self) -> Result<Self::Device, BluetoothError>;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::bluetooth::common::BluetoothError;
|
||||
|
||||
/// BLE Addresses can either be the peripheral's public MAC address, or various
|
||||
/// types of random addresses.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@@ -68,15 +70,14 @@ impl From<u64> for ClassicAddress {
|
||||
}
|
||||
|
||||
impl TryFrom<BleAddress> for ClassicAddress {
|
||||
// TODO proper error handling b/291931475
|
||||
type Error = anyhow::Error;
|
||||
type Error = BluetoothError;
|
||||
|
||||
fn try_from(addr: BleAddress) -> Result<Self, Self::Error> {
|
||||
match addr.kind {
|
||||
BleAddressKind::Public => Ok(ClassicAddress(addr.val)),
|
||||
BleAddressKind::Random => Err(anyhow::anyhow!(
|
||||
"Can't convert BLE Random address to Bluetooth Classic address."
|
||||
)),
|
||||
BleAddressKind::Random => Err(BluetoothError::BadTypeConversion(String::from(
|
||||
"can't convert BLE Random address to Bluetooth Classic address."
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ use thiserror::Error;
|
||||
#[non_exhaustive]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BluetoothError {
|
||||
/// Reported when the user attempts a bad type conversion, e.g. converting
|
||||
/// a BLE random address to a BT Classic address.
|
||||
#[error("bad type conversion: {0}")]
|
||||
BadTypeConversion(String),
|
||||
/// Reported when Bluetooth device pairing fails.
|
||||
#[error("pairing error: {0}")]
|
||||
PairingFailed(String),
|
||||
|
||||
@@ -29,11 +29,11 @@ impl Adapter for BleAdapter {
|
||||
panic!("Unsupported target platform.");
|
||||
}
|
||||
|
||||
fn start_scan_devices(&mut self) -> Result<(), BluetoothError> {
|
||||
fn start_scan(&mut self) -> Result<(), BluetoothError> {
|
||||
panic!("Unsupported target platform.");
|
||||
}
|
||||
|
||||
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> {
|
||||
fn stop_scan(&mut self) -> Result<(), BluetoothError> {
|
||||
panic!("Unsupported target platform.");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::{stream::Stream, StreamExt};
|
||||
use tracing::{error, warn};
|
||||
use futures::{channel::mpsc::Receiver, StreamExt};
|
||||
use tracing::{error, info, warn};
|
||||
use windows::{
|
||||
Devices::Bluetooth::{
|
||||
Advertisement::{
|
||||
@@ -55,16 +54,23 @@ use windows::{
|
||||
};
|
||||
|
||||
use super::BleDevice;
|
||||
use crate::bluetooth::common::{Adapter, BleAddress, BleAddressKind, BluetoothError};
|
||||
use crate::bluetooth::common::{
|
||||
Adapter, BleAddress, BleAddressKind, BluetoothError,
|
||||
};
|
||||
|
||||
/// Struct holding the necessary fields for listening to and handling incoming
|
||||
/// BLE advertisements.
|
||||
struct AdvListener {
|
||||
/// Holds callback for sending received advertisement events to `receiver`.
|
||||
watcher: BluetoothLEAdvertisementWatcher,
|
||||
/// Can be polled to consume incoming advertisement events.
|
||||
receiver: Receiver<BluetoothLEAdvertisementReceivedEventArgs>,
|
||||
}
|
||||
|
||||
/// Concrete type implementing `Adapter`, used for Windows BLE.
|
||||
pub struct BleAdapter {
|
||||
inner: BluetoothAdapter,
|
||||
// NOTE: Using Boxed dyn here is silly because only one concrete type ever
|
||||
// used. Change this to `impl Stream` once impl trait return types
|
||||
// stabilized for existential types.
|
||||
// b/289224233.
|
||||
device_stream: Option<Pin<Box<dyn Stream<Item = BleDevice> + Send + Sync>>>,
|
||||
listener: Option<AdvListener>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -87,11 +93,11 @@ impl Adapter for BleAdapter {
|
||||
|
||||
Ok(BleAdapter {
|
||||
inner,
|
||||
device_stream: None,
|
||||
listener: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn start_scan_devices(&mut self) -> Result<(), BluetoothError> {
|
||||
fn start_scan(&mut self) -> Result<(), BluetoothError> {
|
||||
let watcher = BluetoothLEAdvertisementWatcher::new()?;
|
||||
match watcher.SetScanningMode(BluetoothLEScanningMode::Active) {
|
||||
Ok(_) => (),
|
||||
@@ -149,7 +155,7 @@ impl Adapter for BleAdapter {
|
||||
>| {
|
||||
// Drop `sender`, closing the channel.
|
||||
let _sender = sender.take();
|
||||
println!("Watcher stopped receiving BLE advertisements.");
|
||||
info!("Watcher stopped receiving BLE advertisements.");
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
@@ -158,48 +164,14 @@ impl Adapter for BleAdapter {
|
||||
watcher.Stopped(&stopped_handler)?;
|
||||
watcher.Start()?;
|
||||
|
||||
// `receiver` is a `futures::channel::mpsc::Receiver`, which implements
|
||||
// `futures::stream::Stream`. This is essentially an async Iterator.
|
||||
// We apply a FilterMap to map from advertisement packet to a future
|
||||
// returning `BleDevice` and filter out undesired connections. We need a
|
||||
// pinned box to satisfy trait bounds for `Stream`.
|
||||
self.device_stream =
|
||||
Some(Box::pin(receiver.filter_map(move |event_args| {
|
||||
// Move `watcher` into `FilterMap` closure. This ensures `watcher`
|
||||
// is only dropped when the stream is closed.
|
||||
let _watcher = &watcher;
|
||||
|
||||
// Move `event_args` into async block.
|
||||
async move {
|
||||
match event_args.AdvertisementType().ok()? {
|
||||
BluetoothLEAdvertisementType::NonConnectableUndirected => {
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
let kind = event_args.BluetoothAddressType().ok()?;
|
||||
let addr = event_args.BluetoothAddress().ok()?;
|
||||
|
||||
let kind = BleAddressKind::try_from(kind).ok()?;
|
||||
let addr = BleAddress::new(addr, kind);
|
||||
|
||||
match BleDevice::new(addr).await {
|
||||
Ok(device) => Some(device),
|
||||
Err(err) => {
|
||||
warn!("Error creating device: {:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})));
|
||||
self.listener = Some(AdvListener { watcher, receiver });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> {
|
||||
if let Some(_) = &self.device_stream {
|
||||
self.device_stream.take();
|
||||
fn stop_scan(&mut self) -> Result<(), BluetoothError> {
|
||||
if let Some(listener) = self.listener.take() {
|
||||
listener.watcher.Stop()?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BluetoothError::FailedPrecondition(String::from(
|
||||
@@ -209,13 +181,37 @@ impl Adapter for BleAdapter {
|
||||
}
|
||||
|
||||
async fn next_device(&mut self) -> Result<Self::Device, BluetoothError> {
|
||||
if let Some(stream) = &mut self.device_stream {
|
||||
stream
|
||||
.next()
|
||||
.await
|
||||
.ok_or(BluetoothError::Internal(String::from(
|
||||
"device returned from Stream is None",
|
||||
)))
|
||||
if let Some(listener) = &mut self.listener {
|
||||
let stream = &mut listener.receiver;
|
||||
// We don't want the end-user to receive empty devices, so this is a
|
||||
// loop to catch and skip trivial errors from advertisements that
|
||||
// can't be turned into devices.
|
||||
loop {
|
||||
let event_args =
|
||||
stream.next().await.ok_or(BluetoothError::Internal(
|
||||
String::from("Event returned from stream is None."),
|
||||
))?;
|
||||
|
||||
match event_args.AdvertisementType()? {
|
||||
BluetoothLEAdvertisementType::NonConnectableUndirected => {
|
||||
()
|
||||
}
|
||||
_ => {
|
||||
let kind = event_args.BluetoothAddressType()?;
|
||||
let addr = event_args.BluetoothAddress()?;
|
||||
|
||||
let kind = BleAddressKind::try_from(kind)?;
|
||||
let addr = BleAddress::new(addr, kind);
|
||||
|
||||
match BleDevice::new(addr).await {
|
||||
Ok(device) => break Ok(device),
|
||||
Err(err) => {
|
||||
warn!("Error creating device: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(BluetoothError::FailedPrecondition(String::from(
|
||||
"device scanning hasn't started, please call `start_scan()`",
|
||||
|
||||
@@ -16,21 +16,23 @@
|
||||
//https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621
|
||||
use windows::Devices::Bluetooth::BluetoothAddressType;
|
||||
|
||||
use crate::bluetooth::common::BleAddressKind;
|
||||
use crate::bluetooth::common::{BleAddressKind, BluetoothError};
|
||||
|
||||
// Convenience for converting from Windows API to crate API.
|
||||
impl TryFrom<BluetoothAddressType> for BleAddressKind {
|
||||
type Error = anyhow::Error;
|
||||
type Error = BluetoothError;
|
||||
|
||||
fn try_from(kind: BluetoothAddressType) -> Result<Self, Self::Error> {
|
||||
match kind {
|
||||
BluetoothAddressType::Public => Ok(BleAddressKind::Public),
|
||||
BluetoothAddressType::Random => Ok(BleAddressKind::Random),
|
||||
BluetoothAddressType::Unspecified => Err(anyhow::anyhow!(
|
||||
"Attempting to construct `BleAddressKind` with device \
|
||||
advertising Unspecified address type."
|
||||
)),
|
||||
_ => Err(anyhow::anyhow!(format!(
|
||||
BluetoothAddressType::Unspecified => {
|
||||
Err(BluetoothError::BadTypeConversion(String::from(
|
||||
"Attempting to construct `BleAddressKind` with device \
|
||||
advertising Unspecified address type.",
|
||||
)))
|
||||
}
|
||||
_ => Err(BluetoothError::BadTypeConversion(format!(
|
||||
"Attempting to construct `BleAddressKind` with device \
|
||||
advertising invalid address type {}.",
|
||||
kind.0,
|
||||
|
||||
@@ -23,7 +23,7 @@ use bluetooth::{Adapter, Address, ClassicAddress, Device};
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let run = async {
|
||||
let mut adapter = bluetooth::default_adapter().await?;
|
||||
adapter.start_scan_devices()?;
|
||||
adapter.start_scan()?;
|
||||
|
||||
while let Ok(ble_device) = adapter.next_device().await {
|
||||
let name = ble_device.name()?;
|
||||
|
||||
Reference in New Issue
Block a user