mirror of
https://github.com/google/bumble.git
synced 2026-05-10 04:18:03 +00:00
device: introduce BR/EDR pending connections
This commit enable the BR/EDR pairing to run asynchronously to the connection being established. When in security mode 3, a controller shall start authentication as part of the connection, which result in HCI events being sent on a BD address without a completed connection (ie. no connection handle).
This commit is contained in:
@@ -419,6 +419,27 @@ class Connection(CompositeEventEmitter):
|
|||||||
self.gatt_client = None # Per-connection client
|
self.gatt_client = None # Per-connection client
|
||||||
self.gatt_server = device.gatt_server # By default, use the device's shared server
|
self.gatt_server = device.gatt_server # By default, use the device's shared server
|
||||||
|
|
||||||
|
# [Classic only]
|
||||||
|
@classmethod
|
||||||
|
def incomplete(cls, device, peer_address):
|
||||||
|
"""
|
||||||
|
Instantiate an incomplete connection (ie. one waiting for a HCI Connection Complete event).
|
||||||
|
Once received it shall be completed using the `.complete` method.
|
||||||
|
"""
|
||||||
|
return cls(device, None, BT_BR_EDR_TRANSPORT, device.public_address, peer_address, None, None, None, None)
|
||||||
|
|
||||||
|
# [Classic only]
|
||||||
|
def complete(self, handle, peer_resolvable_address, role, parameters):
|
||||||
|
"""
|
||||||
|
Finish an incomplete connection upon completion.
|
||||||
|
"""
|
||||||
|
assert self.handle is None
|
||||||
|
assert self.transport == BT_BR_EDR_TRANSPORT
|
||||||
|
self.handle = handle
|
||||||
|
self.peer_resolvable_address = peer_resolvable_address
|
||||||
|
self.role = role
|
||||||
|
self.parameters = parameters
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def role_name(self):
|
def role_name(self):
|
||||||
return 'CENTRAL' if self.role == BT_CENTRAL_ROLE else 'PERIPHERAL'
|
return 'CENTRAL' if self.role == BT_CENTRAL_ROLE else 'PERIPHERAL'
|
||||||
@@ -598,6 +619,8 @@ def with_connection_from_handle(function):
|
|||||||
def with_connection_from_address(function):
|
def with_connection_from_address(function):
|
||||||
@functools.wraps(function)
|
@functools.wraps(function)
|
||||||
def wrapper(self, address, *args, **kwargs):
|
def wrapper(self, address, *args, **kwargs):
|
||||||
|
if (connection := self.pending_connections.get(address, False)):
|
||||||
|
return function(self, connection, *args, **kwargs)
|
||||||
for connection in self.connections.values():
|
for connection in self.connections.values():
|
||||||
if connection.peer_address == address:
|
if connection.peer_address == address:
|
||||||
return function(self, connection, *args, **kwargs)
|
return function(self, connection, *args, **kwargs)
|
||||||
@@ -609,6 +632,8 @@ def with_connection_from_address(function):
|
|||||||
def try_with_connection_from_address(function):
|
def try_with_connection_from_address(function):
|
||||||
@functools.wraps(function)
|
@functools.wraps(function)
|
||||||
def wrapper(self, address, *args, **kwargs):
|
def wrapper(self, address, *args, **kwargs):
|
||||||
|
if (connection := self.pending_connections.get(address, False)):
|
||||||
|
return function(self, connection, address, *args, **kwargs)
|
||||||
for connection in self.connections.values():
|
for connection in self.connections.values():
|
||||||
if connection.peer_address == address:
|
if connection.peer_address == address:
|
||||||
return function(self, connection, address, *args, **kwargs)
|
return function(self, connection, address, *args, **kwargs)
|
||||||
@@ -696,6 +721,7 @@ class Device(CompositeEventEmitter):
|
|||||||
self.le_connecting = False
|
self.le_connecting = False
|
||||||
self.disconnecting = False
|
self.disconnecting = False
|
||||||
self.connections = {} # Connections, by connection handle
|
self.connections = {} # Connections, by connection handle
|
||||||
|
self.pending_connections = {} # Connections, by BD address (BR/EDR only)
|
||||||
self.classic_enabled = False
|
self.classic_enabled = False
|
||||||
self.inquiry_response = None
|
self.inquiry_response = None
|
||||||
self.address_resolver = None
|
self.address_resolver = None
|
||||||
@@ -1323,6 +1349,9 @@ class Device(CompositeEventEmitter):
|
|||||||
max_ce_length = int(prefs.max_ce_length / 0.625),
|
max_ce_length = int(prefs.max_ce_length / 0.625),
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
|
# Save pending connection
|
||||||
|
self.pending_connections[peer_address] = Connection.incomplete(self, peer_address)
|
||||||
|
|
||||||
# TODO: allow passing other settings
|
# TODO: allow passing other settings
|
||||||
result = await self.send_command(HCI_Create_Connection_Command(
|
result = await self.send_command(HCI_Create_Connection_Command(
|
||||||
bd_addr = peer_address,
|
bd_addr = peer_address,
|
||||||
@@ -1360,6 +1389,8 @@ class Device(CompositeEventEmitter):
|
|||||||
if transport == BT_LE_TRANSPORT:
|
if transport == BT_LE_TRANSPORT:
|
||||||
self.le_connecting = False
|
self.le_connecting = False
|
||||||
self.connect_own_address_type = None
|
self.connect_own_address_type = None
|
||||||
|
else:
|
||||||
|
self.pending_connections.pop(peer_address, None)
|
||||||
|
|
||||||
async def accept(
|
async def accept(
|
||||||
self,
|
self,
|
||||||
@@ -1429,6 +1460,9 @@ class Device(CompositeEventEmitter):
|
|||||||
self.on('connection', on_connection)
|
self.on('connection', on_connection)
|
||||||
self.on('connection_failure', on_connection_failure)
|
self.on('connection_failure', on_connection_failure)
|
||||||
|
|
||||||
|
# Save pending connection
|
||||||
|
self.pending_connections[peer_address] = Connection.incomplete(self, peer_address)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Accept connection request
|
# Accept connection request
|
||||||
await self.send_command(HCI_Accept_Connection_Request_Command(
|
await self.send_command(HCI_Accept_Connection_Request_Command(
|
||||||
@@ -1442,6 +1476,7 @@ class Device(CompositeEventEmitter):
|
|||||||
finally:
|
finally:
|
||||||
self.remove_listener('connection', on_connection)
|
self.remove_listener('connection', on_connection)
|
||||||
self.remove_listener('connection_failure', on_connection_failure)
|
self.remove_listener('connection_failure', on_connection_failure)
|
||||||
|
self.pending_connections.pop(peer_address, None)
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def connect_as_gatt(self, peer_address):
|
async def connect_as_gatt(self, peer_address):
|
||||||
@@ -1839,17 +1874,8 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
if transport == BT_BR_EDR_TRANSPORT:
|
if transport == BT_BR_EDR_TRANSPORT:
|
||||||
# Create a new connection
|
# Create a new connection
|
||||||
connection = Connection(
|
connection: Connection = self.pending_connections.pop(peer_address)
|
||||||
self,
|
connection.complete(connection_handle, peer_resolvable_address, role, connection_parameters)
|
||||||
connection_handle,
|
|
||||||
transport,
|
|
||||||
self.public_address,
|
|
||||||
peer_address,
|
|
||||||
peer_resolvable_address,
|
|
||||||
role,
|
|
||||||
connection_parameters,
|
|
||||||
phy=None
|
|
||||||
)
|
|
||||||
self.connections[connection_handle] = connection
|
self.connections[connection_handle] = connection
|
||||||
|
|
||||||
# We may have an accept ongoing waiting for a connection request for `peer_address`.
|
# We may have an accept ongoing waiting for a connection request for `peer_address`.
|
||||||
@@ -1955,6 +1981,9 @@ class Device(CompositeEventEmitter):
|
|||||||
|
|
||||||
# device configuration is set to accept any incoming connection
|
# device configuration is set to accept any incoming connection
|
||||||
elif self.classic_accept_any:
|
elif self.classic_accept_any:
|
||||||
|
# Save pending connection
|
||||||
|
self.pending_connections[bd_addr] = Connection.incomplete(self, bd_addr)
|
||||||
|
|
||||||
self.host.send_command_sync(
|
self.host.send_command_sync(
|
||||||
HCI_Accept_Connection_Request_Command(
|
HCI_Accept_Connection_Request_Command(
|
||||||
bd_addr = bd_addr,
|
bd_addr = bd_addr,
|
||||||
|
|||||||
Reference in New Issue
Block a user