forked from auracaster/bumble_mirror
address PR comments
This commit is contained in:
14
apps/pair.py
14
apps/pair.py
@@ -63,13 +63,9 @@ class Delegate(PairingDelegate):
|
|||||||
# We already asked the peer
|
# We already asked the peer
|
||||||
return
|
return
|
||||||
|
|
||||||
# For classic, just use the address
|
|
||||||
if self.mode == 'classic':
|
|
||||||
self.peer_name = str(self.peer.connection.peer_address)
|
|
||||||
else:
|
|
||||||
# Try to get the peer's name
|
# Try to get the peer's name
|
||||||
if self.peer:
|
if self.peer:
|
||||||
peer_name = await get_peer_name(self.peer)
|
peer_name = await get_peer_name(self.peer, self.mode)
|
||||||
self.peer_name = f'{peer_name or ""} [{self.peer.connection.peer_address}]'
|
self.peer_name = f'{peer_name or ""} [{self.peer.connection.peer_address}]'
|
||||||
else:
|
else:
|
||||||
self.peer_name = '[?]'
|
self.peer_name = '[?]'
|
||||||
@@ -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,7 +140,11 @@ class Delegate(PairingDelegate):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def get_peer_name(peer):
|
async def get_peer_name(peer, mode):
|
||||||
|
if mode == 'classic':
|
||||||
|
return await peer.request_name()
|
||||||
|
else:
|
||||||
|
# Try to get the peer name from GATT
|
||||||
services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE)
|
services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE)
|
||||||
if not services:
|
if not services:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user