Skip to content

Commit 821623f

Browse files
blackspherefollowerqdot
authored andcommitted
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 6c3de80 commit 821623f

File tree

4 files changed

+75
-34
lines changed

4 files changed

+75
-34
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/internal.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,20 @@ impl PeripheralInternal {
217217
service_uuid: Uuid,
218218
characteristics: HashMap<Uuid, Retained<CBCharacteristic>>,
219219
) {
220-
let characteristics = characteristics
221-
.into_iter()
222-
.map(|(characteristic_uuid, characteristic)| {
223-
(
224-
characteristic_uuid,
225-
CharacteristicInternal::new(characteristic),
226-
)
227-
})
228-
.collect();
220+
let characteristics = characteristics.into_iter().fold(
221+
// Only consider the first characteristic of each UUID
222+
// This "should" be unique, but of course it's not enforced
223+
HashMap::<Uuid, CharacteristicInternal>::new(),
224+
|mut map, (characteristic_uuid, characteristic)| {
225+
if !map.contains_key(&characteristic_uuid) {
226+
map.insert(
227+
characteristic_uuid,
228+
CharacteristicInternal::new(characteristic),
229+
);
230+
}
231+
map
232+
},
233+
);
229234
let service = self
230235
.services
231236
.get_mut(&service_uuid)

src/droidplug/peripheral.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::{
88
use async_trait::async_trait;
99
use futures::stream::Stream;
1010
use jni::{
11-
descriptors,
1211
objects::{GlobalRef, JList, JObject},
1312
JNIEnv,
1413
};
@@ -270,7 +269,7 @@ impl api::Peripheral for Peripheral {
270269

271270
for service in list.iter()? {
272271
let service = JBluetoothGattService::from_env(env, service)?;
273-
let mut characteristics = BTreeSet::new();
272+
let mut characteristics = BTreeSet::<Characteristic>::new();
274273
for characteristic in service.get_characteristics()? {
275274
let mut descriptors = BTreeSet::new();
276275
for descriptor in characteristic.get_descriptors()? {
@@ -280,18 +279,23 @@ impl api::Peripheral for Peripheral {
280279
characteristic_uuid: characteristic.get_uuid()?,
281280
});
282281
}
283-
characteristics.insert(Characteristic {
282+
let char = Characteristic {
284283
service_uuid: service.get_uuid()?,
285284
uuid: characteristic.get_uuid()?,
286285
properties: characteristic.get_properties()?,
287286
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-
});
287+
};
288+
// Only consider the first characteristic of each UUID
289+
// This "should" be unique, but of course it's not enforced
290+
if characteristics
291+
.iter()
292+
.filter(|c| c.service_uuid == char.service_uuid && c.uuid == char.uuid)
293+
.count()
294+
== 0
295+
{
296+
characteristics.insert(char.clone());
297+
peripheral_characteristics.push(char.clone());
298+
}
295299
}
296300
peripheral_services.push(Service {
297301
uuid: service.get_uuid()?,

src/winrtble/peripheral.rs

Lines changed: 18 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,22 @@ 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(|(_, characteristic)| async {
418434
let c = characteristic.clone();
419435
(
420436
characteristic,

0 commit comments

Comments
 (0)