address PR comments

This commit is contained in:
Gilles Boccon-Gibod
2022-06-13 16:44:57 -07:00
parent 70dca1d7c9
commit 250c1e3395
4 changed files with 92 additions and 21 deletions

View File

@@ -63,16 +63,12 @@ class Delegate(PairingDelegate):
# We already asked the peer # We already asked the peer
return return
# For classic, just use the address # Try to get the peer's name
if self.mode == 'classic': if self.peer:
self.peer_name = str(self.peer.connection.peer_address) peer_name = await get_peer_name(self.peer, self.mode)
self.peer_name = f'{peer_name or ""} [{self.peer.connection.peer_address}]'
else: else:
# Try to get the peer's name self.peer_name = '[?]'
if self.peer:
peer_name = await get_peer_name(self.peer)
self.peer_name = f'{peer_name or ""} [{self.peer.connection.peer_address}]'
else:
self.peer_name = '[?]'
async def accept(self): async def accept(self):
if self.prompt: if self.prompt:
@@ -107,7 +103,7 @@ class Delegate(PairingDelegate):
print(color(f'### Pairing with {self.peer_name}', 'yellow')) print(color(f'### Pairing with {self.peer_name}', 'yellow'))
print(color('###-----------------------------------', 'yellow')) print(color('###-----------------------------------', 'yellow'))
while True: while True:
response = await aioconsole.ainput(color(f'>>> Does the other device display {number:{digits}}? ', 'yellow')) response = await aioconsole.ainput(color(f'>>> Does the other device display {number:0{digits}}? ', 'yellow'))
response = response.lower().strip() response = response.lower().strip()
if response == 'yes': if response == 'yes':
return True return True
@@ -144,14 +140,18 @@ class Delegate(PairingDelegate):
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
async def get_peer_name(peer): async def get_peer_name(peer, mode):
services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE) if mode == 'classic':
if not services: return await peer.request_name()
return None else:
# Try to get the peer name from GATT
services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE)
if not services:
return None
values = await peer.read_characteristics_by_uuid(GATT_DEVICE_NAME_CHARACTERISTIC, services[0]) values = await peer.read_characteristics_by_uuid(GATT_DEVICE_NAME_CHARACTERISTIC, services[0])
if values: if values:
return values[0].decode('utf-8') return values[0].decode('utf-8')
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View File

