Skip to content

Commit

Permalink
backends/scanner: always filter by service_uuids
Browse files Browse the repository at this point in the history
It was noted that on Linux, if another app was scanning at the same
time, BlueZ would trigger RSSI changes for all devices, even if they
they didn't match the service_uuids filter.

This change ensures that we always filter by service_uuids, even if
the OS isn't doing it for us.

On Windows, the OS wasn't filtering for us anyway, so we can just move
that code to the shared call_detection_callbacks() so that all backends
will make use of it.

Fixes: #1534
  • Loading branch information
dlech committed Apr 30, 2024
1 parent 37152ac commit a818521
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Fixed
* Fixed using wrong value for ``tx_power`` in Android backend. Fixes #1532.
* Fixed 4-character UUIDs not working on ``BleakClient.*_gatt_char`` methods. Fixes #1498.
* Fixed race condition with getting max PDU size on Windows. Fixes #1497.
* Fixed filtering advertisement data by service UUID when multiple apps are scanning. Fixes #1534.

`0.21.1`_ (2023-09-08)
======================
Expand Down
18 changes: 18 additions & 0 deletions bleak/backends/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,24 @@ def call_detection_callbacks(
Backend implementations should call this method when an advertisement
event is received from the OS.
"""

# Backends will make best effort to filter out advertisements that
# don't match the service UUIDs, but if other apps are scanning at the
# same time or something like that, we may still receive advertisements
# that don't match. So we need to do more filtering here to get the
# expected behavior.

if self._service_uuids:
if not advertisement_data.service_uuids:
return

for uuid in advertisement_data.service_uuids:
if uuid in self._service_uuids:
break
else:
# if there were no matching service uuids, the don't call the callback
return

for callback in self._ad_callbacks.values():
callback(device, advertisement_data)

Expand Down
19 changes: 5 additions & 14 deletions bleak/backends/winrt/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ def __init__(
else:
self._scanning_mode = BluetoothLEScanningMode.ACTIVE

# Unfortunately, due to the way Windows handles filtering, we can't
# make use of the service_uuids filter here. If we did we would only
# get the advertisement data or the scan data, but not both, so would
# miss out on other essential data. Advanced users can pass their own
# filters though if they want to.
self._signal_strength_filter = kwargs.get("SignalStrengthFilter", None)
self._advertisement_filter = kwargs.get("AdvertisementFilter", None)

Expand Down Expand Up @@ -198,20 +203,6 @@ def _received_handler(
bdaddr, local_name, raw_data, advertisement_data
)

# On Windows, we have to fake service UUID filtering. If we were to pass
# a BluetoothLEAdvertisementFilter to the BluetoothLEAdvertisementWatcher
# with the service UUIDs appropriately set, we would no longer receive
# scan response data (which commonly contains the local device name).
# So we have to do it like this instead.

if self._service_uuids:
for uuid in uuids:
if uuid in self._service_uuids:
break
else:
# if there were no matching service uuids, the don't call the callback
return

self.call_detection_callbacks(device, advertisement_data)

def _stopped_handler(self, sender, e):
Expand Down

0 comments on commit a818521

Please sign in to comment.