bumble 0.0.179__py3-none-any.whl → 0.0.181__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bumble/helpers.py CHANGED
@@ -15,30 +15,41 @@
15
15
  # -----------------------------------------------------------------------------
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
+ from __future__ import annotations
19
+
20
+ from collections.abc import Callable, MutableMapping
21
+ from typing import cast, Any
18
22
  import logging
19
23
 
20
- from .colors import color
21
- from .att import ATT_CID, ATT_PDU
22
- from .smp import SMP_CID, SMP_Command
23
- from .core import name_or_number
24
- from .l2cap import (
24
+ from bumble import avdtp
25
+ from bumble.colors import color
26
+ from bumble.att import ATT_CID, ATT_PDU
27
+ from bumble.smp import SMP_CID, SMP_Command
28
+ from bumble.core import name_or_number
29
+ from bumble.l2cap import (
25
30
  L2CAP_PDU,
26
31
  L2CAP_CONNECTION_REQUEST,
27
32
  L2CAP_CONNECTION_RESPONSE,
28
33
  L2CAP_SIGNALING_CID,
29
34
  L2CAP_LE_SIGNALING_CID,
30
35
  L2CAP_Control_Frame,
36
+ L2CAP_Connection_Request,
31
37
  L2CAP_Connection_Response,
32
38
  )
33
- from .hci import (
39
+ from bumble.hci import (
40
+ Address,
34
41
  HCI_EVENT_PACKET,
35
42
  HCI_ACL_DATA_PACKET,
36
43
  HCI_DISCONNECTION_COMPLETE_EVENT,
37
44
  HCI_AclDataPacketAssembler,
45
+ HCI_Packet,
46
+ HCI_Event,
47
+ HCI_AclDataPacket,
48
+ HCI_Disconnection_Complete_Event,
38
49
  )
39
- from .rfcomm import RFCOMM_Frame, RFCOMM_PSM
40
- from .sdp import SDP_PDU, SDP_PSM
41
- from .avdtp import MessageAssembler as AVDTP_MessageAssembler, AVDTP_PSM
50
+ from bumble.rfcomm import RFCOMM_Frame, RFCOMM_PSM
51
+ from bumble.sdp import SDP_PDU, SDP_PSM
52
+ from bumble import crypto
42
53
 
43
54
  # -----------------------------------------------------------------------------
44
55
  # Logging
@@ -50,23 +61,25 @@ logger = logging.getLogger(__name__)
50
61
  PSM_NAMES = {
51
62
  RFCOMM_PSM: 'RFCOMM',
52
63
  SDP_PSM: 'SDP',
53
- AVDTP_PSM: 'AVDTP'
54
- # TODO: add more PSM values
64
+ avdtp.AVDTP_PSM: 'AVDTP',
55
65
  }
56
66
 
57
67
 
58
68
  # -----------------------------------------------------------------------------
59
69
  class PacketTracer:
60
70
  class AclStream:
61
- def __init__(self, analyzer):
71
+ psms: MutableMapping[int, int]
72
+ peer: PacketTracer.AclStream
73
+ avdtp_assemblers: MutableMapping[int, avdtp.MessageAssembler]
74
+
75
+ def __init__(self, analyzer: PacketTracer.Analyzer) -> None:
62
76
  self.analyzer = analyzer
63
77
  self.packet_assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu)
64
78
  self.avdtp_assemblers = {} # AVDTP assemblers, by source_cid
65
79
  self.psms = {} # PSM, by source_cid
66
- self.peer = None # ACL stream in the other direction
67
80
 
68
81
  # pylint: disable=too-many-nested-blocks
69
- def on_acl_pdu(self, pdu):
82
+ def on_acl_pdu(self, pdu: bytes) -> None:
70
83
  l2cap_pdu = L2CAP_PDU.from_bytes(pdu)
71
84
 
72
85
  if l2cap_pdu.cid == ATT_CID:
@@ -81,26 +94,30 @@ class PacketTracer:
81
94
 
82
95
  # Check if this signals a new channel
83
96
  if control_frame.code == L2CAP_CONNECTION_REQUEST:
84
- self.psms[control_frame.source_cid] = control_frame.psm
97
+ connection_request = cast(L2CAP_Connection_Request, control_frame)
98
+ self.psms[connection_request.source_cid] = connection_request.psm
85
99
  elif control_frame.code == L2CAP_CONNECTION_RESPONSE:
100
+ connection_response = cast(L2CAP_Connection_Response, control_frame)
86
101
  if (
87
- control_frame.result
102
+ connection_response.result
88
103
  == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL
89
104
  ):
90
105
  if self.peer:
91
- if psm := self.peer.psms.get(control_frame.source_cid):
106
+ if psm := self.peer.psms.get(
107
+ connection_response.source_cid
108
+ ):
92
109
  # Found a pending connection
93
- self.psms[control_frame.destination_cid] = psm
110
+ self.psms[connection_response.destination_cid] = psm
94
111
 
95
112
  # For AVDTP connections, create a packet assembler for
96
113
  # each direction
97
- if psm == AVDTP_PSM:
114
+ if psm == avdtp.AVDTP_PSM:
98
115
  self.avdtp_assemblers[
99
- control_frame.source_cid
100
- ] = AVDTP_MessageAssembler(self.on_avdtp_message)
116
+ connection_response.source_cid
117
+ ] = avdtp.MessageAssembler(self.on_avdtp_message)
101
118
  self.peer.avdtp_assemblers[
102
- control_frame.destination_cid
103
- ] = AVDTP_MessageAssembler(
119
+ connection_response.destination_cid
120
+ ] = avdtp.MessageAssembler(
104
121
  self.peer.on_avdtp_message
105
122
  )
