forked from auracaster/bumble_mirror
Merge pull request #502 from google/gbg/extended-advertising-termination-reverse
support out of order advertising set termination / connection events
This commit is contained in:
@@ -1856,6 +1856,7 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# Extended advertising.
|
# Extended advertising.
|
||||||
self.extended_advertising_sets: Dict[int, AdvertisingSet] = {}
|
self.extended_advertising_sets: Dict[int, AdvertisingSet] = {}
|
||||||
|
self.connecting_extended_advertising_sets: Dict[int, AdvertisingSet] = {}
|
||||||
|
|
||||||
# Legacy advertising.
|
# Legacy advertising.
|
||||||
# The advertising and scan response data, as well as the advertising interval
|
# The advertising and scan response data, as well as the advertising interval
|
||||||
@@ -4009,14 +4010,28 @@ class Device(CompositeEventEmitter):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (connection := self.lookup_connection(connection_handle)):
|
if connection := self.lookup_connection(connection_handle):
|
||||||
logger.warning(f'no connection for handle 0x{connection_handle:04x}')
|
# We have already received the connection complete event.
|
||||||
|
self._complete_le_extended_advertising_connection(
|
||||||
|
connection, advertising_set
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Associate the connection handle with the advertising set, the connection
|
||||||
|
# will complete later.
|
||||||
|
logger.debug(
|
||||||
|
f'the connection with handle {connection_handle:04X} will complete later'
|
||||||
|
)
|
||||||
|
self.connecting_extended_advertising_sets[connection_handle] = advertising_set
|
||||||
|
|
||||||
|
def _complete_le_extended_advertising_connection(
|
||||||
|
self, connection: Connection, advertising_set: AdvertisingSet
|
||||||
|
) -> None:
|
||||||
# Update the connection address.
|
# Update the connection address.
|
||||||
connection.self_address = (
|
connection.self_address = (
|
||||||
advertising_set.random_address
|
advertising_set.random_address
|
||||||
if advertising_set.advertising_parameters.own_address_type
|
if advertising_set.random_address is not None
|
||||||
|
and advertising_set.advertising_parameters.own_address_type
|
||||||
in (OwnAddressType.RANDOM, OwnAddressType.RESOLVABLE_OR_RANDOM)
|
in (OwnAddressType.RANDOM, OwnAddressType.RESOLVABLE_OR_RANDOM)
|
||||||
else self.public_address
|
else self.public_address
|
||||||
)
|
)
|
||||||
@@ -4147,6 +4162,16 @@ class Device(CompositeEventEmitter):
|
|||||||
if role == HCI_CENTRAL_ROLE or not self.supports_le_extended_advertising:
|
if role == HCI_CENTRAL_ROLE or not self.supports_le_extended_advertising:
|
||||||
# We can emit now, we have all the info we need
|
# We can emit now, we have all the info we need
|
||||||
self._emit_le_connection(connection)
|
self._emit_le_connection(connection)
|
||||||
|
return
|
||||||
|
|
||||||
|
if role == HCI_PERIPHERAL_ROLE and self.supports_le_extended_advertising:
|
||||||
|
if advertising_set := self.connecting_extended_advertising_sets.pop(
|
||||||
|
connection_handle, None
|
||||||
|
):
|
||||||
|
# We have already received the advertising set termination event.
|
||||||
|
self._complete_le_extended_advertising_connection(
|
||||||
|
connection, advertising_set
|
||||||
|
)
|
||||||
|
|
||||||
@host_event_handler
|
@host_event_handler
|
||||||
def on_connection_failure(self, transport, peer_address, error_code):
|
def on_connection_failure(self, transport, peer_address, error_code):
|
||||||
|
|||||||
@@ -787,6 +787,10 @@ class Host(AbortableEventEmitter):
|
|||||||
# Just use the same implementation as for the non-enhanced event for now
|
# Just use the same implementation as for the non-enhanced event for now
|
||||||
self.on_hci_le_connection_complete_event(event)
|
self.on_hci_le_connection_complete_event(event)
|
||||||
|
|
||||||
|
def on_hci_le_enhanced_connection_complete_v2_event(self, event):
|
||||||
|
# Just use the same implementation as for the v1 event for now
|
||||||
|
self.on_hci_le_enhanced_connection_complete_event(event)
|
||||||
|
|
||||||
def on_hci_connection_complete_event(self, event):
|
def on_hci_connection_complete_event(self, event):
|
||||||
if event.status == hci.HCI_SUCCESS:
|
if event.status == hci.HCI_SUCCESS:
|
||||||
# Create/update the connection
|
# Create/update the connection
|
||||||
|
|||||||
@@ -301,9 +301,7 @@ async def test_legacy_advertising_connection(own_address_type):
|
|||||||
else:
|
else:
|
||||||
assert device.lookup_connection(0x0001).self_address == device.random_address
|
assert device.lookup_connection(0x0001).self_address == device.random_address
|
||||||
|
|
||||||
# For unknown reason, read_phy() in on_connection() would be killed at the end of
|
await async_barrier()
|
||||||
# test, so we force scheduling here to avoid an warning.
|
|
||||||
await asyncio.sleep(0.0001)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -384,9 +382,41 @@ async def test_extended_advertising_connection(own_address_type):
|
|||||||
else:
|
else:
|
||||||
assert device.lookup_connection(0x0001).self_address == device.random_address
|
assert device.lookup_connection(0x0001).self_address == device.random_address
|
||||||
|
|
||||||
# For unknown reason, read_phy() in on_connection() would be killed at the end of
|
await async_barrier()
|
||||||
# test, so we force scheduling here to avoid an warning.
|
|
||||||
await asyncio.sleep(0.0001)
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'own_address_type,',
|
||||||
|
(OwnAddressType.PUBLIC, OwnAddressType.RANDOM),
|
||||||
|
)
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_extended_advertising_connection_out_of_order(own_address_type):
|
||||||
|
device = Device(host=mock.AsyncMock(spec=Host))
|
||||||
|
peer_address = Address('F0:F1:F2:F3:F4:F5')
|
||||||
|
advertising_set = await device.create_advertising_set(
|
||||||
|
advertising_parameters=AdvertisingParameters(own_address_type=own_address_type)
|
||||||
|
)
|
||||||
|
device.on_advertising_set_termination(
|
||||||
|
HCI_SUCCESS,
|
||||||
|
advertising_set.advertising_handle,
|
||||||
|
0x0001,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
device.on_connection(
|
||||||
|
0x0001,
|
||||||
|
BT_LE_TRANSPORT,
|
||||||
|
peer_address,
|
||||||
|
BT_PERIPHERAL_ROLE,
|
||||||
|
ConnectionParameters(0, 0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
if own_address_type == OwnAddressType.PUBLIC:
|
||||||
|
assert device.lookup_connection(0x0001).self_address == device.public_address
|
||||||
|
else:
|
||||||
|
assert device.lookup_connection(0x0001).self_address == device.random_address
|
||||||
|
|
||||||
|
await async_barrier()
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user