• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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