106
123
 
@@ -113,7 +130,7 @@ class PacketTracer:
113
130
  elif psm == RFCOMM_PSM:
114
131
  rfcomm_frame = RFCOMM_Frame.from_bytes(l2cap_pdu.payload)
115
132
  self.analyzer.emit(rfcomm_frame)
116
- elif psm == AVDTP_PSM:
133
+ elif psm == avdtp.AVDTP_PSM:
117
134
  self.analyzer.emit(
118
135
  f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, '
119
136
  f'PSM=AVDTP]: {l2cap_pdu.payload.hex()}'
@@ -130,22 +147,26 @@ class PacketTracer:
130
147
  else:
131
148
  self.analyzer.emit(l2cap_pdu)
132
149
 
133
- def on_avdtp_message(self, transaction_label, message):
150
+ def on_avdtp_message(
151
+ self, transaction_label: int, message: avdtp.Message
152
+ ) -> None:
134
153
  self.analyzer.emit(
135
154
  f'{color("AVDTP", "green")} [{transaction_label}] {message}'
136
155
  )
137
156
 
138
- def feed_packet(self, packet):
157
+ def feed_packet(self, packet: HCI_AclDataPacket) -> None:
139
158
  self.packet_assembler.feed_packet(packet)
140
159
 
141
160
  class Analyzer:
142
- def __init__(self, label, emit_message):
161
+ acl_streams: MutableMapping[int, PacketTracer.AclStream]
162
+ peer: PacketTracer.Analyzer
163
+
164
+ def __init__(self, label: str, emit_message: Callable[..., None]) -> None:
143
165
  self.label = label
144
166
  self.emit_message = emit_message
145
167
  self.acl_streams = {} # ACL streams, by connection handle
146
- self.peer = None # Analyzer in the other direction
147
168
 
148
- def start_acl_stream(self, connection_handle):
169
+ def start_acl_stream(self, connection_handle: int) -> PacketTracer.AclStream:
149
170
  logger.info(
150
171
  f'[{self.label}] +++ Creating ACL stream for connection '
151
172
  f'0x{connection_handle:04X}'
@@ -160,7 +181,7 @@ class PacketTracer:
160
181
 
161
182
  return stream
162
183
 
163
- def end_acl_stream(self, connection_handle):
184
+ def end_acl_stream(self, connection_handle: int) -> None:
164
185
  if connection_handle in self.acl_streams:
165
186
  logger.info(
166
187
  f'[{self.label}] --- Removing ACL stream for connection '
@@ -171,23 +192,29 @@ class PacketTracer:
171
192
  # Let the other forwarder know so it can cleanup its stream as well
172
193
  self.peer.end_acl_stream(connection_handle)
173
194
 
174
- def on_packet(self, packet):
195
+ def on_packet(self, packet: HCI_Packet) -> None:
175
196
  self.emit(packet)
176
197
 
177
198
  if packet.hci_packet_type == HCI_ACL_DATA_PACKET:
199
+ acl_packet = cast(HCI_AclDataPacket, packet)
178
200
  # Look for an existing stream for this handle, create one if it is the
179
201
  # first ACL packet for that connection handle
180
- if (stream := self.acl_streams.get(packet.connection_handle)) is None:
181
- stream = self.start_acl_stream(packet.connection_handle)
182
- stream.feed_packet(packet)
202
+ if (
203
+ stream := self.acl_streams.get(acl_packet.connection_handle)
204
+ ) is None:
205
+ stream = self.start_acl_stream(acl_packet.connection_handle)
206
+ stream.feed_packet(acl_packet)
183
207
  elif packet.hci_packet_type == HCI_EVENT_PACKET:
184
- if packet.event_code == HCI_DISCONNECTION_COMPLETE_EVENT:
185
- self.end_acl_stream(packet.connection_handle)
208
+ event_packet = cast(HCI_Event, packet)
209
+ if event_packet.event_code == HCI_DISCONNECTION_COMPLETE_EVENT:
210
+ self.end_acl_stream(
211
+ cast(HCI_Disconnection_Complete_Event, packet).connection_handle
212
+ )
186
213
 
187
- def emit(self, message):
214
+ def emit(self, message: Any) -> None:
188
215
  self.emit_message(f'[{self.label}] {message}')
189
216
 
190
- def trace(self, packet, direction=0):
217
+ def trace(self, packet: HCI_Packet, direction: int = 0) -> None:
191
218
  if direction == 0:
192
219
  self.host_to_controller_analyzer.on_packet(packet)
193
220
  else:
@@ -195,10 +222,10 @@ class PacketTracer:
195
222
 
196
223
  def __init__(
197
224
  self,
198
- host_to_controller_label=color('HOST->CONTROLLER', 'blue'),
199
- controller_to_host_label=color('CONTROLLER->HOST', 'cyan'),
200
- emit_message=logger.info,
201
- ):
225
+ host_to_controller_label: str = color('HOST->CONTROLLER', 'blue'),
226
+ controller_to_host_label: str = color('CONTROLLER->HOST', 'cyan'),
227
+ emit_message: Callable[..., None] = logger.info,
228
+ ) -> None:
202
229
  self.host_to_controller_analyzer = PacketTracer.Analyzer(
203
230
  host_to_controller_label, emit_message
204
231
  )
@@ -207,3 +234,15 @@ class PacketTracer:
207
234
  )
208
235
  self.host_to_controller_analyzer.peer = self.controller_to_host_analyzer
209
236
  self.controller_to_host_analyzer.peer = self.host_to_controller_analyzer
237
+
238
+
239
+ def generate_irk() -> bytes:
240
+ return crypto.r()
241
+
242
+
243
+ def verify_rpa_with_irk(rpa: Address, irk: bytes) -> bool:
244
+ rpa_bytes = bytes(rpa)
245
+ prand_given = rpa_bytes[3:]
246
+ hash_given = rpa_bytes[:3]
247
+ hash_local = crypto.ah(irk, prand_given)
248
+ return hash_local[:3] == hash_given
bumble/hfp.py CHANGED
@@ -22,7 +22,7 @@ import dataclasses
22
22
  import enum
23
23
  import traceback
24
24
  import warnings
25
- from typing import Dict, List, Union, Set, TYPE_CHECKING
25
+ from typing import Dict, List, Union, Set, Any, TYPE_CHECKING
26
26
 
27
27
  from . import at
28
28
  from . import rfcomm
@@ -35,7 +35,11 @@ from bumble.core import (
35
35
  BT_L2CAP_PROTOCOL_ID,
36
36
  BT_RFCOMM_PROTOCOL_ID,
37
37
  )
38
- from bumble.hci import HCI_Enhanced_Setup_Synchronous_Connection_Command
38
+ from bumble.hci import (
39
+ HCI_Enhanced_Setup_Synchronous_Connection_Command,
40
+ CodingFormat,
41
+ CodecID,
42
+ )
39
43
  from bumble.sdp import (
40
44
  DataElement,
41
45
  ServiceAttribute,
@@ -66,6 +70,7 @@ class HfpProtocolError(ProtocolError):
66
70
  # Protocol Support
67
71
  # -----------------------------------------------------------------------------
68
72
 
73
+
69
74
  # -----------------------------------------------------------------------------
70
75
  class HfpProtocol:
71
76
  dlc: rfcomm.DLC
@@ -842,19 +847,15 @@ class DefaultCodecParameters(enum.IntEnum):
842
847
  @dataclasses.dataclass
843
848
  class EscoParameters:
844
849
  # Codec specific
845
- transmit_coding_format: HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat
846
- receive_coding_format: HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat
850
+ transmit_coding_format: CodingFormat
851
+ receive_coding_format: CodingFormat
847
852
  packet_type: HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType
848
853
  retransmission_effort: HCI_Enhanced_Setup_Synchronous_Connection_Command.RetransmissionEffort
849
854
  max_latency: int
850
855
 
851
856
  # Common
852
- input_coding_format: HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat = (
853
- HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.TRANSPARENT
854
- )
855
- output_coding_format: HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat = (
856
- HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.TRANSPARENT
857
- )
857
+ input_coding_format: CodingFormat = CodingFormat(CodecID.LINEAR_PCM)
858
+ output_coding_format: CodingFormat = CodingFormat(CodecID.LINEAR_PCM)
858
859
  input_coded_data_size: int = 16
859
860
  output_coded_data_size: int = 16
860
861
  input_pcm_data_format: HCI_Enhanced_Setup_Synchronous_Connection_Command.PcmDataFormat = (
@@ -880,26 +881,31 @@ class EscoParameters:
880
881
  transmit_codec_frame_size: int = 60
881
882
  receive_codec_frame_size: int = 60
882
883
 
884
+ def asdict(self) -> Dict[str, Any]:
885
+ # dataclasses.asdict() will recursively deep-copy the entire object,
886
+ # which is expensive and breaks CodingFormat object, so let it simply copy here.
887
+ return self.__dict__
888
+
883
889
 
884
890
  _ESCO_PARAMETERS_CVSD_D0 = EscoParameters(
885
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
886
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
891
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
892
+ receive_coding_format=CodingFormat(CodecID.CVSD),
887
893
  max_latency=0xFFFF,
888
894
  packet_type=HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.HV1,
889
895
  retransmission_effort=HCI_Enhanced_Setup_Synchronous_Connection_Command.RetransmissionEffort.NO_RETRANSMISSION,
890
896
  )
891
897
 
892
898
  _ESCO_PARAMETERS_CVSD_D1 = EscoParameters(
893
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
894
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
899
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
900
+ receive_coding_format=CodingFormat(CodecID.CVSD),
895
901
  max_latency=0xFFFF,
896
902
  packet_type=HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.HV3,
897
903
  retransmission_effort=HCI_Enhanced_Setup_Synchronous_Connection_Command.RetransmissionEffort.NO_RETRANSMISSION,
898
904
  )
899
905
 
900
906
  _ESCO_PARAMETERS_CVSD_S1 = EscoParameters(
901
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
902
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
907
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
908
+ receive_coding_format=CodingFormat(CodecID.CVSD),
903
909
  max_latency=0x0007,
904
910
  packet_type=(
905
911
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -912,8 +918,8 @@ _ESCO_PARAMETERS_CVSD_S1 = EscoParameters(
912
918
  )
913
919
 
914
920
  _ESCO_PARAMETERS_CVSD_S2 = EscoParameters(
915
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
916
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
921
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
922
+ receive_coding_format=CodingFormat(CodecID.CVSD),
917
923
  max_latency=0x0007,
918
924
  packet_type=(
919
925
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -925,8 +931,8 @@ _ESCO_PARAMETERS_CVSD_S2 = EscoParameters(
925
931
  )
926
932
 
927
933
  _ESCO_PARAMETERS_CVSD_S3 = EscoParameters(
928
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
929
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
934
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
935
+ receive_coding_format=CodingFormat(CodecID.CVSD),
930
936
  max_latency=0x000A,
931
937
  packet_type=(
932
938
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -938,8 +944,8 @@ _ESCO_PARAMETERS_CVSD_S3 = EscoParameters(
938
944
  )
939
945
 
940
946
  _ESCO_PARAMETERS_CVSD_S4 = EscoParameters(
941
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
942
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.CVSD,
947
+ transmit_coding_format=CodingFormat(CodecID.CVSD),
948
+ receive_coding_format=CodingFormat(CodecID.CVSD),
943
949
  max_latency=0x000C,
944
950
  packet_type=(
945
951
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -951,8 +957,8 @@ _ESCO_PARAMETERS_CVSD_S4 = EscoParameters(
951
957
  )
952
958
 
953
959
  _ESCO_PARAMETERS_MSBC_T1 = EscoParameters(
954
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.MSBC,
955
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.MSBC,
960
+ transmit_coding_format=CodingFormat(CodecID.MSBC),
961
+ receive_coding_format=CodingFormat(CodecID.MSBC),
956
962
  max_latency=0x0008,
957
963
  packet_type=(
958
964
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -960,12 +966,14 @@ _ESCO_PARAMETERS_MSBC_T1 = EscoParameters(
960
966
  | HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.NO_2_EV5
961
967
  | HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.NO_3_EV5
962
968
  ),
969
+ input_bandwidth=32000,
970
+ output_bandwidth=32000,
963
971
  retransmission_effort=HCI_Enhanced_Setup_Synchronous_Connection_Command.RetransmissionEffort.OPTIMIZE_FOR_QUALITY,
964
972
  )
965
973
 
966
974
  _ESCO_PARAMETERS_MSBC_T2 = EscoParameters(
967
- transmit_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.MSBC,
968
- receive_coding_format=HCI_Enhanced_Setup_Synchronous_Connection_Command.CodingFormat.MSBC,
975
+ transmit_coding_format=CodingFormat(CodecID.MSBC),
976
+ receive_coding_format=CodingFormat(CodecID.MSBC),
969
977
  max_latency=0x000D,
970
978
  packet_type=(
971
979
  HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.EV3
@@ -974,10 +982,12 @@ _ESCO_PARAMETERS_MSBC_T2 = EscoParameters(
974
982
  | HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.NO_2_EV5
975
983
  | HCI_Enhanced_Setup_Synchronous_Connection_Command.PacketType.NO_3_EV5
976
984
  ),
985
+ input_bandwidth=32000,
986
+ output_bandwidth=32000,
977
987
  retransmission_effort=HCI_Enhanced_Setup_Synchronous_Connection_Command.RetransmissionEffort.OPTIMIZE_FOR_QUALITY,
978
988
  )
979
989
 
980
- ESCO_PERAMETERS = {
990
+ ESCO_PARAMETERS = {
981
991
  DefaultCodecParameters.SCO_CVSD_D0: _ESCO_PARAMETERS_CVSD_D0,
982
992
  DefaultCodecParameters.SCO_CVSD_D1: _ESCO_PARAMETERS_CVSD_D1,
983
993
  DefaultCodecParameters.ESCO_CVSD_S1: _ESCO_PARAMETERS_CVSD_S1,