• 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# -----------------------------------------------------------------------------
18from __future__ import annotations
19import struct
20from typing import List, Optional, Tuple, Union, cast
21
22from .company_ids import COMPANY_IDENTIFIERS
23
24
25# -----------------------------------------------------------------------------
26# Constants
27# -----------------------------------------------------------------------------
28# fmt: off
29
30BT_CENTRAL_ROLE    = 0
31BT_PERIPHERAL_ROLE = 1
32
33BT_BR_EDR_TRANSPORT = 0
34BT_LE_TRANSPORT     = 1
35
36
37# fmt: on
38
39
40# -----------------------------------------------------------------------------
41# Utils
42# -----------------------------------------------------------------------------
43def bit_flags_to_strings(bits, bit_flag_names):
44    names = []
45    index = 0
46    while bits != 0:
47        if bits & 1:
48            name = bit_flag_names[index] if index < len(bit_flag_names) else f'#{index}'
49            names.append(name)
50        bits >>= 1
51        index += 1
52
53    return names
54
55
56def name_or_number(dictionary, number, width=2):
57    name = dictionary.get(number)
58    if name is not None:
59        return name
60    return f'[0x{number:0{width}X}]'
61
62
63def padded_bytes(buffer, size):
64    padding_size = max(size - len(buffer), 0)
65    return buffer + bytes(padding_size)
66
67
68def get_dict_key_by_value(dictionary, value):
69    for key, val in dictionary.items():
70        if val == value:
71            return key
72    return None
73
74
75# -----------------------------------------------------------------------------
76# Exceptions
77# -----------------------------------------------------------------------------
78class BaseError(Exception):
79    """Base class for errors with an error code, error name and namespace"""
80
81    def __init__(self, error_code, error_namespace='', error_name='', details=''):
82        super().__init__()
83        self.error_code = error_code
84        self.error_namespace = error_namespace
85        self.error_name = error_name
86        self.details = details
87
88    def __str__(self):
89        if self.error_namespace:
90            namespace = f'{self.error_namespace}/'
91        else:
92            namespace = ''
93        if self.error_name:
94            name = f'{self.error_name} [0x{self.error_code:X}]'
95        else:
96            name = f'0x{self.error_code:X}'
97
98        return f'{type(self).__name__}({namespace}{name})'
99
100
101class ProtocolError(BaseError):
102    """Protocol Error"""
103
104
105class TimeoutError(Exception):  # pylint: disable=redefined-builtin
106    """Timeout Error"""
107
108
109class CommandTimeoutError(Exception):
110    """Command Timeout Error"""
111
112
113class InvalidStateError(Exception):
114    """Invalid State Error"""
115
116
117class ConnectionError(BaseError):  # pylint: disable=redefined-builtin
118    """Connection Error"""
119
120    FAILURE = 0x01
121    CONNECTION_REFUSED = 0x02
122
123    def __init__(
124        self,
125        error_code,
126        transport,
127        peer_address,
128        error_namespace='',
129        error_name='',
130        details='',
131    ):
132        super().__init__(error_code, error_namespace, error_name, details)
133        self.transport = transport
134        self.peer_address = peer_address
135
136
137# -----------------------------------------------------------------------------
138# UUID
139#
140# NOTE: the internal byte representation is in little-endian byte order
141#
142# Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
143# -----------------------------------------------------------------------------
144class UUID:
145    '''
146    See Bluetooth spec Vol 3, Part B - 2.5.1 UUID
147
148    Note that this class expects and works in little-endian byte-order throughout.
149    The exception is when interacting with strings, which are in big-endian byte-order.
150    '''
151
152    BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1]  # little-endian
153    UUIDS: List[UUID] = []  # Registry of all instances created
154
155    def __init__(self, uuid_str_or_int, name=None):
156        if isinstance(uuid_str_or_int, int):
157            self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
158        else:
159            if len(uuid_str_or_int) == 36:
160                if (
161                    uuid_str_or_int[8] != '-'
162                    or uuid_str_or_int[13] != '-'
163                    or uuid_str_or_int[18] != '-'
164                    or uuid_str_or_int[23] != '-'
165                ):
166                    raise ValueError('invalid UUID format')
167                uuid_str = uuid_str_or_int.replace('-', '')
168            else:
169                uuid_str = uuid_str_or_int
170            if len(uuid_str) != 32 and len(uuid_str) != 8 and len(uuid_str) != 4:
171                raise ValueError(f"invalid UUID format: {uuid_str}")
172            self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str)))
173        self.name = name
174
175    def register(self):
176        # Register this object in the class registry, and update the entry's name if
177        # it wasn't set already
178        for uuid in self.UUIDS:
179            if self == uuid:
180                if uuid.name is None:
181                    uuid.name = self.name
182                return uuid
183
184        self.UUIDS.append(self)
185        return self
186
187    @classmethod
188    def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID:
189        if len(uuid_bytes) in (2, 4, 16):
190            self = cls.__new__(cls)
191            self.uuid_bytes = uuid_bytes
192            self.name = name
193
194            return self.register()
195
196        raise ValueError('only 2, 4 and 16 bytes are allowed')
197
198    @classmethod
199    def from_16_bits(cls, uuid_16, name=None):
200        return cls.from_bytes(struct.pack('<H', uuid_16), name)
201
202    @classmethod
203    def from_32_bits(cls, uuid_32, name=None):
204        return cls.from_bytes(struct.pack('<I', uuid_32), name)
205
206    @classmethod
207    def parse_uuid(cls, uuid_as_bytes, offset):
208        return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:])
209
210    @classmethod
211    def parse_uuid_2(cls, uuid_as_bytes, offset):
212        return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2])
213
214    def to_bytes(self, force_128=False):
215        '''
216        Serialize UUID in little-endian byte-order
217        '''
218        if not force_128:
219            return self.uuid_bytes
220
221        if len(self.uuid_bytes) == 2:
222            return self.BASE_UUID + self.uuid_bytes + bytes([0, 0])
223        elif len(self.uuid_bytes) == 4:
224            return self.BASE_UUID + self.uuid_bytes
225        elif len(self.uuid_bytes) == 16:
226            return self.uuid_bytes
227        else:
228            assert False, "unreachable"
229
230    def to_pdu_bytes(self):
231        '''
232        Convert to bytes for use in an ATT PDU.
233        According to Vol 3, Part F - 3.2.1 Attribute Type:
234        "All 32-bit Attribute UUIDs shall be converted to 128-bit UUIDs when the
235         Attribute UUID is contained in an ATT PDU."
236        '''
237        return self.to_bytes(force_128=(len(self.uuid_bytes) == 4))
238
239    def to_hex_str(self) -> str:
240        if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4:
241            return bytes(reversed(self.uuid_bytes)).hex().upper()
242
243        return ''.join(
244            [
245                bytes(reversed(self.uuid_bytes[12:16])).hex(),
246                bytes(reversed(self.uuid_bytes[10:12])).hex(),
247                bytes(reversed(self.uuid_bytes[8:10])).hex(),
248                bytes(reversed(self.uuid_bytes[6:8])).hex(),
249                bytes(reversed(self.uuid_bytes[0:6])).hex(),
250            ]
251        ).upper()
252
253    def __bytes__(self):
254        return self.to_bytes()
255
256    def __eq__(self, other):
257        if isinstance(other, UUID):
258            return self.to_bytes(force_128=True) == other.to_bytes(force_128=True)
259
260        if isinstance(other, str):
261            return UUID(other) == self
262
263        return False
264
265    def __hash__(self):
266        return hash(self.uuid_bytes)
267
268    def __str__(self):
269        if len(self.uuid_bytes) == 2:
270            uuid = struct.unpack('<H', self.uuid_bytes)[0]
271            result = f'UUID-16:{uuid:04X}'
272        elif len(self.uuid_bytes) == 4:
273            uuid = struct.unpack('<I', self.uuid_bytes)[0]
274            result = f'UUID-32:{uuid:08X}'
275        else:
276            result = '-'.join(
277                [
278                    bytes(reversed(self.uuid_bytes[12:16])).hex(),
279                    bytes(reversed(self.uuid_bytes[10:12])).hex(),
280                    bytes(reversed(self.uuid_bytes[8:10])).hex(),
281                    bytes(reversed(self.uuid_bytes[6:8])).hex(),
282                    bytes(reversed(self.uuid_bytes[0:6])).hex(),
283                ]
284            ).upper()
285
286        if self.name is not None:
287            return result + f' ({self.name})'
288
289        return result
290
291    def __repr__(self):
292        return str(self)
293
294
295# -----------------------------------------------------------------------------
296# Common UUID constants
297# -----------------------------------------------------------------------------
298# fmt: off
299# pylint: disable=line-too-long
300
301# Protocol Identifiers
302BT_SDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0001, 'SDP')
303BT_UDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0002, 'UDP')
304BT_RFCOMM_PROTOCOL_ID                   = UUID.from_16_bits(0x0003, 'RFCOMM')
305BT_TCP_PROTOCOL_ID                      = UUID.from_16_bits(0x0004, 'TCP')
306BT_TCS_BIN_PROTOCOL_ID                  = UUID.from_16_bits(0x0005, 'TCP-BIN')
307BT_TCS_AT_PROTOCOL_ID                   = UUID.from_16_bits(0x0006, 'TCS-AT')
308BT_ATT_PROTOCOL_ID                      = UUID.from_16_bits(0x0007, 'ATT')
309BT_OBEX_PROTOCOL_ID                     = UUID.from_16_bits(0x0008, 'OBEX')
310BT_IP_PROTOCOL_ID                       = UUID.from_16_bits(0x0009, 'IP')
311BT_FTP_PROTOCOL_ID                      = UUID.from_16_bits(0x000A, 'FTP')
312BT_HTTP_PROTOCOL_ID                     = UUID.from_16_bits(0x000C, 'HTTP')
313BT_WSP_PROTOCOL_ID                      = UUID.from_16_bits(0x000E, 'WSP')
314BT_BNEP_PROTOCOL_ID                     = UUID.from_16_bits(0x000F, 'BNEP')
315BT_UPNP_PROTOCOL_ID                     = UUID.from_16_bits(0x0010, 'UPNP')
316BT_HIDP_PROTOCOL_ID                     = UUID.from_16_bits(0x0011, 'HIDP')
317BT_HARDCOPY_CONTROL_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x0012, 'HardcopyControlChannel')
318BT_HARDCOPY_DATA_CHANNEL_PROTOCOL_ID    = UUID.from_16_bits(0x0014, 'HardcopyDataChannel')
319BT_HARDCOPY_NOTIFICATION_PROTOCOL_ID    = UUID.from_16_bits(0x0016, 'HardcopyNotification')
320BT_AVTCP_PROTOCOL_ID                    = UUID.from_16_bits(0x0017, 'AVCTP')
321BT_AVDTP_PROTOCOL_ID                    = UUID.from_16_bits(0x0019, 'AVDTP')
322BT_CMTP_PROTOCOL_ID                     = UUID.from_16_bits(0x001B, 'CMTP')
323BT_MCAP_CONTROL_CHANNEL_PROTOCOL_ID     = UUID.from_16_bits(0x001E, 'MCAPControlChannel')
324BT_MCAP_DATA_CHANNEL_PROTOCOL_ID        = UUID.from_16_bits(0x001F, 'MCAPDataChannel')
325BT_L2CAP_PROTOCOL_ID                    = UUID.from_16_bits(0x0100, 'L2CAP')
326
327# Service Classes and Profiles
328BT_SERVICE_DISCOVERY_SERVER_SERVICE_CLASS_ID_SERVICE = UUID.from_16_bits(0x1000, 'ServiceDiscoveryServerServiceClassID')
329BT_BROWSE_GROUP_DESCRIPTOR_SERVICE_CLASS_ID_SERVICE  = UUID.from_16_bits(0x1001, 'BrowseGroupDescriptorServiceClassID')
330BT_SERIAL_PORT_SERVICE                               = UUID.from_16_bits(0x1101, 'SerialPort')
331BT_LAN_ACCESS_USING_PPP_SERVICE                      = UUID.from_16_bits(0x1102, 'LANAccessUsingPPP')
332BT_DIALUP_NETWORKING_SERVICE                         = UUID.from_16_bits(0x1103, 'DialupNetworking')
333BT_IR_MCSYNC_SERVICE                                 = UUID.from_16_bits(0x1104, 'IrMCSync')
334BT_OBEX_OBJECT_PUSH_SERVICE                          = UUID.from_16_bits(0x1105, 'OBEXObjectPush')
335BT_OBEX_FILE_TRANSFER_SERVICE                        = UUID.from_16_bits(0x1106, 'OBEXFileTransfer')
336BT_IR_MCSYNC_COMMAND_SERVICE                         = UUID.from_16_bits(0x1107, 'IrMCSyncCommand')
337BT_HEADSET_SERVICE                                   = UUID.from_16_bits(0x1108, 'Headset')
338BT_CORDLESS_TELEPHONY_SERVICE                        = UUID.from_16_bits(0x1109, 'CordlessTelephony')
339BT_AUDIO_SOURCE_SERVICE                              = UUID.from_16_bits(0x110A, 'AudioSource')
340BT_AUDIO_SINK_SERVICE                                = UUID.from_16_bits(0x110B, 'AudioSink')
341BT_AV_REMOTE_CONTROL_TARGET_SERVICE                  = UUID.from_16_bits(0x110C, 'A/V_RemoteControlTarget')
342BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE               = UUID.from_16_bits(0x110D, 'AdvancedAudioDistribution')
343BT_AV_REMOTE_CONTROL_SERVICE                         = UUID.from_16_bits(0x110E, 'A/V_RemoteControl')
344BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE              = UUID.from_16_bits(0x110F, 'A/V_RemoteControlController')
345BT_INTERCOM_SERVICE                                  = UUID.from_16_bits(0x1110, 'Intercom')
346BT_FAX_SERVICE                                       = UUID.from_16_bits(0x1111, 'Fax')
347BT_HEADSET_AUDIO_GATEWAY_SERVICE                     = UUID.from_16_bits(0x1112, 'Headset - Audio Gateway')
348BT_WAP_SERVICE                                       = UUID.from_16_bits(0x1113, 'WAP')
349BT_WAP_CLIENT_SERVICE                                = UUID.from_16_bits(0x1114, 'WAP_CLIENT')
350BT_PANU_SERVICE                                      = UUID.from_16_bits(0x1115, 'PANU')
351BT_NAP_SERVICE                                       = UUID.from_16_bits(0x1116, 'NAP')
352BT_GN_SERVICE                                        = UUID.from_16_bits(0x1117, 'GN')
353BT_DIRECT_PRINTING_SERVICE                           = UUID.from_16_bits(0x1118, 'DirectPrinting')
354BT_REFERENCE_PRINTING_SERVICE                        = UUID.from_16_bits(0x1119, 'ReferencePrinting')
355BT_BASIC_IMAGING_PROFILE_SERVICE                     = UUID.from_16_bits(0x111A, 'Basic Imaging Profile')
356BT_IMAGING_RESPONDER_SERVICE                         = UUID.from_16_bits(0x111B, 'ImagingResponder')
357BT_IMAGING_AUTOMATIC_ARCHIVE_SERVICE                 = UUID.from_16_bits(0x111C, 'ImagingAutomaticArchive')
358BT_IMAGING_REFERENCED_OBJECTS_SERVICE                = UUID.from_16_bits(0x111D, 'ImagingReferencedObjects')
359BT_HANDSFREE_SERVICE                                 = UUID.from_16_bits(0x111E, 'Handsfree')
360BT_HANDSFREE_AUDIO_GATEWAY_SERVICE                   = UUID.from_16_bits(0x111F, 'HandsfreeAudioGateway')
361BT_DIRECT_PRINTING_REFERENCE_OBJECTS_SERVICE         = UUID.from_16_bits(0x1120, 'DirectPrintingReferenceObjectsService')
362BT_REFLECTED_UI_SERVICE                              = UUID.from_16_bits(0x1121, 'ReflectedUI')
363BT_BASIC_PRINTING_SERVICE                            = UUID.from_16_bits(0x1122, 'BasicPrinting')
364BT_PRINTING_STATUS_SERVICE                           = UUID.from_16_bits(0x1123, 'PrintingStatus')
365BT_HUMAN_INTERFACE_DEVICE_SERVICE                    = UUID.from_16_bits(0x1124, 'HumanInterfaceDeviceService')
366BT_HARDCOPY_CABLE_REPLACEMENT_SERVICE                = UUID.from_16_bits(0x1125, 'HardcopyCableReplacement')
367BT_HCR_PRINT_SERVICE                                 = UUID.from_16_bits(0x1126, 'HCR_Print')
368BT_HCR_SCAN_SERVICE                                  = UUID.from_16_bits(0x1127, 'HCR_Scan')
369BT_COMMON_ISDN_ACCESS_SERVICE                        = UUID.from_16_bits(0x1128, 'Common_ISDN_Access')
370BT_SIM_ACCESS_SERVICE                                = UUID.from_16_bits(0x112D, 'SIM_Access')
371BT_PHONEBOOK_ACCESS_PCE_SERVICE                      = UUID.from_16_bits(0x112E, 'Phonebook Access - PCE')
372BT_PHONEBOOK_ACCESS_PSE_SERVICE                      = UUID.from_16_bits(0x112F, 'Phonebook Access - PSE')
373BT_PHONEBOOK_ACCESS_SERVICE                          = UUID.from_16_bits(0x1130, 'Phonebook Access')
374BT_HEADSET_HS_SERVICE                                = UUID.from_16_bits(0x1131, 'Headset - HS')
375BT_MESSAGE_ACCESS_SERVER_SERVICE                     = UUID.from_16_bits(0x1132, 'Message Access Server')
376BT_MESSAGE_NOTIFICATION_SERVER_SERVICE               = UUID.from_16_bits(0x1133, 'Message Notification Server')
377BT_MESSAGE_ACCESS_PROFILE_SERVICE                    = UUID.from_16_bits(0x1134, 'Message Access Profile')
378BT_GNSS_SERVICE                                      = UUID.from_16_bits(0x1135, 'GNSS')
379BT_GNSS_SERVER_SERVICE                               = UUID.from_16_bits(0x1136, 'GNSS_Server')
380BT_3D_DISPLAY_SERVICE                                = UUID.from_16_bits(0x1137, '3D Display')
381BT_3D_GLASSES_SERVICE                                = UUID.from_16_bits(0x1138, '3D Glasses')
382BT_3D_SYNCHRONIZATION_SERVICE                        = UUID.from_16_bits(0x1139, '3D Synchronization')
383BT_MPS_PROFILE_SERVICE                               = UUID.from_16_bits(0x113A, 'MPS Profile')
384BT_MPS_SC_SERVICE                                    = UUID.from_16_bits(0x113B, 'MPS SC')
385BT_ACCESS_SERVICE_SERVICE                            = UUID.from_16_bits(0x113C, 'CTN Access Service')
386BT_CTN_NOTIFICATION_SERVICE_SERVICE                  = UUID.from_16_bits(0x113D, 'CTN Notification Service')
387BT_CTN_PROFILE_SERVICE                               = UUID.from_16_bits(0x113E, 'CTN Profile')
388BT_PNP_INFORMATION_SERVICE                           = UUID.from_16_bits(0x1200, 'PnPInformation')
389BT_GENERIC_NETWORKING_SERVICE                        = UUID.from_16_bits(0x1201, 'GenericNetworking')
390BT_GENERIC_FILE_TRANSFER_SERVICE                     = UUID.from_16_bits(0x1202, 'GenericFileTransfer')
391BT_GENERIC_AUDIO_SERVICE                             = UUID.from_16_bits(0x1203, 'GenericAudio')
392BT_GENERIC_TELEPHONY_SERVICE                         = UUID.from_16_bits(0x1204, 'GenericTelephony')
393BT_UPNP_SERVICE                                      = UUID.from_16_bits(0x1205, 'UPNP_Service')
394BT_UPNP_IP_SERVICE                                   = UUID.from_16_bits(0x1206, 'UPNP_IP_Service')
395BT_ESDP_UPNP_IP_PAN_SERVICE                          = UUID.from_16_bits(0x1300, 'ESDP_UPNP_IP_PAN')
396BT_ESDP_UPNP_IP_LAP_SERVICE                          = UUID.from_16_bits(0x1301, 'ESDP_UPNP_IP_LAP')
397BT_ESDP_UPNP_L2CAP_SERVICE                           = UUID.from_16_bits(0x1302, 'ESDP_UPNP_L2CAP')
398BT_VIDEO_SOURCE_SERVICE                              = UUID.from_16_bits(0x1303, 'VideoSource')
399BT_VIDEO_SINK_SERVICE                                = UUID.from_16_bits(0x1304, 'VideoSink')
400BT_VIDEO_DISTRIBUTION_SERVICE                        = UUID.from_16_bits(0x1305, 'VideoDistribution')
401BT_HDP_SERVICE                                       = UUID.from_16_bits(0x1400, 'HDP')
402BT_HDP_SOURCE_SERVICE                                = UUID.from_16_bits(0x1401, 'HDP Source')
403BT_HDP_SINK_SERVICE                                  = UUID.from_16_bits(0x1402, 'HDP Sink')
404
405# fmt: on
406# pylint: enable=line-too-long
407
408
409# -----------------------------------------------------------------------------
410# DeviceClass
411# -----------------------------------------------------------------------------
412class DeviceClass:
413    # fmt: off
414    # pylint: disable=line-too-long
415
416    # Major Service Classes (flags combined with OR)
417    LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0)
418    LE_AUDIO_SERVICE_CLASS                  = (1 << 1)
419    RESERVED                                = (1 << 2)
420    POSITIONING_SERVICE_CLASS               = (1 << 3)
421    NETWORKING_SERVICE_CLASS                = (1 << 4)
422    RENDERING_SERVICE_CLASS                 = (1 << 5)
423    CAPTURING_SERVICE_CLASS                 = (1 << 6)
424    OBJECT_TRANSFER_SERVICE_CLASS           = (1 << 7)
425    AUDIO_SERVICE_CLASS                     = (1 << 8)
426    TELEPHONY_SERVICE_CLASS                 = (1 << 9)
427    INFORMATION_SERVICE_CLASS               = (1 << 10)
428
429    SERVICE_CLASS_LABELS = [
430        'Limited Discoverable Mode',
431        'LE audio',
432        '(reserved)',
433        'Positioning',
434        'Networking',
435        'Rendering',
436        'Capturing',
437        'Object Transfer',
438        'Audio',
439        'Telephony',
440        'Information'
441    ]
442
443    # Major Device Classes
444    MISCELLANEOUS_MAJOR_DEVICE_CLASS            = 0x00
445    COMPUTER_MAJOR_DEVICE_CLASS                 = 0x01
446    PHONE_MAJOR_DEVICE_CLASS                    = 0x02
447    LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = 0x03
448    AUDIO_VIDEO_MAJOR_DEVICE_CLASS              = 0x04
449    PERIPHERAL_MAJOR_DEVICE_CLASS               = 0x05
450    IMAGING_MAJOR_DEVICE_CLASS                  = 0x06
451    WEARABLE_MAJOR_DEVICE_CLASS                 = 0x07
452    TOY_MAJOR_DEVICE_CLASS                      = 0x08
453    HEALTH_MAJOR_DEVICE_CLASS                   = 0x09
454    UNCATEGORIZED_MAJOR_DEVICE_CLASS            = 0x1F
455
456    MAJOR_DEVICE_CLASS_NAMES = {
457        MISCELLANEOUS_MAJOR_DEVICE_CLASS:            'Miscellaneous',
458        COMPUTER_MAJOR_DEVICE_CLASS:                 'Computer',
459        PHONE_MAJOR_DEVICE_CLASS:                    'Phone',
460        LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS: 'LAN/Network Access Point',
461        AUDIO_VIDEO_MAJOR_DEVICE_CLASS:              'Audio/Video',
462        PERIPHERAL_MAJOR_DEVICE_CLASS:               'Peripheral',
463        IMAGING_MAJOR_DEVICE_CLASS:                  'Imaging',
464        WEARABLE_MAJOR_DEVICE_CLASS:                 'Wearable',
465        TOY_MAJOR_DEVICE_CLASS:                      'Toy',
466        HEALTH_MAJOR_DEVICE_CLASS:                   'Health',
467        UNCATEGORIZED_MAJOR_DEVICE_CLASS:            'Uncategorized'
468    }
469
470    COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS         = 0x00
471    COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS   = 0x01
472    COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = 0x02
473    COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS       = 0x03
474    COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS       = 0x04
475    COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS      = 0x05
476    COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS     = 0x06
477    COMPUTER_TABLET_MINOR_DEVICE_CLASS                = 0x07
478
479    COMPUTER_MINOR_DEVICE_CLASS_NAMES = {
480        COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS:         'Uncategorized',
481        COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS:   'Desktop workstation',
482        COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS: 'Server-class computer',
483        COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS:       'Laptop',
484        COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS:       'Handheld PC/PDA',
485        COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS:      'Palm-size PC/PDA',
486        COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS:     'Wearable computer',
487        COMPUTER_TABLET_MINOR_DEVICE_CLASS:                'Tablet'
488    }
489
490    PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS                = 0x00
491    PHONE_CELLULAR_MINOR_DEVICE_CLASS                     = 0x01
492    PHONE_CORDLESS_MINOR_DEVICE_CLASS                     = 0x02
493    PHONE_SMARTPHONE_MINOR_DEVICE_CLASS                   = 0x03
494    PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = 0x04
495    PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS                  = 0x05
496
497    PHONE_MINOR_DEVICE_CLASS_NAMES = {
498        PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS:                'Uncategorized',
499        PHONE_CELLULAR_MINOR_DEVICE_CLASS:                     'Cellular',
500        PHONE_CORDLESS_MINOR_DEVICE_CLASS:                     'Cordless',
501        PHONE_SMARTPHONE_MINOR_DEVICE_CLASS:                   'Smartphone',
502        PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS: 'Wired modem or voice gateway',
503        PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS:                  'Common ISDN access'
504    }
505
506    AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS                 = 0x00
507    AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS       = 0x01
508    AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS             = 0x02
509    # (RESERVED)                                                 = 0x03
510    AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS                    = 0x04
511    AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS                   = 0x05
512    AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS                    = 0x06
513    AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS                = 0x07
514    AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS                     = 0x08
515    AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS                   = 0x09
516    AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS             = 0x0A
517    AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS                           = 0x0B
518    AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS                  = 0x0C
519    AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS                     = 0x0D
520    AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS                 = 0x0E
521    AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x0F
522    AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS            = 0x10
523    # (RESERVED)                                                 = 0x11
524    AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS                 = 0x12
525
526    AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES = {
527        AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS:                 'Uncategorized',
528        AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS:       'Wearable Headset Device',
529        AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS:             'Hands-free Device',
530        AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS:                    'Microphone',
531        AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS:                   'Loudspeaker',
532        AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS:                    'Headphones',
533        AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS:                'Portable Audio',
534        AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS:                     'Car audio',
535        AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS:                   'Set-top box',
536        AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS:             'HiFi Audio Device',
537        AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS:                           'VCR',
538        AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS:                  'Video Camera',
539        AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS:                     'Camcorder',
540        AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS:                 'Video Monitor',
541        AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Video Display and Loudspeaker',
542        AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS:            'Video Conferencing',
543        AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS:                 'Gaming/Toy'
544    }
545
546    PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS                  = 0x00
547    PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS                       = 0x10
548    PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS                = 0x20
549    PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x30
550    PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS                       = 0x01
551    PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS                        = 0x02
552    PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS                 = 0x03
553    PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS                 = 0x04
554    PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS               = 0x05
555    PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS                    = 0x06
556    PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS                    = 0x07
557    PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS               = 0x08
558    PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = 0x09
559
560    PERIPHERAL_MINOR_DEVICE_CLASS_NAMES = {
561        PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS:                  'Uncategorized',
562        PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS:                       'Keyboard',
563        PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS:                'Pointing device',
564        PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Combo keyboard/pointing device',
565        PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS:                       'Joystick',
566        PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS:                        'Gamepad',
567        PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS:                 'Remote control',
568        PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS:                 'Sensing device',
569        PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS:               'Digitizer tablet',
570        PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS:                    'Card Reader',
571        PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS:                    'Digital Pen',
572        PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS:               'Handheld scanner',
573        PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS: 'Handheld gestural input device'
574    }
575
576    MINOR_DEVICE_CLASS_NAMES = {
577        COMPUTER_MAJOR_DEVICE_CLASS:    COMPUTER_MINOR_DEVICE_CLASS_NAMES,
578        PHONE_MAJOR_DEVICE_CLASS:       PHONE_MINOR_DEVICE_CLASS_NAMES,
579        AUDIO_VIDEO_MAJOR_DEVICE_CLASS: AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES,
580        PERIPHERAL_MAJOR_DEVICE_CLASS:  PERIPHERAL_MINOR_DEVICE_CLASS_NAMES
581    }
582
583    # fmt: on
584    # pylint: enable=line-too-long
585
586    @staticmethod
587    def split_class_of_device(class_of_device):
588        # Split the bit fields of the composite class of device value into:
589        # (service_classes, major_device_class, minor_device_class)
590        return (
591            (class_of_device >> 13 & 0x7FF),
592            (class_of_device >> 8 & 0x1F),
593            (class_of_device >> 2 & 0x3F),
594        )
595
596    @staticmethod
597    def pack_class_of_device(service_classes, major_device_class, minor_device_class):
598        return service_classes << 13 | major_device_class << 8 | minor_device_class << 2
599
600    @staticmethod
601    def service_class_labels(service_class_flags):
602        return bit_flags_to_strings(
603            service_class_flags, DeviceClass.SERVICE_CLASS_LABELS
604        )
605
606    @staticmethod
607    def major_device_class_name(device_class):
608        return name_or_number(DeviceClass.MAJOR_DEVICE_CLASS_NAMES, device_class)
609
610    @staticmethod
611    def minor_device_class_name(major_device_class, minor_device_class):
612        class_names = DeviceClass.MINOR_DEVICE_CLASS_NAMES.get(major_device_class)
613        if class_names is None:
614            return f'#{minor_device_class:02X}'
615        return name_or_number(class_names, minor_device_class)
616
617
618# -----------------------------------------------------------------------------
619# Advertising Data
620# -----------------------------------------------------------------------------
621AdvertisingObject = Union[
622    List[UUID], Tuple[UUID, bytes], bytes, str, int, Tuple[int, int], Tuple[int, bytes]
623]
624
625
626class AdvertisingData:
627    # fmt: off
628    # pylint: disable=line-too-long
629
630    # This list is only partial, it still needs to be filled in from the spec
631    FLAGS                                          = 0x01
632    INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS  = 0x02
633    COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS    = 0x03
634    INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS  = 0x04
635    COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS    = 0x05
636    INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x06
637    COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS   = 0x07
638    SHORTENED_LOCAL_NAME                           = 0x08
639    COMPLETE_LOCAL_NAME                            = 0x09
640    TX_POWER_LEVEL                                 = 0x0A
641    CLASS_OF_DEVICE                                = 0x0D
642    SIMPLE_PAIRING_HASH_C                          = 0x0E
643    SIMPLE_PAIRING_HASH_C_192                      = 0x0E
644    SIMPLE_PAIRING_RANDOMIZER_R                    = 0x0F
645    SIMPLE_PAIRING_RANDOMIZER_R_192                = 0x0F
646    DEVICE_ID                                      = 0x10
647    SECURITY_MANAGER_TK_VALUE                      = 0x10
648    SECURITY_MANAGER_OUT_OF_BAND_FLAGS             = 0x11
649    PERIPHERAL_CONNECTION_INTERVAL_RANGE           = 0x12
650    LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS      = 0x14
651    LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS     = 0x15
652    SERVICE_DATA                                   = 0x16
653    SERVICE_DATA_16_BIT_UUID                       = 0x16
654    PUBLIC_TARGET_ADDRESS                          = 0x17
655    RANDOM_TARGET_ADDRESS                          = 0x18
656    APPEARANCE                                     = 0x19
657    ADVERTISING_INTERVAL                           = 0x1A
658    LE_BLUETOOTH_DEVICE_ADDRESS                    = 0x1B
659    LE_ROLE                                        = 0x1C
660    SIMPLE_PAIRING_HASH_C_256                      = 0x1D
661    SIMPLE_PAIRING_RANDOMIZER_R_256                = 0x1E
662    LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS      = 0x1F
663    SERVICE_DATA_32_BIT_UUID                       = 0x20
664    SERVICE_DATA_128_BIT_UUID                      = 0x21
665    LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE       = 0x22
666    LE_SECURE_CONNECTIONS_RANDOM_VALUE             = 0x23
667    URI                                            = 0x24
668    INDOOR_POSITIONING                             = 0x25
669    TRANSPORT_DISCOVERY_DATA                       = 0x26
670    LE_SUPPORTED_FEATURES                          = 0x27
671    CHANNEL_MAP_UPDATE_INDICATION                  = 0x28
672    PB_ADV                                         = 0x29
673    MESH_MESSAGE                                   = 0x2A
674    MESH_BEACON                                    = 0x2B
675    BIGINFO                                        = 0x2C
676    BROADCAST_CODE                                 = 0x2D
677    RESOLVABLE_SET_IDENTIFIER                      = 0x2E
678    ADVERTISING_INTERVAL_LONG                      = 0x2F
679    THREE_D_INFORMATION_DATA                       = 0x3D
680    MANUFACTURER_SPECIFIC_DATA                     = 0xFF
681
682    AD_TYPE_NAMES = {
683        FLAGS:                                          'FLAGS',
684        INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:  'INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
685        COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:    'COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
686        INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:  'INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
687        COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:    'COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
688        INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 'INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
689        COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:   'COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
690        SHORTENED_LOCAL_NAME:                           'SHORTENED_LOCAL_NAME',
691        COMPLETE_LOCAL_NAME:                            'COMPLETE_LOCAL_NAME',
692        TX_POWER_LEVEL:                                 'TX_POWER_LEVEL',
693        CLASS_OF_DEVICE:                                'CLASS_OF_DEVICE',
694        SIMPLE_PAIRING_HASH_C:                          'SIMPLE_PAIRING_HASH_C',
695        SIMPLE_PAIRING_HASH_C_192:                      'SIMPLE_PAIRING_HASH_C_192',
696        SIMPLE_PAIRING_RANDOMIZER_R:                    'SIMPLE_PAIRING_RANDOMIZER_R',
697        SIMPLE_PAIRING_RANDOMIZER_R_192:                'SIMPLE_PAIRING_RANDOMIZER_R_192',
698        DEVICE_ID:                                      'DEVICE_ID',
699        SECURITY_MANAGER_TK_VALUE:                      'SECURITY_MANAGER_TK_VALUE',
700        SECURITY_MANAGER_OUT_OF_BAND_FLAGS:             'SECURITY_MANAGER_OUT_OF_BAND_FLAGS',
701        PERIPHERAL_CONNECTION_INTERVAL_RANGE:           'PERIPHERAL_CONNECTION_INTERVAL_RANGE',
702        LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS:      'LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS',
703        LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS:     'LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS',
704        SERVICE_DATA:                                   'SERVICE_DATA',
705        SERVICE_DATA_16_BIT_UUID:                       'SERVICE_DATA_16_BIT_UUID',
706        PUBLIC_TARGET_ADDRESS:                          'PUBLIC_TARGET_ADDRESS',
707        RANDOM_TARGET_ADDRESS:                          'RANDOM_TARGET_ADDRESS',
708        APPEARANCE:                                     'APPEARANCE',
709        ADVERTISING_INTERVAL:                           'ADVERTISING_INTERVAL',
710        LE_BLUETOOTH_DEVICE_ADDRESS:                    'LE_BLUETOOTH_DEVICE_ADDRESS',
711        LE_ROLE:                                        'LE_ROLE',
712        SIMPLE_PAIRING_HASH_C_256:                      'SIMPLE_PAIRING_HASH_C_256',
713        SIMPLE_PAIRING_RANDOMIZER_R_256:                'SIMPLE_PAIRING_RANDOMIZER_R_256',
714        LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS:      'LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS',
715        SERVICE_DATA_32_BIT_UUID:                       'SERVICE_DATA_32_BIT_UUID',
716        SERVICE_DATA_128_BIT_UUID:                      'SERVICE_DATA_128_BIT_UUID',
717        LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE:       'LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE',
718        LE_SECURE_CONNECTIONS_RANDOM_VALUE:             'LE_SECURE_CONNECTIONS_RANDOM_VALUE',
719        URI:                                            'URI',
720        INDOOR_POSITIONING:                             'INDOOR_POSITIONING',
721        TRANSPORT_DISCOVERY_DATA:                       'TRANSPORT_DISCOVERY_DATA',
722        LE_SUPPORTED_FEATURES:                          'LE_SUPPORTED_FEATURES',
723        CHANNEL_MAP_UPDATE_INDICATION:                  'CHANNEL_MAP_UPDATE_INDICATION',
724        PB_ADV:                                         'PB_ADV',
725        MESH_MESSAGE:                                   'MESH_MESSAGE',
726        MESH_BEACON:                                    'MESH_BEACON',
727        BIGINFO:                                        'BIGINFO',
728        BROADCAST_CODE:                                 'BROADCAST_CODE',
729        RESOLVABLE_SET_IDENTIFIER:                      'RESOLVABLE_SET_IDENTIFIER',
730        ADVERTISING_INTERVAL_LONG:                      'ADVERTISING_INTERVAL_LONG',
731        THREE_D_INFORMATION_DATA:                       'THREE_D_INFORMATION_DATA',
732        MANUFACTURER_SPECIFIC_DATA:                     'MANUFACTURER_SPECIFIC_DATA'
733    }
734
735    LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01
736    LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02
737    BR_EDR_NOT_SUPPORTED_FLAG         = 0x04
738    BR_EDR_CONTROLLER_FLAG            = 0x08
739    BR_EDR_HOST_FLAG                  = 0x10
740
741    ad_structures: List[Tuple[int, bytes]]
742
743    # fmt: on
744    # pylint: enable=line-too-long
745
746    def __init__(self, ad_structures: Optional[List[Tuple[int, bytes]]] = None) -> None:
747        if ad_structures is None:
748            ad_structures = []
749        self.ad_structures = ad_structures[:]
750
751    @staticmethod
752    def from_bytes(data):
753        instance = AdvertisingData()
754        instance.append(data)
755        return instance
756
757    @staticmethod
758    def flags_to_string(flags, short=False):
759        flag_names = (
760            ['LE Limited', 'LE General', 'No BR/EDR', 'BR/EDR C', 'BR/EDR H']
761            if short
762            else [
763                'LE Limited Discoverable Mode',
764                'LE General Discoverable Mode',
765                'BR/EDR Not Supported',
766                'Simultaneous LE and BR/EDR (Controller)',
767                'Simultaneous LE and BR/EDR (Host)',
768            ]
769        )
770        return ','.join(bit_flags_to_strings(flags, flag_names))
771
772    @staticmethod
773    def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> List[UUID]:
774        uuids = []
775        offset = 0
776        while (uuid_size * (offset + 1)) <= len(ad_data):
777            uuids.append(UUID.from_bytes(ad_data[offset : offset + uuid_size]))
778            offset += uuid_size
779        return uuids
780
781    @staticmethod
782    def uuid_list_to_string(ad_data, uuid_size):
783        return ', '.join(
784            [
785                str(uuid)
786                for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size)
787            ]
788        )
789
790    @staticmethod
791    def ad_data_to_string(ad_type, ad_data):
792        if ad_type == AdvertisingData.FLAGS:
793            ad_type_str = 'Flags'
794            ad_data_str = AdvertisingData.flags_to_string(ad_data[0], short=True)
795        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
796            ad_type_str = 'Complete List of 16-bit Service Class UUIDs'
797            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
798        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
799            ad_type_str = 'Incomplete List of 16-bit Service Class UUIDs'
800            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
801        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
802            ad_type_str = 'Complete List of 32-bit Service Class UUIDs'
803            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
804        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
805            ad_type_str = 'Incomplete List of 32-bit Service Class UUIDs'
806            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
807        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
808            ad_type_str = 'Complete List of 128-bit Service Class UUIDs'
809            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
810        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
811            ad_type_str = 'Incomplete List of 128-bit Service Class UUIDs'
812            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
813        elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
814            ad_type_str = 'Service Data'
815            uuid = UUID.from_bytes(ad_data[:2])
816            ad_data_str = f'service={uuid}, data={ad_data[2:].hex()}'
817        elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
818            ad_type_str = 'Service Data'
819            uuid = UUID.from_bytes(ad_data[:4])
820            ad_data_str = f'service={uuid}, data={ad_data[4:].hex()}'
821        elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
822            ad_type_str = 'Service Data'
823            uuid = UUID.from_bytes(ad_data[:16])
824            ad_data_str = f'service={uuid}, data={ad_data[16:].hex()}'
825        elif ad_type == AdvertisingData.SHORTENED_LOCAL_NAME:
826            ad_type_str = 'Shortened Local Name'
827            ad_data_str = f'"{ad_data.decode("utf-8")}"'
828        elif ad_type == AdvertisingData.COMPLETE_LOCAL_NAME:
829            ad_type_str = 'Complete Local Name'
830            ad_data_str = f'"{ad_data.decode("utf-8")}"'
831        elif ad_type == AdvertisingData.TX_POWER_LEVEL:
832            ad_type_str = 'TX Power Level'
833            ad_data_str = str(ad_data[0])
834        elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
835            ad_type_str = 'Manufacturer Specific Data'
836            company_id = struct.unpack_from('<H', ad_data, 0)[0]
837            company_name = COMPANY_IDENTIFIERS.get(company_id, f'0x{company_id:04X}')
838            ad_data_str = f'company={company_name}, data={ad_data[2:].hex()}'
839        elif ad_type == AdvertisingData.APPEARANCE:
840            ad_type_str = 'Appearance'
841            ad_data_str = ad_data.hex()
842        else:
843            ad_type_str = AdvertisingData.AD_TYPE_NAMES.get(ad_type, f'0x{ad_type:02X}')
844            ad_data_str = ad_data.hex()
845
846        return f'[{ad_type_str}]: {ad_data_str}'
847
848    # pylint: disable=too-many-return-statements
849    @staticmethod
850    def ad_data_to_object(ad_type: int, ad_data: bytes) -> AdvertisingObject:
851        if ad_type in (
852            AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
853            AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
854            AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS,
855        ):
856            return AdvertisingData.uuid_list_to_objects(ad_data, 2)
857
858        if ad_type in (
859            AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
860            AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
861            AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS,
862        ):
863            return AdvertisingData.uuid_list_to_objects(ad_data, 4)
864
865        if ad_type in (
866            AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
867            AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
868            AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
869        ):
870            return AdvertisingData.uuid_list_to_objects(ad_data, 16)
871
872        if ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
873            return (UUID.from_bytes(ad_data[:2]), ad_data[2:])
874
875        if ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
876            return (UUID.from_bytes(ad_data[:4]), ad_data[4:])
877
878        if ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
879            return (UUID.from_bytes(ad_data[:16]), ad_data[16:])
880
881        if ad_type in (
882            AdvertisingData.SHORTENED_LOCAL_NAME,
883            AdvertisingData.COMPLETE_LOCAL_NAME,
884            AdvertisingData.URI,
885        ):
886            return ad_data.decode("utf-8")
887
888        if ad_type in (AdvertisingData.TX_POWER_LEVEL, AdvertisingData.FLAGS):
889            return cast(int, struct.unpack('B', ad_data)[0])
890
891        if ad_type in (
892            AdvertisingData.APPEARANCE,
893            AdvertisingData.ADVERTISING_INTERVAL,
894        ):
895            return cast(int, struct.unpack('<H', ad_data)[0])
896
897        if ad_type == AdvertisingData.CLASS_OF_DEVICE:
898            return cast(int, struct.unpack('<I', bytes([*ad_data, 0]))[0])
899
900        if ad_type == AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE:
901            return cast(Tuple[int, int], struct.unpack('<HH', ad_data))
902
903        if ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
904            return (cast(int, struct.unpack_from('<H', ad_data, 0)[0]), ad_data[2:])
905
906        return ad_data
907
908    def append(self, data):
909        offset = 0
910        while offset + 1 < len(data):
911            length = data[offset]
912            offset += 1
913            if length > 0:
914                ad_type = data[offset]
915                ad_data = data[offset + 1 : offset + length]
916                self.ad_structures.append((ad_type, ad_data))
917            offset += length
918
919    def get_all(self, type_id: int, raw: bool = False) -> List[AdvertisingObject]:
920        '''
921        Get Advertising Data Structure(s) with a given type
922
923        Returns a (possibly empty) list of matches.
924        '''
925
926        def process_ad_data(ad_data: bytes) -> AdvertisingObject:
927            return ad_data if raw else self.ad_data_to_object(type_id, ad_data)
928
929        return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id]
930
931    def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingObject]:
932        '''
933        Get Advertising Data Structure(s) with a given type
934
935        Returns the first entry, or None if no structure matches.
936        '''
937
938        all = self.get_all(type_id, raw=raw)
939        return all[0] if all else None
940
941    def __bytes__(self):
942        return b''.join(
943            [bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures]
944        )
945
946    def to_string(self, separator=', '):
947        return separator.join(
948            [AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures]
949        )
950
951    def __str__(self):
952        return self.to_string()
953
954
955# -----------------------------------------------------------------------------
956# Connection Parameters
957# -----------------------------------------------------------------------------
958class ConnectionParameters:
959    def __init__(self, connection_interval, peripheral_latency, supervision_timeout):
960        self.connection_interval = connection_interval
961        self.peripheral_latency = peripheral_latency
962        self.supervision_timeout = supervision_timeout
963
964    def __str__(self):
965        return (
966            f'ConnectionParameters(connection_interval={self.connection_interval}, '
967            f'peripheral_latency={self.peripheral_latency}, '
968            f'supervision_timeout={self.supervision_timeout}'
969        )
970
971
972# -----------------------------------------------------------------------------
973# Connection PHY
974# -----------------------------------------------------------------------------
975class ConnectionPHY:
976    def __init__(self, tx_phy, rx_phy):
977        self.tx_phy = tx_phy
978        self.rx_phy = rx_phy
979
980    def __str__(self):
981        return f'ConnectionPHY(tx_phy={self.tx_phy}, rx_phy={self.rx_phy})'
982