Skip to content

Commit ac68037

Browse files
fix: Duplicate Characteristic UUID overwrite each other
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.
1 parent 1937ff5 commit ac68037

File tree

4 files changed

+66
-25
lines changed

4 files changed

+66
-25
lines changed

src/bluez/peripheral.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,35 @@ impl api::Peripheral for Peripheral {
182182
let services = self.session.get_services(&self.device).await?;
183183
for service in services {
184184
let characteristics = self.session.get_characteristics(&service.id).await?;
185-
let characteristics =
186-
join_all(characteristics.into_iter().map(|characteristic| async {
187-
let descriptors = self
188-
.session
189-
.get_descriptors(&characteristic.id)
190-
.await
191-
.unwrap_or(Vec::new())
192-
.into_iter()
193-
.map(|descriptor| (descriptor.uuid, descriptor))
194-
.collect();
195-
CharacteristicInternal::new(characteristic, descriptors)
196-
}))
197-
.await;
185+
let characteristics = join_all(
186+
characteristics
187+
.into_iter()
188+
.fold(
189+
// Only consider the first characteristic of each UUID
190+
// This "should" be unique, but of course it's not enforced
191+
HashMap::<Uuid, CharacteristicInfo>::new(),
192+
|mut map, characteristic| {
193+
if !map.contains_key(&characteristic.uuid) {
194+
map.insert(characteristic.uuid, characteristic);
195+
}
196+
map
197+
},
198+
)
199+
.into_iter()
200+
.map(|mapped_characteristic| async {
201+
let characteristic = mapped_characteristic.1;
202+
let descriptors = self
203+
.session
204+
.get_descriptors(&characteristic.id)
205+
.await
206+
.unwrap_or(Vec::new())
207+
.into_iter()
208+
.map(|descriptor| (descriptor.uuid, descriptor))
209+
.collect();
210+
CharacteristicInternal::new(characteristic, descriptors)
211+
}),
212+
)
213+
.await;
198214
services_internal.insert(
199215
service.uuid,
200216
ServiceInternal {

src/corebluetooth/central_delegate.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use cocoa::base::{id, nil};
2929
use futures::channel::mpsc::{self, Receiver, Sender};
3030
use futures::sink::SinkExt;
3131
use libc::c_void;
32-
use log::{error, trace};
32+
use log::{error, trace, warn};
3333
use objc::{
3434
class,
3535
declare::ClassDecl,
@@ -690,7 +690,15 @@ pub mod CentralDelegate {
690690
// Create the map entry we'll need to export.
691691
let uuid = cbuuid_to_uuid(cb::attribute_uuid(c));
692692
let held_char = unsafe { StrongPtr::retain(c) };
693-
characteristics.insert(uuid, held_char);
693+
694+
// Only consider the first characteristic of each UUID
695+
// This "should" be unique, but of course it's not enforced
696+
697+
if !characteristics.contains_key(&uuid) {
698+
characteristics.insert(uuid, held_char);
699+
} else {
700+
warn!("Duplicate characteristic UUID discovered: {:?}", uuid)
701+
}
694702
}
695703
let peripheral_uuid = nsuuid_to_uuid(cb::peer_identifier(peripheral));
696704
let service_uuid = cbuuid_to_uuid(cb::attribute_uuid(service));

src/droidplug/peripheral.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,18 +280,18 @@ impl api::Peripheral for Peripheral {
280280
characteristic_uuid: characteristic.get_uuid()?,
281281
});
282282
}
283-
characteristics.insert(Characteristic {
283+
let char = Characteristic {
284284
service_uuid: service.get_uuid()?,
285285
uuid: characteristic.get_uuid()?,
286286
properties: characteristic.get_properties()?,
287287
descriptors: descriptors.clone(),
288-
});
289-
peripheral_characteristics.push(Characteristic {
290-
service_uuid: service.get_uuid()?,
291-
uuid: characteristic.get_uuid()?,
292-
properties: characteristic.get_properties()?,
293-
descriptors: descriptors,
294-
});
288+
};
289+
// Only consider the first characteristic of each UUID
290+
// This "should" be unique, but of course it's not enforced
291+
if !characteristics.contains(&char) {
292+
characteristics.insert(char.clone());
293+
peripheral_characteristics.push(char.clone());
294+
}
295295
}
296296
peripheral_services.push(Service {
297297
uuid: service.get_uuid()?,

src/winrtble/peripheral.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ use tokio::sync::broadcast;
4444
use uuid::Uuid;
4545

4646
use std::sync::Weak;
47+
use windows::core::GUID;
48+
use windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic;
4749
use windows::Devices::Bluetooth::{Advertisement::*, BluetoothAddressType};
4850

4951
#[cfg_attr(
@@ -413,8 +415,23 @@ impl ApiPeripheral for Peripheral {
413415
if !self.shared.ble_services.contains_key(&uuid) {
414416
match BLEDevice::get_characteristics(service).await {
415417
Ok(characteristics) => {
416-
let characteristics =
417-
characteristics.into_iter().map(|characteristic| async {
418+
let characteristics = characteristics
419+
.into_iter()
420+
.fold(
421+
// Only consider the first characteristic of each UUID
422+
// This "should" be unique, but of course it's not enforced
423+
HashMap::<GUID, GattCharacteristic>::new(),
424+
|mut map, gatt_characteristic| {
425+
let uuid = gatt_characteristic.Uuid().unwrap_or_default();
426+
if !map.contains_key(&uuid) {
427+
map.insert(uuid, gatt_characteristic);
428+
}
429+
map
430+
},
431+
)
432+
.into_iter()
433+
.map(|mapped_characteristic| async {
434+
let characteristic = mapped_characteristic.1;
418435
let c = characteristic.clone();
419436
(
420437
characteristic,

0 commit comments

Comments
 (0)