mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
fix role state for classic connections
This commit is contained in:
11
apps/pair.py
11
apps/pair.py
@@ -264,6 +264,7 @@ async def pair(
|
||||
sc,
|
||||
mitm,
|
||||
bond,
|
||||
ctkd,
|
||||
io,
|
||||
prompt,
|
||||
request,
|
||||
@@ -317,6 +318,7 @@ async def pair(
|
||||
if mode == 'classic':
|
||||
device.classic_enabled = True
|
||||
device.le_enabled = False
|
||||
device.classic_smp_enabled = ctkd
|
||||
|
||||
# Get things going
|
||||
await device.power_on()
|
||||
@@ -379,6 +381,13 @@ class LogHandler(logging.Handler):
|
||||
@click.option(
|
||||
'--bond', type=bool, default=True, help='Enable bonding', show_default=True
|
||||
)
|
||||
@click.option(
|
||||
'--ctkd',
|
||||
type=bool,
|
||||
default=True,
|
||||
help='Enable CTKD',
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
'--io',
|
||||
type=click.Choice(
|
||||
@@ -405,6 +414,7 @@ def main(
|
||||
sc,
|
||||
mitm,
|
||||
bond,
|
||||
ctkd,
|
||||
io,
|
||||
prompt,
|
||||
request,
|
||||
@@ -427,6 +437,7 @@ def main(
|
||||
sc,
|
||||
mitm,
|
||||
bond,
|
||||
ctkd,
|
||||
io,
|
||||
prompt,
|
||||
request,
|
||||
|
||||
@@ -595,7 +595,7 @@ class Connection(CompositeEventEmitter):
|
||||
|
||||
# [Classic only]
|
||||
@classmethod
|
||||
def incomplete(cls, device, peer_address):
|
||||
def incomplete(cls, device, peer_address, role):
|
||||
"""
|
||||
Instantiate an incomplete connection (ie. one waiting for a HCI Connection
|
||||
Complete event).
|
||||
@@ -608,28 +608,30 @@ class Connection(CompositeEventEmitter):
|
||||
device.public_address,
|
||||
peer_address,
|
||||
None,
|
||||
None,
|
||||
role,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
# [Classic only]
|
||||
def complete(self, handle, peer_resolvable_address, role, parameters):
|
||||
def complete(self, handle, 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
|
||||
# Quirk: role might be known before complete
|
||||
if self.role is None:
|
||||
self.role = role
|
||||
self.parameters = parameters
|
||||
|
||||
@property
|
||||
def role_name(self):
|
||||
return 'CENTRAL' if self.role == BT_CENTRAL_ROLE else 'PERIPHERAL'
|
||||
if self.role is None:
|
||||
return 'NOT-SET'
|
||||
if self.role == BT_CENTRAL_ROLE:
|
||||
return 'CENTRAL'
|
||||
if self.role == BT_PERIPHERAL_ROLE:
|
||||
return 'PERIPHERAL'
|
||||
return f'UNKNOWN[{self.role}]'
|
||||
|
||||
@property
|
||||
def is_encrypted(self):
|
||||
@@ -637,7 +639,7 @@ class Connection(CompositeEventEmitter):
|
||||
|
||||
@property
|
||||
def is_incomplete(self) -> bool:
|
||||
return self.handle == None
|
||||
return self.handle is None
|
||||
|
||||
def send_l2cap_pdu(self, cid, pdu):
|
||||
self.device.send_l2cap_pdu(self.handle, cid, pdu)
|
||||
@@ -750,10 +752,11 @@ class DeviceConfiguration:
|
||||
self.advertising_interval_max = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
||||
self.le_enabled = True
|
||||
# LE host enable 2nd parameter
|
||||
self.le_simultaneous_enabled = True
|
||||
self.le_simultaneous_enabled = False
|
||||
self.classic_enabled = False
|
||||
self.classic_sc_enabled = True
|
||||
self.classic_ssp_enabled = True
|
||||
self.classic_smp_enabled = True
|
||||
self.classic_accept_any = True
|
||||
self.connectable = True
|
||||
self.discoverable = True
|
||||
@@ -788,6 +791,9 @@ class DeviceConfiguration:
|
||||
self.classic_ssp_enabled = config.get(
|
||||
'classic_ssp_enabled', self.classic_ssp_enabled
|
||||
)
|
||||
self.classic_smp_enabled = config.get(
|
||||
'classic_smp_enabled', self.classic_smp_enabled
|
||||
)
|
||||
self.classic_accept_any = config.get(
|
||||
'classic_accept_any', self.classic_accept_any
|
||||
)
|
||||
@@ -997,8 +1003,9 @@ class Device(CompositeEventEmitter):
|
||||
self.le_enabled = config.le_enabled
|
||||
self.classic_enabled = config.classic_enabled
|
||||
self.le_simultaneous_enabled = config.le_simultaneous_enabled
|
||||
self.classic_ssp_enabled = config.classic_ssp_enabled
|
||||
self.classic_sc_enabled = config.classic_sc_enabled
|
||||
self.classic_ssp_enabled = config.classic_ssp_enabled
|
||||
self.classic_smp_enabled = config.classic_smp_enabled
|
||||
self.discoverable = config.discoverable
|
||||
self.connectable = config.connectable
|
||||
self.classic_accept_any = config.classic_accept_any
|
||||
@@ -1043,9 +1050,6 @@ class Device(CompositeEventEmitter):
|
||||
# Setup SMP
|
||||
self.smp_manager = smp.Manager(self)
|
||||
self.l2cap_channel_manager.register_fixed_channel(smp.SMP_CID, self.on_smp_pdu)
|
||||
self.l2cap_channel_manager.register_fixed_channel(
|
||||
smp.SMP_BR_CID, self.on_smp_pdu
|
||||
)
|
||||
|
||||
# Register the SDP server with the L2CAP Channel Manager
|
||||
self.sdp_server.register(self.l2cap_channel_manager)
|
||||
@@ -1182,6 +1186,12 @@ class Device(CompositeEventEmitter):
|
||||
if self.keystore is None:
|
||||
self.keystore = KeyStore.create_for_device(self)
|
||||
|
||||
# Finish setting up SMP based on post-init configurable options
|
||||
if self.classic_smp_enabled:
|
||||
self.l2cap_channel_manager.register_fixed_channel(
|
||||
smp.SMP_BR_CID, self.on_smp_pdu
|
||||
)
|
||||
|
||||
if self.host.supports_command(HCI_WRITE_LE_HOST_SUPPORT_COMMAND):
|
||||
await self.send_command(
|
||||
HCI_Write_LE_Host_Support_Command(
|
||||
@@ -1803,7 +1813,7 @@ class Device(CompositeEventEmitter):
|
||||
else:
|
||||
# Save pending connection
|
||||
self.pending_connections[peer_address] = Connection.incomplete(
|
||||
self, peer_address
|
||||
self, peer_address, BT_CENTRAL_ROLE
|
||||
)
|
||||
|
||||
# TODO: allow passing other settings
|
||||
@@ -1942,7 +1952,7 @@ class Device(CompositeEventEmitter):
|
||||
|
||||
# Save pending connection
|
||||
self.pending_connections[peer_address] = Connection.incomplete(
|
||||
self, peer_address
|
||||
self, peer_address, BT_PERIPHERAL_ROLE
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -2215,6 +2225,9 @@ class Device(CompositeEventEmitter):
|
||||
keys = await self.keystore.get(str(address))
|
||||
if keys is not None:
|
||||
logger.debug('found keys in the key store')
|
||||
if keys.link_key is None:
|
||||
logger.debug('no link key')
|
||||
return None
|
||||
return keys.link_key.value
|
||||
|
||||
# [Classic only]
|
||||
@@ -2464,25 +2477,24 @@ class Device(CompositeEventEmitter):
|
||||
connection_handle,
|
||||
transport,
|
||||
peer_address,
|
||||
peer_resolvable_address,
|
||||
role,
|
||||
connection_parameters,
|
||||
):
|
||||
logger.debug(
|
||||
f'*** Connection: [0x{connection_handle:04X}] '
|
||||
f'{peer_address} as {HCI_Constant.role_name(role)}'
|
||||
f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}'
|
||||
)
|
||||
if connection_handle in self.connections:
|
||||
logger.warning(
|
||||
'new connection reuses the same handle as a previous connection'
|
||||
)
|
||||
|
||||
peer_resolvable_address = None
|
||||
|
||||
if transport == BT_BR_EDR_TRANSPORT:
|
||||
# Create a new connection
|
||||
connection = self.pending_connections.pop(peer_address)
|
||||
connection.complete(
|
||||
connection_handle, peer_resolvable_address, role, connection_parameters
|
||||
)
|
||||
connection.complete(connection_handle, connection_parameters)
|
||||
self.connections[connection_handle] = connection
|
||||
|
||||
# Emit an event to notify listeners of the new connection
|
||||
@@ -2594,7 +2606,9 @@ class Device(CompositeEventEmitter):
|
||||
# device configuration is set to accept any incoming connection
|
||||
elif self.classic_accept_any:
|
||||
# Save pending connection
|
||||
self.pending_connections[bd_addr] = Connection.incomplete(self, bd_addr)
|
||||
self.pending_connections[bd_addr] = Connection.incomplete(
|
||||
self, bd_addr, BT_PERIPHERAL_ROLE
|
||||
)
|
||||
|
||||
self.host.send_command_sync(
|
||||
HCI_Accept_Connection_Request_Command(
|
||||
|
||||
@@ -94,10 +94,9 @@ HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS = 1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class Connection:
|
||||
def __init__(self, host, handle, role, peer_address, transport):
|
||||
def __init__(self, host, handle, peer_address, transport):
|
||||
self.host = host
|
||||
self.handle = handle
|
||||
self.role = role
|
||||
self.peer_address = peer_address
|
||||
self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu)
|
||||
self.transport = transport
|
||||
@@ -534,7 +533,7 @@ class Host(AbortableEventEmitter):
|
||||
if event.status == HCI_SUCCESS:
|
||||
# Create/update the connection
|
||||
logger.debug(
|
||||
f'### CONNECTION: [0x{event.connection_handle:04X}] '
|
||||
f'### LE CONNECTION: [0x{event.connection_handle:04X}] '
|
||||
f'{event.peer_address} as {HCI_Constant.role_name(event.role)}'
|
||||
)
|
||||
|
||||
@@ -543,7 +542,6 @@ class Host(AbortableEventEmitter):
|
||||
connection = Connection(
|
||||
self,
|
||||
event.connection_handle,
|
||||
event.role,
|
||||
event.peer_address,
|
||||
BT_LE_TRANSPORT,
|
||||
)
|
||||
@@ -560,7 +558,6 @@ class Host(AbortableEventEmitter):
|
||||
event.connection_handle,
|
||||
BT_LE_TRANSPORT,
|
||||
event.peer_address,
|
||||
None,
|
||||
event.role,
|
||||
connection_parameters,
|
||||
)
|
||||
@@ -589,7 +586,6 @@ class Host(AbortableEventEmitter):
|
||||
connection = Connection(
|
||||
self,
|
||||
event.connection_handle,
|
||||
BT_CENTRAL_ROLE,
|
||||
event.bd_addr,
|
||||
BT_BR_EDR_TRANSPORT,
|
||||
)
|
||||
@@ -602,7 +598,6 @@ class Host(AbortableEventEmitter):
|
||||
BT_BR_EDR_TRANSPORT,
|
||||
event.bd_addr,
|
||||
None,
|
||||
BT_CENTRAL_ROLE,
|
||||
None,
|
||||
)
|
||||
else:
|
||||
@@ -622,8 +617,7 @@ class Host(AbortableEventEmitter):
|
||||
if event.status == HCI_SUCCESS:
|
||||
logger.debug(
|
||||
f'### DISCONNECTION: [0x{event.connection_handle:04X}] '
|
||||
f'{connection.peer_address} as '
|
||||
f'{HCI_Constant.role_name(connection.role)}, '
|
||||
f'{connection.peer_address} '
|
||||
f'reason={event.reason}'
|
||||
)
|
||||
del self.connections[event.connection_handle]
|
||||
@@ -739,10 +733,6 @@ class Host(AbortableEventEmitter):
|
||||
f'role change for {event.bd_addr}: '
|
||||
f'{HCI_Constant.role_name(event.new_role)}'
|
||||
)
|
||||
if connection := self.find_connection_by_bd_addr(
|
||||
event.bd_addr, BT_BR_EDR_TRANSPORT
|
||||
):
|
||||
connection.role = event.new_role
|
||||
self.emit('role_change', event.bd_addr, event.new_role)
|
||||
else:
|
||||
logger.debug(
|
||||
|
||||
@@ -273,7 +273,7 @@ class JsonKeyStore(KeyStore):
|
||||
db = await self.load()
|
||||
|
||||
namespace = db.setdefault(self.namespace, {})
|
||||
namespace[name] = keys.to_dict()
|
||||
namespace.setdefault(name, {}).update(keys.to_dict())
|
||||
|
||||
await self.save(db)
|
||||
|
||||
|
||||
@@ -439,7 +439,7 @@ class DLC(EventEmitter):
|
||||
|
||||
logger.debug(
|
||||
f'<<< Credits [{self.dlci}]: '
|
||||
f'received {credits}, total={self.tx_credits}'
|
||||
f'received {received_credits}, total={self.tx_credits}'
|
||||
)
|
||||
data = data[1:]
|
||||
|
||||
|
||||
@@ -553,7 +553,7 @@ class PairingConfig:
|
||||
def __init__(
|
||||
self,
|
||||
sc: bool = True,
|
||||
mitm: bool = True,
|
||||
mitm: bool = False,
|
||||
bonding: bool = True,
|
||||
delegate: Optional[PairingDelegate] = None,
|
||||
) -> None:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"name": "Bumble Hands-Free",
|
||||
"class_of_device": 2360324
|
||||
"class_of_device": 2360324,
|
||||
"keystore": "JsonKeyStore",
|
||||
"le_enabled": false
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ async def test_self_smp_wrong_pin():
|
||||
async def compare_numbers(self, number, digits):
|
||||
return False
|
||||
|
||||
wrong_pin_pairing_config = PairingConfig(delegate=WrongPinDelegate())
|
||||
wrong_pin_pairing_config = PairingConfig(mitm=True, delegate=WrongPinDelegate())
|
||||
paired = False
|
||||
try:
|
||||
await _test_self_smp_with_configs(
|
||||
|
||||
Reference in New Issue
Block a user