@@ -137,6 +137,10 @@ class Peer:
def get_characteristics_by_uuid(self, uuid, service = None): def get_characteristics_by_uuid(self, uuid, service = None):
return self.gatt_client.get_characteristics_by_uuid(uuid, service) return self.gatt_client.get_characteristics_by_uuid(uuid, service)
# [Classic only]
async def request_name(self):
return await self.connection.request_remote_name()
def __str__(self): def __str__(self):
return f'{self.connection.peer_address} as {self.connection.role_name}' return f'{self.connection.peer_address} as {self.connection.role_name}'
@@ -176,6 +180,7 @@ class Connection(CompositeEventEmitter):
self.transport = transport self.transport = transport
self.peer_address = peer_address self.peer_address = peer_address
self.peer_resolvable_address = peer_resolvable_address self.peer_resolvable_address = peer_resolvable_address
self.peer_name = None # Classic only
self.role = role self.role = role
self.parameters = parameters self.parameters = parameters
self.encryption = 0 self.encryption = 0
@@ -231,6 +236,10 @@ class Connection(CompositeEventEmitter):
supervision_timeout supervision_timeout
) )
# [Classic only]
async def request_remote_name(self):
return await self.device.request_remote_name(self)
def __str__(self): def __str__(self):
return f'Connection(handle=0x{self.handle:04X}, role={self.role_name}, address={self.peer_address})' return f'Connection(handle=0x{self.handle:04X}, role={self.role_name}, address={self.peer_address})'
@@ -290,8 +299,7 @@ def with_connection_from_handle(function):
@functools.wraps(function) @functools.wraps(function)
def wrapper(self, connection_handle, *args, **kwargs): def wrapper(self, connection_handle, *args, **kwargs):
if (connection := self.lookup_connection(connection_handle)) is None: if (connection := self.lookup_connection(connection_handle)) is None:
logger.warn(f'no connection found for handle 0x{connection_handle:04X}') raise ValueError('no connection for handle')
return
return function(self, connection, *args, **kwargs) return function(self, connection, *args, **kwargs)
return wrapper return wrapper
@@ -303,7 +311,7 @@ def with_connection_from_address(function):
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)
logger.warn(f'no connection found for address {address}') raise ValueError('no connection for address')
return wrapper return wrapper
@@ -1044,6 +1052,40 @@ class Device(CompositeEventEmitter):
connection.remove_listener('connection_encryption_change', on_encryption_change) connection.remove_listener('connection_encryption_change', on_encryption_change)
connection.remove_listener('connection_encryption_failure', on_encryption_failure) connection.remove_listener('connection_encryption_failure', on_encryption_failure)
# [Classic only]
async def request_remote_name(self, connection):
# Set up event handlers
pending_name = asyncio.get_running_loop().create_future()
def on_remote_name():
pending_name.set_result(connection.peer_name)
def on_remote_name_failure(error_code):
pending_name.set_exception(HCI_Error(error_code))
connection.on('remote_name', on_remote_name)
connection.on('remote_name_failure', on_remote_name_failure)
try:
result = await self.send_command(
HCI_Remote_Name_Request_Command(
bd_addr = connection.peer_address,
page_scan_repetition_mode = HCI_Remote_Name_Request_Command.R0, # TODO investigate other options
reserved = 0,
clock_offset = 0 # TODO investigate non-0 values
)
)
if result.status != HCI_COMMAND_STATUS_PENDING:
logger.warn(f'HCI_Set_Connection_Encryption_Command failed: {HCI_Constant.error_name(result.status)}')
raise HCI_Error(result.status)
# Wait for the result
return await pending_name
finally:
connection.remove_listener('remote_name', on_remote_name)
connection.remove_listener('remote_name_failure', on_remote_name_failure)
# [Classic only] # [Classic only]
@host_event_handler @host_event_handler
def on_link_key(self, bd_addr, link_key, key_type): def on_link_key(self, bd_addr, link_key, key_type):
@@ -1188,10 +1230,12 @@ class Device(CompositeEventEmitter):
# Compute the authentication requirements # Compute the authentication requirements
authentication_requirements = ( authentication_requirements = (
# No Bonding
( (
HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS, HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS,
HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS
), ),
# General Bonding
( (
HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS, HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS,
HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS
@@ -1203,7 +1247,7 @@ class Device(CompositeEventEmitter):
HCI_IO_Capability_Request_Reply_Command( HCI_IO_Capability_Request_Reply_Command(
bd_addr = connection.peer_address, bd_addr = connection.peer_address,
io_capability = io_capability, io_capability = io_capability,
oob_data_present = 0x00, oob_data_present = 0x00, # Not present
authentication_requirements = authentication_requirements authentication_requirements = authentication_requirements
) )
) )
@@ -1272,6 +1316,24 @@ class Device(CompositeEventEmitter):
HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address) HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address)
) )
# [Classic only]
@host_event_handler
@with_connection_from_address
def on_remote_name(self, connection, remote_name):
# Try to decode the name
try:
connection.peer_name = remote_name.decode('utf-8')
connection.emit('remote_name')
except UnicodeDecodeError as error:
logger.warning('peer name is not valid UTF-8')
connection.emit('remote_name_failure', error)
# [Classic only]
@host_event_handler
@with_connection_from_address
def on_remote_name_failure(self, connection, error):
connection.emit('remote_name_failure', error)
@host_event_handler @host_event_handler
@with_connection_from_handle @with_connection_from_handle
def on_connection_encryption_change(self, connection, encryption): def on_connection_encryption_change(self, connection, encryption):

View File

@@ -1378,6 +1378,9 @@ class HCI_Remote_Name_Request_Command(HCI_Command):
''' '''
See Bluetooth spec @ 7.1.19 Remote Name Request Command See Bluetooth spec @ 7.1.19 Remote Name Request Command
''' '''
R0 = 0x00
R1 = 0x01
R2 = 0x02
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View File

@@ -593,3 +593,9 @@ class Host(EventEmitter):
event.extended_inquiry_response, event.extended_inquiry_response,
event.rssi event.rssi
) )
def on_hci_remote_name_request_complete_event(self, event):
if event.status != HCI_SUCCESS:
self.emit('remote_name_failure', event.bd_addr, event.status)
else:
self.emit('remote_name', event.bd_addr, event.remote_name)