Merge pull request #1950 from google/revert-1867-fpwinrs_seeker

Revert "Fast Pair BLE Device Discovery for Windows in Rust"
This commit is contained in:
suetfei
2023-07-14 12:52:38 -07:00
committed by GitHub
16 changed files with 7 additions and 566 deletions
+2 -12
View File
@@ -47,16 +47,6 @@ jobs:
submodules: recursive
- name: Build FPP
run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml
- name: Build Fast Pair
- name: Build Fairpair
run: cargo build --manifest-path fastpair/rust/Cargo.toml
build-rust-windows:
name: Build Rust on Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Build Fast Pair
run: cargo build --manifest-path fastpair/rust/Cargo.toml
-6
View File
@@ -47,9 +47,3 @@ Carthage/Build
.UlyssesRoot
.Ulysses-Settings.plist
.Ulysses-Group.plist
# IntelliJ
.idea
# Rust
Cargo.lock
-2
View File
@@ -1,2 +0,0 @@
# Build files
target
-12
View File
@@ -20,15 +20,3 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
futures = { version = "0.3", features = ["executor"] }
tracing = "0.1.37"
cfg-if = "1.0.0"
async-trait = "0.1"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.48", features = [
"Devices_Bluetooth",
"Devices_Bluetooth_Advertisement",
"Foundation",
] }
-1
View File
@@ -1 +0,0 @@
max_width = 80
-42
View File
@@ -1,42 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use async_trait::async_trait;
/// Concrete types implementing this trait are Bluetooth Central devices.
/// They provide methods for retrieving nearby connections and device info.
#[async_trait]
pub trait Adapter: Sized {
type Device: Device;
/// Retrieve the system-default Bluetooth adapter.
async fn default() -> Result<Self, anyhow::Error>;
/// Begin scanning for nearby devices.
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error>;
/// Stop scanning for nearby devices.
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error>;
/// Poll next discovered device.
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error>;
}
/// Concrete types implementing this trait represent Bluetooth Peripheral devices.
/// They provide methods for retrieving device info and running device actions,
/// such as pairing.
pub trait Device {
/// Retrieve the name advertised by this device.
fn name(&self) -> Result<String, anyhow::Error>;
}
-35
View File
@@ -1,35 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Split into separate crate once demo is finished, providing custom error types
// instead of using anyhow.
// b/290070686
pub mod common;
pub use common::{Adapter, Device};
cfg_if::cfg_if! {
if #[cfg(windows)] {
mod windows_ble;
use windows_ble::BleAdapter;
} else {
mod unsupported;
use unsupported::BleAdapter;
}
}
pub async fn default_adapter() -> Result<impl Adapter, anyhow::Error> {
BleAdapter::default().await
}
@@ -1,49 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use async_trait::async_trait;
use super::BleDevice;
use crate::bluetooth::common::Adapter;
/// Concrete type implementing `Adapter`, used for unsupported devices.
/// Every method should panic.
pub struct BleAdapter;
#[async_trait]
impl Adapter for BleAdapter {
type Device = BleDevice;
async fn default() -> Result<Self, anyhow::Error> {
panic!("Unsupported target platform.");
}
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> {
panic!("Unsupported target platform.");
}
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> {
panic!("Unsupported target platform.");
}
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error> {
panic!("Unsupported target platform.");
}
}
mod tests {
use super::*;
// TODO b/288592509 unit tests
}
@@ -1,31 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::bluetooth::common::Device;
/// Concrete type implementing `Device`, used for unsupported devices.
/// Every method should panic.
pub struct BleDevice;
impl Device for BleDevice {
fn name(&self) -> Result<String, anyhow::Error> {
panic!("Unsupported target platform.")
}
}
mod tests {
use super::*;
// TODO b/288592509 unit tests
}
@@ -1,20 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Bluetooth LE module for unsupported devices. Every method panics.
mod adapter;
mod device;
pub use adapter::*;
pub use device::*;
@@ -1,222 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// 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 windows::{
Devices::Bluetooth::{
Advertisement::{
// Struct that receives Bluetooth Low Energy (LE) advertisements.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher?view=winrt-22621
BluetoothLEAdvertisementReceivedEventArgs,
// Enum describing the type of advertisement (connectable, directed, etc).
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621
BluetoothLEAdvertisementType,
// Provides data for a Received event on a `BluetoothLEAdvertisementWatcher`.
// Instance is created when the Received event occurs in the watcher struct.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementreceivedeventargs?view=winrt-22621
BluetoothLEAdvertisementWatcher,
// Provides data for a Stopped event on a `BluetoothLEAdvertisementWatcher`.
// Instance is created when the Stopped event occurs on a watcher struct.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcherstoppedeventargs?view=winrt-22621
BluetoothLEAdvertisementWatcherStoppedEventArgs,
// Defines constants that specify a Bluetooth LE scanning mode.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothlescanningmode?view=winrt-22621
BluetoothLEScanningMode,
},
// Struct for obtaining global constant information about a computer's
// Bluetooth adapter.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter?view=winrt-22621
BluetoothAdapter,
},
// Wraps a closure for handling events associated with a struct
// (e.g. Received and Stopped events in BluetoothLEAdvertisementWatcher).
// https://learn.microsoft.com/en-us/uwp/api/windows.foundation.typedeventhandler-2?view=winrt-22621
Foundation::TypedEventHandler,
};
use super::BleDevice;
use crate::bluetooth::common::Adapter;
/// 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>>>,
}
#[async_trait]
impl Adapter for BleAdapter {
type Device = BleDevice;
async fn default() -> Result<Self, anyhow::Error> {
let inner = BluetoothAdapter::GetDefaultAsync()?.await?;
if !inner.IsLowEnergySupported()? {
return Err(anyhow::anyhow!(
"This device's Bluetooth Adapter doesn't support Bluetooth LE Transport type."
));
}
if !inner.IsCentralRoleSupported()? {
return Err(anyhow::anyhow!(
"This device's Bluetooth Adapter doesn't support Bluetooth LE central role."
));
}
Ok(BleAdapter {
inner,
device_stream: None,
})
}
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> {
let watcher = BluetoothLEAdvertisementWatcher::new()?;
match watcher.SetScanningMode(BluetoothLEScanningMode::Active) {
Ok(_) => (),
Err(err) => {
warn!("Failed to turn on active scanning. Error: {}", err)
}
};
if self.inner.IsExtendedAdvertisingSupported()? {
watcher.SetAllowExtendedAdvertisements(true)?;
}
// `futures::channel::mpsc` is like `std::sync::mpsc` but `impl Stream`.
let (sender, receiver) = futures::channel::mpsc::channel(16);
let sender = Arc::new(std::sync::Mutex::new(sender));
// `received_handler` closure holds non-owning channel reference, to
// ensure `stopped_handler` can close the channel when
// `received_handler` is done.
let weak_sender = Arc::downgrade(&sender);
let received_handler = TypedEventHandler::new(
// Move `weak_sender` into closure.
move |watcher: &Option<BluetoothLEAdvertisementWatcher>,
event_args: &Option<
BluetoothLEAdvertisementReceivedEventArgs,
>| {
if watcher.is_some() {
if let Some(event_args) = event_args {
if let Some(sender) = weak_sender.upgrade() {
match sender
.lock()
.unwrap()
.try_send(event_args.clone())
{
Ok(_) => (),
Err(err) => {
error!("Error while handling Received event: {:?}", err)
}
}
}
}
}
Ok(())
},
);
// `stopped_handler` closure owns channel reference, can close channel.
let mut sender = Some(sender);
let stopped_handler = TypedEventHandler::new(
// Move `sender` into closure.
move |_watcher,
_event_args: &Option<
BluetoothLEAdvertisementWatcherStoppedEventArgs,
>| {
// Drop `sender`, closing the channel.
let _sender = sender.take();
println!("Watcher stopped receiving BLE advertisements.");
Ok(())
},
);
watcher.Received(&received_handler)?;
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 addr = event_args.BluetoothAddress().ok()?;
let kind = event_args.BluetoothAddressType().ok()?;
match BleDevice::from_addr(addr, kind).await {
Ok(device) => Some(device),
Err(err) => {
warn!("Error creating device: {:?}", err);
None
}
}
}
}
}
})));
Ok(())
}
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> {
if let Some(_) = &self.device_stream {
self.device_stream.take();
Ok(())
} else {
Err(anyhow::anyhow!("Device scanning hasn't started."))
}
}
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error> {
if let Some(stream) = &mut self.device_stream {
stream
.next()
.await
.ok_or(anyhow::anyhow!("Device returned from stream is None."))
} else {
Err(anyhow::anyhow!("Device scanning hasn't started."))
}
}
}
mod tests {
use super::*;
// TODO b/288592509 unit tests
}
@@ -1,58 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use async_trait::async_trait;
use windows::Devices::Bluetooth::{
// Enum describing the type of address (public, random, unspecified).
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621
BluetoothAddressType,
// Struct for interacting with and pairing to a discovered BLE device.
// https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice?view=winrt-22621
BluetoothLEDevice,
};
use crate::bluetooth::common::Device;
/// Concrete type implementing `Device`, used for Windows BLE.
pub struct BleDevice {
inner: BluetoothLEDevice,
}
impl BleDevice {
/// Create a `BleDevice` instance from the raw bluetooth address information.
pub(super) async fn from_addr(
addr: u64,
kind: BluetoothAddressType,
) -> Result<Self, anyhow::Error> {
let inner =
BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync(addr, kind)?
.await?;
Ok(BleDevice { inner })
}
}
#[async_trait]
impl Device for BleDevice {
fn name(&self) -> Result<String, anyhow::Error> {
Ok(self.inner.Name()?.to_string_lossy())
}
}
mod tests {
use super::*;
// TODO b/288592509 unit tests
}
@@ -1,20 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Bluetooth LE module for Windows devices.
mod adapter;
mod device;
pub use adapter::*;
pub use device::*;
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Library file, exports modules for use in integration tests and external crates.
pub mod bluetooth;
+5 -19
View File
@@ -1,4 +1,4 @@
// Copyright 2023 Google LLC
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,23 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use futures::executor;
mod message_stream;
mod types;
mod bluetooth;
use bluetooth::{Adapter, Device};
fn main() -> Result<(), anyhow::Error> {
let run = async {
let mut adapter = bluetooth::default_adapter().await?;
adapter.start_scan_devices()?;
while let Ok(device) = adapter.next_device().await {
println!("found {}", device.name()?)
}
unreachable!("Done scanning");
};
executor::block_on(run)
fn main() {
println!("Fastpair Rust!");
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use fastpair::*;
mod tests {
use super::*;
// TODO b/288592509 write integration tests
}