Skip to content

Commit

Permalink
fix: Duplicate Characteristic UUID overwrite each other
Browse files Browse the repository at this point in the history
This meant that if a device (like the LOOB) declares multiple characteristics
on a service with the same UUID, the later is used not the first. Ideally we'd
have access to them all, but since we can't tell them apart at the higher levels
first wins seems better than last wins.
  • Loading branch information
blackspherefollower committed Oct 6, 2024
1 parent 6aa36a1 commit aaab333
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 34 deletions.
42 changes: 29 additions & 13 deletions src/bluez/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,35 @@ impl api::Peripheral for Peripheral {
let services = self.session.get_services(&self.device).await?;
for service in services {
let characteristics = self.session.get_characteristics(&service.id).await?;
let characteristics =
join_all(characteristics.into_iter().map(|characteristic| async {
let descriptors = self
.session
.get_descriptors(&characteristic.id)
.await
.unwrap_or(Vec::new())
.into_iter()
.map(|descriptor| (descriptor.uuid, descriptor))
.collect();
CharacteristicInternal::new(characteristic, descriptors)
}))
.await;
let characteristics = join_all(
characteristics
.into_iter()
.fold(
// Only consider the first characteristic of each UUID
// This "should" be unique, but of course it's not enforced
HashMap::<Uuid, CharacteristicInfo>::new(),
|mut map, characteristic| {
if !map.contains_key(&characteristic.uuid) {
map.insert(characteristic.uuid, characteristic);
}
map
},
)
.into_iter()
.map(|mapped_characteristic| async {
let characteristic = mapped_characteristic.1;
let descriptors = self
.session
.get_descriptors(&characteristic.id)
.await
.unwrap_or(Vec::new())
.into_iter()
.map(|descriptor| (descriptor.uuid, descriptor))
.collect();
CharacteristicInternal::new(characteristic, descriptors)
}),
)
.await;
services_internal.insert(
service.uuid,
ServiceInternal {
Expand Down
23 changes: 14 additions & 9 deletions src/corebluetooth/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,20 @@ impl PeripheralInternal {
service_uuid: Uuid,
characteristics: HashMap<Uuid, Retained<CBCharacteristic>>,
) {
let characteristics = characteristics
.into_iter()
.map(|(characteristic_uuid, characteristic)| {
(
characteristic_uuid,
CharacteristicInternal::new(characteristic),
)
})
.collect();
let characteristics = characteristics.into_iter().fold(
// Only consider the first characteristic of each UUID
// This "should" be unique, but of course it's not enforced
HashMap::<Uuid, CharacteristicInternal>::new(),
|mut map, (characteristic_uuid, characteristic)| {
if !map.contains_key(&characteristic_uuid) {
map.insert(
characteristic_uuid,
CharacteristicInternal::new(characteristic),
);
}
map
},
);
let service = self
.services
.get_mut(&service_uuid)
Expand Down
23 changes: 13 additions & 10 deletions src/droidplug/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::{
use async_trait::async_trait;
use futures::stream::Stream;
use jni::{
descriptors,
objects::{GlobalRef, JList, JObject},
JNIEnv,
};
Expand Down Expand Up @@ -270,7 +269,7 @@ impl api::Peripheral for Peripheral {

for service in list.iter()? {
let service = JBluetoothGattService::from_env(env, service)?;
let mut characteristics = BTreeSet::new();
let mut characteristics = BTreeSet::<Characteristic>::new();
for characteristic in service.get_characteristics()? {
let mut descriptors = BTreeSet::new();
for descriptor in characteristic.get_descriptors()? {
Expand All @@ -280,18 +279,22 @@ impl api::Peripheral for Peripheral {
characteristic_uuid: characteristic.get_uuid()?,
});
}
characteristics.insert(Characteristic {
let char = Characteristic {
service_uuid: service.get_uuid()?,
uuid: characteristic.get_uuid()?,
properties: characteristic.get_properties()?,
descriptors: descriptors.clone(),
});
peripheral_characteristics.push(Characteristic {
service_uuid: service.get_uuid()?,
uuid: characteristic.get_uuid()?,
properties: characteristic.get_properties()?,
descriptors: descriptors,
});
};
// Only consider the first characteristic of each UUID
// This "should" be unique, but of course it's not enforced
if characteristics
.iter()
.filter(|c| c.service_uuid == char.service_uuid && c.uuid == char.uuid)
.count() == 0
{
characteristics.insert(char.clone());
peripheral_characteristics.push(char.clone());
}
}
peripheral_services.push(Service {
uuid: service.get_uuid()?,
Expand Down
20 changes: 18 additions & 2 deletions src/winrtble/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ use tokio::sync::broadcast;
use uuid::Uuid;

use std::sync::Weak;
use windows::core::GUID;
use windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic;
use windows::Devices::Bluetooth::{Advertisement::*, BluetoothAddressType};

#[cfg_attr(
Expand Down Expand Up @@ -413,8 +415,22 @@ impl ApiPeripheral for Peripheral {
if !self.shared.ble_services.contains_key(&uuid) {
match BLEDevice::get_characteristics(service).await {
Ok(characteristics) => {
let characteristics =
characteristics.into_iter().map(|characteristic| async {
let characteristics = characteristics
.into_iter()
.fold(
// Only consider the first characteristic of each UUID
// This "should" be unique, but of course it's not enforced
HashMap::<GUID, GattCharacteristic>::new(),
|mut map, gatt_characteristic| {
let uuid = gatt_characteristic.Uuid().unwrap_or_default();
if !map.contains_key(&uuid) {
map.insert(uuid, gatt_characteristic);
}
map
},
)
.into_iter()
.map(|(_, characteristic)| async {
let c = characteristic.clone();
(
characteristic,
Expand Down

0 comments on commit aaab333

Please sign in to comment.