[fp-rs] Device now holds 16bit UUID service data, collected by Adapter.

This commit is contained in:
Lucas Silva Shepard
2023-07-29 17:18:46 -07:00
parent 4be9872f6d
commit 81592cc4c5
7 changed files with 101 additions and 13 deletions
+2
View File
@@ -32,4 +32,6 @@ windows = { version = "0.48", features = [
"Devices_Enumeration",
"Devices_Bluetooth_Advertisement",
"Foundation",
"Foundation_Collections",
"Storage_Streams",
] }
@@ -0,0 +1,36 @@
// 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.
pub enum BleDataSection {
ServiceData16BitUUid = 0x16,
}
pub struct ServiceData<U: Copy> {
uuid: U,
data: Vec<u8>,
}
impl<U: Copy> ServiceData<U> {
pub fn new(uuid: U, data: Vec<u8>) -> Self {
ServiceData { uuid, data }
}
pub fn uuid(&self) -> U {
self.uuid
}
pub fn data(&self) -> &Vec<u8> {
&self.data
}
}
+3 -1
View File
@@ -14,7 +14,7 @@
use async_trait::async_trait;
use super::{Address, BluetoothError, PairingResult};
use super::{Address, BluetoothError, PairingResult, ServiceData};
/// Concrete types implementing this trait represent Bluetooth Peripheral devices.
/// They provide methods for retrieving device info and running device actions,
@@ -29,4 +29,6 @@ pub trait Device: Sized {
/// Attempt pairing with the peripheral device.
async fn pair(&self) -> Result<PairingResult, BluetoothError>;
fn service_data(&self) -> &Vec<ServiceData<u16>>;
}
@@ -15,10 +15,12 @@
/// Module for shared functionality between all Bluetooth platforms.
mod adapter;
mod address;
mod data;
mod device;
mod error;
pub use adapter::*;
pub use address::*;
pub use data::*;
pub use device::*;
pub use error::*;
+44 -7
View File
@@ -51,11 +51,16 @@ use windows::{
// (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,
// Struct for reading data from a Windows stream, like an IVectorView.
// https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-22621
Storage::Streams::DataReader,
};
use super::BleDevice;
use crate::bluetooth::common::{
Adapter, BleAddress, BleAddressKind, BluetoothError,
Adapter, BleAddress, BleAddressKind, BleDataSection, BluetoothError,
ServiceData,
};
/// Struct holding the necessary fields for listening to and handling incoming
@@ -73,6 +78,39 @@ pub struct BleAdapter {
listener: Option<AdvListener>,
}
/// Parse the advertisement's service data.
/// Further Reading:
/// * `BleMedium::AdvertisementReceivedHandler` under
/// github.com/google/nearby/internal/platform/implementation/windows_ble/ble_medium.cc.
/// * Bluetooth Core Specification Supplement, Part A, Section 1.11.
/// * go/fast_pair_windows_data_parse.
#[inline]
fn get_service_data_16bit_uuid(
event_args: &BluetoothLEAdvertisementReceivedEventArgs,
) -> Result<Vec<ServiceData<u16>>, BluetoothError> {
let advertisement = event_args.Advertisement()?;
let mut service_data_vec = Vec::new();
// Note `service_data` is `!Send` and `!Sync`. This means processing must
// occur in a synchronous environment (namely, this function's scope).
// The compiler will complain if similar code is written between awaits
// in an async function.
for service_data in advertisement
.GetSectionsByType(BleDataSection::ServiceData16BitUUid as u8)?
{
let data_reader = DataReader::FromBuffer(&service_data.Data()?)?;
let uuid = data_reader.ReadUInt16()?;
let unconsumed_buffer_len =
data_reader.UnconsumedBufferLength()? as usize;
let mut data = vec![0u8; unconsumed_buffer_len];
data_reader.ReadBytes(&mut data)?;
service_data_vec.push(ServiceData::new(uuid, data));
}
Ok(service_data_vec)
}
#[async_trait]
impl Adapter for BleAdapter {
type Device = BleDevice;
@@ -202,13 +240,12 @@ impl Adapter for BleAdapter {
let kind = BleAddressKind::try_from(kind)?;
let addr = BleAddress::new(addr, kind);
let service_data =
get_service_data_16bit_uuid(&event_args)?;
match BleDevice::new(addr).await {
Ok(device) => break Ok(device),
Err(err) => {
warn!("Error creating device: {:?}", err);
}
}
let device = BleDevice::new(addr, service_data).await?;
break Ok(device);
}
}
}
+12 -3
View File
@@ -49,12 +49,13 @@ use windows::{
Foundation::TypedEventHandler,
};
use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, BluetoothError, PairingResult};
use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, ServiceData, BluetoothError, PairingResult};
/// Concrete type implementing `Device`, used for Windows BLE.
pub struct BleDevice {
inner: BluetoothLEDevice,
addr: BleAddress,
service_data: Vec<ServiceData<u16>>
}
/// Concrete type implementing `Device`, used for Windows Bluetooth Classic.
@@ -65,7 +66,7 @@ pub struct ClassicDevice {
impl BleDevice {
/// `BleDevice` constructor.
pub async fn new(addr: BleAddress) -> Result<Self, BluetoothError> {
pub async fn new(addr: BleAddress, service_data: Vec<ServiceData<u16>>) -> Result<Self, BluetoothError> {
let kind = BluetoothAddressType::from(addr.get_kind());
let raw_addr = u64::from(addr);
@@ -74,7 +75,7 @@ impl BleDevice {
)?
.await?;
Ok(BleDevice { inner, addr })
Ok(BleDevice { inner, addr, service_data })
}
}
@@ -95,6 +96,10 @@ impl Device for BleDevice {
// `ClassicDevice::pair` directly.
unimplemented!("BLE Pairing is currently unsupported.")
}
fn service_data(&self) -> &Vec<ServiceData<u16>> {
&self.service_data
}
}
@@ -169,6 +174,10 @@ impl Device for ClassicDevice {
}
}
}
fn service_data(&self) -> &Vec<ServiceData<u16>> {
unimplemented!("Service data is currently unsupported for Classic devices.")
}
}
mod tests {
+2 -2
View File
@@ -36,8 +36,8 @@ fn main() -> Result<(), Box<dyn Error>> {
// Dynamic dispatch is necessary here because `BleDevice` and
// `ClassicDevice` share the `Device` trait (and thus must have
// the same return type for `address()` method). This can be
// changed if `Device` trait should exclusively define
// cross-platform behavior.
// changed later if `Device` trait should exclusively define
// shared cross-platform behavior.
let classic_addr = match addr {
Address::Ble(ble) => ClassicAddress::try_from(ble),
Address::Classic(_) => unreachable!(