|
9 | 9 | import asyncio |
10 | 10 | import logging |
11 | 11 | import os |
| 12 | +import sys |
12 | 13 | from typing import ( |
13 | 14 | Any, |
14 | 15 | Callable, |
|
24 | 25 | ) |
25 | 26 | from weakref import WeakKeyDictionary |
26 | 27 |
|
| 28 | +if sys.version_info < (3, 11): |
| 29 | + from async_timeout import timeout as async_timeout |
| 30 | +else: |
| 31 | + from asyncio import timeout as async_timeout |
| 32 | + |
27 | 33 | from dbus_fast import BusType, Message, MessageType, Variant, unpack_variants |
28 | 34 | from dbus_fast.aio.message_bus import MessageBus |
29 | 35 |
|
@@ -441,7 +447,6 @@ async def passive_scan( |
441 | 447 | filters: List[OrPatternLike], |
442 | 448 | advertisement_callback: AdvertisementCallback, |
443 | 449 | device_removed_callback: DeviceRemovedCallback, |
444 | | - discovery_stopped_callback: DiscoveryStoppedCallback, |
445 | 450 | ) -> Callable[[], Coroutine]: |
446 | 451 | """ |
447 | 452 | Configures the advertisement data filters and starts scanning. |
@@ -472,12 +477,28 @@ async def passive_scan( |
472 | 477 | ) |
473 | 478 | self._device_removed_callbacks.append(device_removed_callback_and_state) |
474 | 479 |
|
| 480 | + # Once a monitoring job is activated by BlueZ, the client can expect to get notified |
| 481 | + # on the targeted advertisements no matter if there is an ongoing discovery session |
| 482 | + # (started/stopped with org.bluez.Adapter1.StartDiscovery/StopDiscovery, as done when |
| 483 | + # using active scanning). |
| 484 | + # That is why discovery_stopped_callback is not added to self._discovery_stopped_callbacks |
| 485 | + # at this point, as org.bluez.Adapter1.Discovering status is not relevant for passive |
| 486 | + # scanning. |
| 487 | + |
| 488 | + # If advertisement monitor is released before the scanning is stopped, it means that the |
| 489 | + # kernel does not support passive scanning and error was returned when trying to execute |
| 490 | + # MGMT command "Add Adv Patterns Monitor" (see https://github.com/hbldh/bleak/issues/1136). |
| 491 | + # Otherwise, monitor will be activated and start to receive advertisement packets. |
| 492 | + monitor_processed = asyncio.Queue() |
| 493 | + |
475 | 494 | try: |
476 | | - monitor = AdvertisementMonitor(filters, discovery_stopped_callback) |
| 495 | + monitor = AdvertisementMonitor(filters, monitor_processed.put_nowait) |
477 | 496 |
|
478 | 497 | # this should be a unique path to allow multiple python interpreters |
479 | 498 | # running bleak and multiple scanners within a single interpreter |
480 | | - monitor_path = f"/org/bleak/{os.getpid()}/{id(monitor)}" |
| 499 | + monitor_path = ( |
| 500 | + f"/org/bleak/{os.getpid()}/{type(monitor).__name__}_{id(monitor)}" |
| 501 | + ) |
481 | 502 |
|
482 | 503 | reply = await self._bus.call( |
483 | 504 | Message( |
@@ -529,14 +550,32 @@ async def stop(): |
529 | 550 | ) |
530 | 551 | assert_reply(reply) |
531 | 552 |
|
532 | | - return stop |
| 553 | + try: |
| 554 | + # Advertising Monitor will be "immediately" activated or released |
| 555 | + async with async_timeout(1): |
| 556 | + if await monitor_processed.get(): |
| 557 | + # Advertising Monitor has been activated |
| 558 | + return stop |
| 559 | + |
| 560 | + except asyncio.TimeoutError: |
| 561 | + pass |
| 562 | + |
| 563 | + # Do not call await stop() here as the bus is already locked |
533 | 564 |
|
534 | 565 | except BaseException: |
535 | 566 | # if starting scanning failed, don't leak the callbacks |
536 | 567 | self._advertisement_callbacks.remove(callback_and_state) |
537 | 568 | self._device_removed_callbacks.remove(device_removed_callback_and_state) |
538 | 569 | raise |
539 | 570 |
|
| 571 | + # Reaching here means that the Advertising Monitor has not been successfully activated |
| 572 | + await stop() |
| 573 | + |
| 574 | + raise BleakNoPassiveScanError( |
| 575 | + "Advertising Monitor (required for passive scanning) is not supported by this kernel" |
| 576 | + " (Linux kernel >= 5.10 is required)" |
| 577 | + ) |
| 578 | + |
540 | 579 | def add_device_watcher( |
541 | 580 | self, |
542 | 581 | device_path: str, |
|
0 commit comments