1# Copyright 2021-2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# ----------------------------------------------------------------------------- 16# Imports 17# ----------------------------------------------------------------------------- 18import asyncio 19import collections 20import logging 21import struct 22 23from bumble.colors import color 24from bumble.l2cap import L2CAP_PDU 25from bumble.snoop import Snooper 26 27from typing import Optional 28 29from .hci import ( 30 Address, 31 HCI_ACL_DATA_PACKET, 32 HCI_COMMAND_COMPLETE_EVENT, 33 HCI_COMMAND_PACKET, 34 HCI_EVENT_PACKET, 35 HCI_LE_READ_BUFFER_SIZE_COMMAND, 36 HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, 37 HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, 38 HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, 39 HCI_READ_BUFFER_SIZE_COMMAND, 40 HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND, 41 HCI_RESET_COMMAND, 42 HCI_SUCCESS, 43 HCI_SUPPORTED_COMMANDS_FLAGS, 44 HCI_VERSION_BLUETOOTH_CORE_4_0, 45 HCI_AclDataPacket, 46 HCI_AclDataPacketAssembler, 47 HCI_Constant, 48 HCI_Error, 49 HCI_LE_Long_Term_Key_Request_Negative_Reply_Command, 50 HCI_LE_Long_Term_Key_Request_Reply_Command, 51 HCI_LE_Read_Buffer_Size_Command, 52 HCI_LE_Read_Local_Supported_Features_Command, 53 HCI_LE_Read_Suggested_Default_Data_Length_Command, 54 HCI_LE_Remote_Connection_Parameter_Request_Reply_Command, 55 HCI_LE_Set_Event_Mask_Command, 56 HCI_LE_Write_Suggested_Default_Data_Length_Command, 57 HCI_Link_Key_Request_Negative_Reply_Command, 58 HCI_Link_Key_Request_Reply_Command, 59 HCI_Packet, 60 HCI_Read_Buffer_Size_Command, 61 HCI_Read_Local_Supported_Commands_Command, 62 HCI_Read_Local_Version_Information_Command, 63 HCI_Reset_Command, 64 HCI_Set_Event_Mask_Command, 65) 66from .core import ( 67 BT_BR_EDR_TRANSPORT, 68 BT_CENTRAL_ROLE, 69 BT_LE_TRANSPORT, 70 ConnectionPHY, 71 ConnectionParameters, 72) 73from .utils import AbortableEventEmitter 74 75 76# ----------------------------------------------------------------------------- 77# Logging 78# ----------------------------------------------------------------------------- 79logger = logging.getLogger(__name__) 80 81 82# ----------------------------------------------------------------------------- 83# Constants 84# ----------------------------------------------------------------------------- 85# fmt: off 86 87HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH = 27 88HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS = 1 89HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH = 27 90HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS = 1 91 92# fmt: on 93 94 95# ----------------------------------------------------------------------------- 96class Connection: 97 def __init__(self, host, handle, role, peer_address, transport): 98 self.host = host 99 self.handle = handle 100 self.role = role 101 self.peer_address = peer_address 102 self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) 103 self.transport = transport 104 105 def on_hci_acl_data_packet(self, packet): 106 self.assembler.feed_packet(packet) 107 108 def on_acl_pdu(self, pdu): 109 l2cap_pdu = L2CAP_PDU.from_bytes(pdu) 110 self.host.on_l2cap_pdu(self, l2cap_pdu.cid, l2cap_pdu.payload) 111 112 113# ----------------------------------------------------------------------------- 114class Host(AbortableEventEmitter): 115 def __init__(self, controller_source=None, controller_sink=None): 116 super().__init__() 117 118 self.hci_sink = None 119 self.ready = False # True when we can accept incoming packets 120 self.reset_done = False 121 self.connections = {} # Connections, by connection handle 122 self.pending_command = None 123 self.pending_response = None 124 self.hc_le_acl_data_packet_length = HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH 125 self.hc_total_num_le_acl_data_packets = HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS 126 self.hc_acl_data_packet_length = HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH 127 self.hc_total_num_acl_data_packets = HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS 128 self.acl_packet_queue = collections.deque() 129 self.acl_packets_in_flight = 0 130 self.local_version = None 131 self.local_supported_commands = bytes(64) 132 self.local_le_features = 0 133 self.suggested_max_tx_octets = 251 # Max allowed 134 self.suggested_max_tx_time = 2120 # Max allowed 135 self.command_semaphore = asyncio.Semaphore(1) 136 self.long_term_key_provider = None 137 self.link_key_provider = None 138 self.pairing_io_capability_provider = None # Classic only 139 self.snooper = None 140 141 # Connect to the source and sink if specified 142 if controller_source: 143 controller_source.set_packet_sink(self) 144 if controller_sink: 145 self.set_packet_sink(controller_sink) 146 147 def find_connection_by_bd_addr( 148 self, 149 bd_addr: Address, 150 transport: Optional[int] = None, 151 check_address_type: bool = False, 152 ) -> Optional[Connection]: 153 for connection in self.connections.values(): 154 if connection.peer_address.to_bytes() == bd_addr.to_bytes(): 155 if ( 156 check_address_type 157 and connection.peer_address.address_type != bd_addr.address_type 158 ): 159 continue 160 if transport is None or connection.transport == transport: 161 return connection 162 163 return None 164 165 async def flush(self) -> None: 166 # Make sure no command is pending 167 await self.command_semaphore.acquire() 168 169 # Flush current host state, then release command semaphore 170 self.emit('flush') 171 self.command_semaphore.release() 172 173 async def reset(self): 174 if self.ready: 175 self.ready = False 176 await self.flush() 177 178 await self.send_command(HCI_Reset_Command(), check_result=True) 179 self.ready = True 180 181 response = await self.send_command( 182 HCI_Read_Local_Supported_Commands_Command(), check_result=True 183 ) 184 self.local_supported_commands = response.return_parameters.supported_commands 185 186 if self.supports_command(HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND): 187 response = await self.send_command( 188 HCI_LE_Read_Local_Supported_Features_Command(), check_result=True 189 ) 190 self.local_le_features = struct.unpack( 191 '<Q', response.return_parameters.le_features 192 )[0] 193 194 if self.supports_command(HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND): 195 response = await self.send_command( 196 HCI_Read_Local_Version_Information_Command(), check_result=True 197 ) 198 self.local_version = response.return_parameters 199 200 await self.send_command( 201 HCI_Set_Event_Mask_Command(event_mask=bytes.fromhex('FFFFFFFFFFFFFF3F')) 202 ) 203 204 if ( 205 self.local_version is not None 206 and self.local_version.hci_version <= HCI_VERSION_BLUETOOTH_CORE_4_0 207 ): 208 # Some older controllers don't like event masks with bits they don't 209 # understand 210 le_event_mask = bytes.fromhex('1F00000000000000') 211 else: 212 le_event_mask = bytes.fromhex('FFFFF00000000000') 213 214 await self.send_command( 215 HCI_LE_Set_Event_Mask_Command(le_event_mask=le_event_mask) 216 ) 217 218 if self.supports_command(HCI_READ_BUFFER_SIZE_COMMAND): 219 response = await self.send_command( 220 HCI_Read_Buffer_Size_Command(), check_result=True 221 ) 222 self.hc_acl_data_packet_length = ( 223 response.return_parameters.hc_acl_data_packet_length 224 ) 225 self.hc_total_num_acl_data_packets = ( 226 response.return_parameters.hc_total_num_acl_data_packets 227 ) 228 229 logger.debug( 230 'HCI ACL flow control: ' 231 f'hc_acl_data_packet_length={self.hc_acl_data_packet_length},' 232 f'hc_total_num_acl_data_packets={self.hc_total_num_acl_data_packets}' 233 ) 234 235 if self.supports_command(HCI_LE_READ_BUFFER_SIZE_COMMAND): 236 response = await self.send_command( 237 HCI_LE_Read_Buffer_Size_Command(), check_result=True 238 ) 239 self.hc_le_acl_data_packet_length = ( 240 response.return_parameters.hc_le_acl_data_packet_length 241 ) 242 self.hc_total_num_le_acl_data_packets = ( 243 response.return_parameters.hc_total_num_le_acl_data_packets 244 ) 245 246 logger.debug( 247 'HCI LE ACL flow control: ' 248 f'hc_le_acl_data_packet_length={self.hc_le_acl_data_packet_length},' 249 'hc_total_num_le_acl_data_packets=' 250 f'{self.hc_total_num_le_acl_data_packets}' 251 ) 252 253 if ( 254 response.return_parameters.hc_le_acl_data_packet_length == 0 255 or response.return_parameters.hc_total_num_le_acl_data_packets == 0 256 ): 257 # LE and Classic share the same values 258 self.hc_le_acl_data_packet_length = self.hc_acl_data_packet_length 259 self.hc_total_num_le_acl_data_packets = ( 260 self.hc_total_num_acl_data_packets 261 ) 262 263 if self.supports_command( 264 HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND 265 ) and self.supports_command(HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND): 266 response = await self.send_command( 267 HCI_LE_Read_Suggested_Default_Data_Length_Command() 268 ) 269 suggested_max_tx_octets = response.return_parameters.suggested_max_tx_octets 270 suggested_max_tx_time = response.return_parameters.suggested_max_tx_time 271 if ( 272 suggested_max_tx_octets != self.suggested_max_tx_octets 273 or suggested_max_tx_time != self.suggested_max_tx_time 274 ): 275 await self.send_command( 276 HCI_LE_Write_Suggested_Default_Data_Length_Command( 277 suggested_max_tx_octets=self.suggested_max_tx_octets, 278 suggested_max_tx_time=self.suggested_max_tx_time, 279 ) 280 ) 281 282 self.reset_done = True 283 284 @property 285 def controller(self): 286 return self.hci_sink 287 288 @controller.setter 289 def controller(self, controller): 290 self.set_packet_sink(controller) 291 if controller: 292 controller.set_packet_sink(self) 293 294 def set_packet_sink(self, sink): 295 self.hci_sink = sink 296 297 def send_hci_packet(self, packet): 298 if self.snooper: 299 self.snooper.snoop(bytes(packet), Snooper.Direction.HOST_TO_CONTROLLER) 300 301 self.hci_sink.on_packet(packet.to_bytes()) 302 303 async def send_command(self, command, check_result=False): 304 logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: {command}') 305 306 # Wait until we can send (only one pending command at a time) 307 async with self.command_semaphore: 308 assert self.pending_command is None 309 assert self.pending_response is None 310 311 # Create a future value to hold the eventual response 312 self.pending_response = asyncio.get_running_loop().create_future() 313 self.pending_command = command 314 315 try: 316 self.send_hci_packet(command) 317 response = await self.pending_response 318 319 # Check the return parameters if required 320 if check_result: 321 if isinstance(response.return_parameters, int): 322 status = response.return_parameters 323 elif isinstance(response.return_parameters, bytes): 324 # return parameters first field is a one byte status code 325 status = response.return_parameters[0] 326 else: 327 status = response.return_parameters.status 328 329 if status != HCI_SUCCESS: 330 logger.warning( 331 f'{command.name} failed ({HCI_Constant.error_name(status)})' 332 ) 333 raise HCI_Error(status) 334 335 return response 336 except Exception as error: 337 logger.warning( 338 f'{color("!!! Exception while sending HCI packet:", "red")} {error}' 339 ) 340 raise error 341 finally: 342 self.pending_command = None 343 self.pending_response = None 344 345 # Use this method to send a command from a task 346 def send_command_sync(self, command): 347 async def send_command(command): 348 await self.send_command(command) 349 350 asyncio.create_task(send_command(command)) 351 352 def send_l2cap_pdu(self, connection_handle, cid, pdu): 353 l2cap_pdu = L2CAP_PDU(cid, pdu).to_bytes() 354 355 # Send the data to the controller via ACL packets 356 bytes_remaining = len(l2cap_pdu) 357 offset = 0 358 pb_flag = 0 359 while bytes_remaining: 360 # TODO: support different LE/Classic lengths 361 data_total_length = min(bytes_remaining, self.hc_le_acl_data_packet_length) 362 acl_packet = HCI_AclDataPacket( 363 connection_handle=connection_handle, 364 pb_flag=pb_flag, 365 bc_flag=0, 366 data_total_length=data_total_length, 367 data=l2cap_pdu[offset : offset + data_total_length], 368 ) 369 logger.debug( 370 f'{color("### HOST -> CONTROLLER", "blue")}: (CID={cid}) {acl_packet}' 371 ) 372 self.queue_acl_packet(acl_packet) 373 pb_flag = 1 374 offset += data_total_length 375 bytes_remaining -= data_total_length 376 377 def queue_acl_packet(self, acl_packet): 378 self.acl_packet_queue.appendleft(acl_packet) 379 self.check_acl_packet_queue() 380 381 if len(self.acl_packet_queue): 382 logger.debug( 383 f'{self.acl_packets_in_flight} ACL packets in flight, ' 384 f'{len(self.acl_packet_queue)} in queue' 385 ) 386 387 def check_acl_packet_queue(self): 388 # Send all we can (TODO: support different LE/Classic limits) 389 while ( 390 len(self.acl_packet_queue) > 0 391 and self.acl_packets_in_flight < self.hc_total_num_le_acl_data_packets 392 ): 393 packet = self.acl_packet_queue.pop() 394 self.send_hci_packet(packet) 395 self.acl_packets_in_flight += 1 396 397 def supports_command(self, command): 398 # Find the support flag position for this command 399 for (octet, flags) in enumerate(HCI_SUPPORTED_COMMANDS_FLAGS): 400 for (flag_position, value) in enumerate(flags): 401 if value == command: 402 # Check if the flag is set 403 if octet < len(self.local_supported_commands) and flag_position < 8: 404 return ( 405 self.local_supported_commands[octet] & (1 << flag_position) 406 ) != 0 407 408 return False 409 410 @property 411 def supported_commands(self): 412 commands = [] 413 for (octet, flags) in enumerate(self.local_supported_commands): 414 if octet < len(HCI_SUPPORTED_COMMANDS_FLAGS): 415 for flag in range(8): 416 if flags & (1 << flag) != 0: 417 command = HCI_SUPPORTED_COMMANDS_FLAGS[octet][flag] 418 if command is not None: 419 commands.append(command) 420 421 return commands 422 423 def supports_le_feature(self, feature): 424 return (self.local_le_features & (1 << feature)) != 0 425 426 @property 427 def supported_le_features(self): 428 return [ 429 feature for feature in range(64) if self.local_le_features & (1 << feature) 430 ] 431 432 # Packet Sink protocol (packets coming from the controller via HCI) 433 def on_packet(self, packet): 434 hci_packet = HCI_Packet.from_bytes(packet) 435 if self.ready or ( 436 hci_packet.hci_packet_type == HCI_EVENT_PACKET 437 and hci_packet.event_code == HCI_COMMAND_COMPLETE_EVENT 438 and hci_packet.command_opcode == HCI_RESET_COMMAND 439 ): 440 self.on_hci_packet(hci_packet) 441 else: 442 logger.debug('reset not done, ignoring packet from controller') 443 444 def on_hci_packet(self, packet): 445 logger.debug(f'{color("### CONTROLLER -> HOST", "green")}: {packet}') 446 447 if self.snooper: 448 self.snooper.snoop(bytes(packet), Snooper.Direction.CONTROLLER_TO_HOST) 449 450 # If the packet is a command, invoke the handler for this packet 451 if packet.hci_packet_type == HCI_COMMAND_PACKET: 452 self.on_hci_command_packet(packet) 453 elif packet.hci_packet_type == HCI_EVENT_PACKET: 454 self.on_hci_event_packet(packet) 455 elif packet.hci_packet_type == HCI_ACL_DATA_PACKET: 456 self.on_hci_acl_data_packet(packet) 457 else: 458 logger.warning(f'!!! unknown packet type {packet.hci_packet_type}') 459 460 def on_hci_command_packet(self, command): 461 logger.warning(f'!!! unexpected command packet: {command}') 462 463 def on_hci_event_packet(self, event): 464 handler_name = f'on_{event.name.lower()}' 465 handler = getattr(self, handler_name, self.on_hci_event) 466 handler(event) 467 468 def on_hci_acl_data_packet(self, packet): 469 # Look for the connection to which this data belongs 470 if connection := self.connections.get(packet.connection_handle): 471 connection.on_hci_acl_data_packet(packet) 472 473 def on_l2cap_pdu(self, connection, cid, pdu): 474 self.emit('l2cap_pdu', connection.handle, cid, pdu) 475 476 def on_command_processed(self, event): 477 if self.pending_response: 478 # Check that it is what we were expecting 479 if self.pending_command.op_code != event.command_opcode: 480 logger.warning( 481 '!!! command result mismatch, expected ' 482 f'0x{self.pending_command.op_code:X} but got ' 483 f'0x{event.command_opcode:X}' 484 ) 485 486 self.pending_response.set_result(event) 487 else: 488 logger.warning('!!! no pending response future to set') 489 490 ############################################################ 491 # HCI handlers 492 ############################################################ 493 def on_hci_event(self, event): 494 logger.warning(f'{color(f"--- Ignoring event {event}", "red")}') 495 496 def on_hci_command_complete_event(self, event): 497 if event.command_opcode == 0: 498 # This is used just for the Num_HCI_Command_Packets field, not related to 499 # an actual command 500 logger.debug('no-command event') 501 return None 502 503 return self.on_command_processed(event) 504 505 def on_hci_command_status_event(self, event): 506 return self.on_command_processed(event) 507 508 def on_hci_number_of_completed_packets_event(self, event): 509 total_packets = sum(event.num_completed_packets) 510 if total_packets <= self.acl_packets_in_flight: 511 self.acl_packets_in_flight -= total_packets 512 self.check_acl_packet_queue() 513 else: 514 logger.warning( 515 color( 516 '!!! {total_packets} completed but only ' 517 f'{self.acl_packets_in_flight} in flight' 518 ) 519 ) 520 self.acl_packets_in_flight = 0 521 522 # Classic only 523 def on_hci_connection_request_event(self, event): 524 # Notify the listeners 525 self.emit( 526 'connection_request', 527 event.bd_addr, 528 event.class_of_device, 529 event.link_type, 530 ) 531 532 def on_hci_le_connection_complete_event(self, event): 533 # Check if this is a cancellation 534 if event.status == HCI_SUCCESS: 535 # Create/update the connection 536 logger.debug( 537 f'### CONNECTION: [0x{event.connection_handle:04X}] ' 538 f'{event.peer_address} as {HCI_Constant.role_name(event.role)}' 539 ) 540 541 connection = self.connections.get(event.connection_handle) 542 if connection is None: 543 connection = Connection( 544 self, 545 event.connection_handle, 546 event.role, 547 event.peer_address, 548 BT_LE_TRANSPORT, 549 ) 550 self.connections[event.connection_handle] = connection 551 552 # Notify the client 553 connection_parameters = ConnectionParameters( 554 event.connection_interval, 555 event.peripheral_latency, 556 event.supervision_timeout, 557 ) 558 self.emit( 559 'connection', 560 event.connection_handle, 561 BT_LE_TRANSPORT, 562 event.peer_address, 563 None, 564 event.role, 565 connection_parameters, 566 ) 567 else: 568 logger.debug(f'### CONNECTION FAILED: {event.status}') 569 570 # Notify the listeners 571 self.emit( 572 'connection_failure', BT_LE_TRANSPORT, event.peer_address, event.status 573 ) 574 575 def on_hci_le_enhanced_connection_complete_event(self, event): 576 # Just use the same implementation as for the non-enhanced event for now 577 self.on_hci_le_connection_complete_event(event) 578 579 def on_hci_connection_complete_event(self, event): 580 if event.status == HCI_SUCCESS: 581 # Create/update the connection 582 logger.debug( 583 f'### BR/EDR CONNECTION: [0x{event.connection_handle:04X}] ' 584 f'{event.bd_addr}' 585 ) 586 587 connection = self.connections.get(event.connection_handle) 588 if connection is None: 589 connection = Connection( 590 self, 591 event.connection_handle, 592 BT_CENTRAL_ROLE, 593 event.bd_addr, 594 BT_BR_EDR_TRANSPORT, 595 ) 596 self.connections[event.connection_handle] = connection 597 598 # Notify the client 599 self.emit( 600 'connection', 601 event.connection_handle, 602 BT_BR_EDR_TRANSPORT, 603 event.bd_addr, 604 None, 605 BT_CENTRAL_ROLE, 606 None, 607 ) 608 else: 609 logger.debug(f'### BR/EDR CONNECTION FAILED: {event.status}') 610 611 # Notify the client 612 self.emit( 613 'connection_failure', BT_BR_EDR_TRANSPORT, event.bd_addr, event.status 614 ) 615 616 def on_hci_disconnection_complete_event(self, event): 617 # Find the connection 618 if (connection := self.connections.get(event.connection_handle)) is None: 619 logger.warning('!!! DISCONNECTION COMPLETE: unknown handle') 620 return 621 622 if event.status == HCI_SUCCESS: 623 logger.debug( 624 f'### DISCONNECTION: [0x{event.connection_handle:04X}] ' 625 f'{connection.peer_address} as ' 626 f'{HCI_Constant.role_name(connection.role)}, ' 627 f'reason={event.reason}' 628 ) 629 del self.connections[event.connection_handle] 630 631 # Notify the listeners 632 self.emit('disconnection', event.connection_handle, event.reason) 633 else: 634 logger.debug(f'### DISCONNECTION FAILED: {event.status}') 635 636 # Notify the listeners 637 self.emit('disconnection_failure', event.connection_handle, event.status) 638 639 def on_hci_le_connection_update_complete_event(self, event): 640 if (connection := self.connections.get(event.connection_handle)) is None: 641 logger.warning('!!! CONNECTION PARAMETERS UPDATE COMPLETE: unknown handle') 642 return 643 644 # Notify the client 645 if event.status == HCI_SUCCESS: 646 connection_parameters = ConnectionParameters( 647 event.connection_interval, 648 event.peripheral_latency, 649 event.supervision_timeout, 650 ) 651 self.emit( 652 'connection_parameters_update', connection.handle, connection_parameters 653 ) 654 else: 655 self.emit( 656 'connection_parameters_update_failure', connection.handle, event.status 657 ) 658 659 def on_hci_le_phy_update_complete_event(self, event): 660 if (connection := self.connections.get(event.connection_handle)) is None: 661 logger.warning('!!! CONNECTION PHY UPDATE COMPLETE: unknown handle') 662 return 663 664 # Notify the client 665 if event.status == HCI_SUCCESS: 666 connection_phy = ConnectionPHY(event.tx_phy, event.rx_phy) 667 self.emit('connection_phy_update', connection.handle, connection_phy) 668 else: 669 self.emit('connection_phy_update_failure', connection.handle, event.status) 670 671 def on_hci_le_advertising_report_event(self, event): 672 for report in event.reports: 673 self.emit('advertising_report', report) 674 675 def on_hci_le_extended_advertising_report_event(self, event): 676 self.on_hci_le_advertising_report_event(event) 677 678 def on_hci_le_remote_connection_parameter_request_event(self, event): 679 if event.connection_handle not in self.connections: 680 logger.warning('!!! REMOTE CONNECTION PARAMETER REQUEST: unknown handle') 681 return 682 683 # For now, just accept everything 684 # TODO: delegate the decision 685 self.send_command_sync( 686 HCI_LE_Remote_Connection_Parameter_Request_Reply_Command( 687 connection_handle=event.connection_handle, 688 interval_min=event.interval_min, 689 interval_max=event.interval_max, 690 max_latency=event.max_latency, 691 timeout=event.timeout, 692 min_ce_length=0, 693 max_ce_length=0, 694 ) 695 ) 696 697 def on_hci_le_long_term_key_request_event(self, event): 698 if (connection := self.connections.get(event.connection_handle)) is None: 699 logger.warning('!!! LE LONG TERM KEY REQUEST: unknown handle') 700 return 701 702 async def send_long_term_key(): 703 if self.long_term_key_provider is None: 704 logger.debug('no long term key provider') 705 long_term_key = None 706 else: 707 long_term_key = await self.abort_on( 708 'flush', 709 # pylint: disable-next=not-callable 710 self.long_term_key_provider( 711 connection.handle, 712 event.random_number, 713 event.encryption_diversifier, 714 ), 715 ) 716 if long_term_key: 717 response = HCI_LE_Long_Term_Key_Request_Reply_Command( 718 connection_handle=event.connection_handle, 719 long_term_key=long_term_key, 720 ) 721 else: 722 response = HCI_LE_Long_Term_Key_Request_Negative_Reply_Command( 723 connection_handle=event.connection_handle 724 ) 725 726 await self.send_command(response) 727 728 asyncio.create_task(send_long_term_key()) 729 730 def on_hci_synchronous_connection_complete_event(self, event): 731 pass 732 733 def on_hci_synchronous_connection_changed_event(self, event): 734 pass 735 736 def on_hci_role_change_event(self, event): 737 if event.status == HCI_SUCCESS: 738 logger.debug( 739 f'role change for {event.bd_addr}: ' 740 f'{HCI_Constant.role_name(event.new_role)}' 741 ) 742 if connection := self.find_connection_by_bd_addr( 743 event.bd_addr, BT_BR_EDR_TRANSPORT 744 ): 745 connection.role = event.new_role 746 self.emit('role_change', event.bd_addr, event.new_role) 747 else: 748 logger.debug( 749 f'role change for {event.bd_addr} failed: ' 750 f'{HCI_Constant.error_name(event.status)}' 751 ) 752 self.emit('role_change_failure', event.bd_addr, event.status) 753 754 def on_hci_le_data_length_change_event(self, event): 755 self.emit( 756 'connection_data_length_change', 757 event.connection_handle, 758 event.max_tx_octets, 759 event.max_tx_time, 760 event.max_rx_octets, 761 event.max_rx_time, 762 ) 763 764 def on_hci_authentication_complete_event(self, event): 765 # Notify the client 766 if event.status == HCI_SUCCESS: 767 self.emit('connection_authentication', event.connection_handle) 768 else: 769 self.emit( 770 'connection_authentication_failure', 771 event.connection_handle, 772 event.status, 773 ) 774 775 def on_hci_encryption_change_event(self, event): 776 # Notify the client 777 if event.status == HCI_SUCCESS: 778 self.emit( 779 'connection_encryption_change', 780 event.connection_handle, 781 event.encryption_enabled, 782 ) 783 else: 784 self.emit( 785 'connection_encryption_failure', event.connection_handle, event.status 786 ) 787 788 def on_hci_encryption_key_refresh_complete_event(self, event): 789 # Notify the client 790 if event.status == HCI_SUCCESS: 791 self.emit('connection_encryption_key_refresh', event.connection_handle) 792 else: 793 self.emit( 794 'connection_encryption_key_refresh_failure', 795 event.connection_handle, 796 event.status, 797 ) 798 799 def on_hci_link_supervision_timeout_changed_event(self, event): 800 pass 801 802 def on_hci_max_slots_change_event(self, event): 803 pass 804 805 def on_hci_page_scan_repetition_mode_change_event(self, event): 806 pass 807 808 def on_hci_link_key_notification_event(self, event): 809 logger.debug( 810 f'link key for {event.bd_addr}: {event.link_key.hex()}, ' 811 f'type={HCI_Constant.link_key_type_name(event.key_type)}' 812 ) 813 self.emit('link_key', event.bd_addr, event.link_key, event.key_type) 814 815 def on_hci_simple_pairing_complete_event(self, event): 816 logger.debug( 817 f'simple pairing complete for {event.bd_addr}: ' 818 f'status={HCI_Constant.status_name(event.status)}' 819 ) 820 821 def on_hci_pin_code_request_event(self, event): 822 self.emit('pin_code_request', event.bd_addr) 823 824 def on_hci_link_key_request_event(self, event): 825 async def send_link_key(): 826 if self.link_key_provider is None: 827 logger.debug('no link key provider') 828 link_key = None 829 else: 830 link_key = await self.abort_on( 831 'flush', 832 # pylint: disable-next=not-callable 833 self.link_key_provider(event.bd_addr), 834 ) 835 if link_key: 836 response = HCI_Link_Key_Request_Reply_Command( 837 bd_addr=event.bd_addr, link_key=link_key 838 ) 839 else: 840 response = HCI_Link_Key_Request_Negative_Reply_Command( 841 bd_addr=event.bd_addr 842 ) 843 844 await self.send_command(response) 845 846 asyncio.create_task(send_link_key()) 847 848 def on_hci_io_capability_request_event(self, event): 849 self.emit('authentication_io_capability_request', event.bd_addr) 850 851 def on_hci_io_capability_response_event(self, event): 852 pass 853 854 def on_hci_user_confirmation_request_event(self, event): 855 self.emit( 856 'authentication_user_confirmation_request', 857 event.bd_addr, 858 event.numeric_value, 859 ) 860 861 def on_hci_user_passkey_request_event(self, event): 862 self.emit('authentication_user_passkey_request', event.bd_addr) 863 864 def on_hci_user_passkey_notification_event(self, event): 865 self.emit( 866 'authentication_user_passkey_notification', event.bd_addr, event.passkey 867 ) 868 869 def on_hci_inquiry_complete_event(self, _event): 870 self.emit('inquiry_complete') 871 872 def on_hci_inquiry_result_with_rssi_event(self, event): 873 for response in event.responses: 874 self.emit( 875 'inquiry_result', 876 response.bd_addr, 877 response.class_of_device, 878 b'', 879 response.rssi, 880 ) 881 882 def on_hci_extended_inquiry_result_event(self, event): 883 self.emit( 884 'inquiry_result', 885 event.bd_addr, 886 event.class_of_device, 887 event.extended_inquiry_response, 888 event.rssi, 889 ) 890 891 def on_hci_remote_name_request_complete_event(self, event): 892 if event.status != HCI_SUCCESS: 893 self.emit('remote_name_failure', event.bd_addr, event.status) 894 else: 895 self.emit('remote_name', event.bd_addr, event.remote_name) 896 897 def on_hci_remote_host_supported_features_notification_event(self, event): 898 self.emit( 899 'remote_host_supported_features', 900 event.bd_addr, 901 event.host_supported_features, 902 ) 903