bumble 0.0.170__py3-none-any.whl → 0.0.172__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/l2cap.py CHANGED
@@ -17,6 +17,7 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
  import asyncio
20
+ import enum
20
21
  import logging
21
22
  import struct
22
23
 
@@ -676,56 +677,35 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
676
677
 
677
678
  # -----------------------------------------------------------------------------
678
679
  class Channel(EventEmitter):
679
- # States
680
- CLOSED = 0x00
681
- WAIT_CONNECT = 0x01
682
- WAIT_CONNECT_RSP = 0x02
683
- OPEN = 0x03
684
- WAIT_DISCONNECT = 0x04
685
- WAIT_CREATE = 0x05
686
- WAIT_CREATE_RSP = 0x06
687
- WAIT_MOVE = 0x07
688
- WAIT_MOVE_RSP = 0x08
689
- WAIT_MOVE_CONFIRM = 0x09
690
- WAIT_CONFIRM_RSP = 0x0A
691
-
692
- # CONFIG substates
693
- WAIT_CONFIG = 0x10
694
- WAIT_SEND_CONFIG = 0x11
695
- WAIT_CONFIG_REQ_RSP = 0x12
696
- WAIT_CONFIG_RSP = 0x13
697
- WAIT_CONFIG_REQ = 0x14
698
- WAIT_IND_FINAL_RSP = 0x15
699
- WAIT_FINAL_RSP = 0x16
700
- WAIT_CONTROL_IND = 0x17
701
-
702
- STATE_NAMES = {
703
- CLOSED: 'CLOSED',
704
- WAIT_CONNECT: 'WAIT_CONNECT',
705
- WAIT_CONNECT_RSP: 'WAIT_CONNECT_RSP',
706
- OPEN: 'OPEN',
707
- WAIT_DISCONNECT: 'WAIT_DISCONNECT',
708
- WAIT_CREATE: 'WAIT_CREATE',
709
- WAIT_CREATE_RSP: 'WAIT_CREATE_RSP',
710
- WAIT_MOVE: 'WAIT_MOVE',
711
- WAIT_MOVE_RSP: 'WAIT_MOVE_RSP',
712
- WAIT_MOVE_CONFIRM: 'WAIT_MOVE_CONFIRM',
713
- WAIT_CONFIRM_RSP: 'WAIT_CONFIRM_RSP',
714
- WAIT_CONFIG: 'WAIT_CONFIG',
715
- WAIT_SEND_CONFIG: 'WAIT_SEND_CONFIG',
716
- WAIT_CONFIG_REQ_RSP: 'WAIT_CONFIG_REQ_RSP',
717
- WAIT_CONFIG_RSP: 'WAIT_CONFIG_RSP',
718
- WAIT_CONFIG_REQ: 'WAIT_CONFIG_REQ',
719
- WAIT_IND_FINAL_RSP: 'WAIT_IND_FINAL_RSP',
720
- WAIT_FINAL_RSP: 'WAIT_FINAL_RSP',
721
- WAIT_CONTROL_IND: 'WAIT_CONTROL_IND',
722
- }
680
+ class State(enum.IntEnum):
681
+ # States
682
+ CLOSED = 0x00
683
+ WAIT_CONNECT = 0x01
684
+ WAIT_CONNECT_RSP = 0x02
685
+ OPEN = 0x03
686
+ WAIT_DISCONNECT = 0x04
687
+ WAIT_CREATE = 0x05
688
+ WAIT_CREATE_RSP = 0x06
689
+ WAIT_MOVE = 0x07
690
+ WAIT_MOVE_RSP = 0x08
691
+ WAIT_MOVE_CONFIRM = 0x09
692
+ WAIT_CONFIRM_RSP = 0x0A
693
+
694
+ # CONFIG substates
695
+ WAIT_CONFIG = 0x10
696
+ WAIT_SEND_CONFIG = 0x11
697
+ WAIT_CONFIG_REQ_RSP = 0x12
698
+ WAIT_CONFIG_RSP = 0x13
699
+ WAIT_CONFIG_REQ = 0x14
700
+ WAIT_IND_FINAL_RSP = 0x15
701
+ WAIT_FINAL_RSP = 0x16
702
+ WAIT_CONTROL_IND = 0x17
723
703
 
724
704
  connection_result: Optional[asyncio.Future[None]]
725
705
  disconnection_result: Optional[asyncio.Future[None]]
726
706
  response: Optional[asyncio.Future[bytes]]
727
707
  sink: Optional[Callable[[bytes], Any]]
728
- state: int
708
+ state: State
729
709
  connection: Connection
730
710
 
731
711
  def __init__(
@@ -741,7 +721,7 @@ class Channel(EventEmitter):
741
721
  self.manager = manager
742
722
  self.connection = connection
743
723
  self.signaling_cid = signaling_cid
744
- self.state = Channel.CLOSED
724
+ self.state = self.State.CLOSED
745
725
  self.mtu = mtu
746
726
  self.psm = psm
747
727
  self.source_cid = source_cid
@@ -751,10 +731,8 @@ class Channel(EventEmitter):
751
731
  self.disconnection_result = None
752
732
  self.sink = None
753
733
 
754
- def change_state(self, new_state: int) -> None:
755
- logger.debug(
756
- f'{self} state change -> {color(Channel.STATE_NAMES[new_state], "cyan")}'
757
- )
734
+ def _change_state(self, new_state: State) -> None:
735
+ logger.debug(f'{self} state change -> {color(new_state.name, "cyan")}')
758
736
  self.state = new_state
759
737
 
760
738
  def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
@@ -767,7 +745,7 @@ class Channel(EventEmitter):
767
745
  # Check that there isn't already a request pending
768
746
  if self.response:
769
747
  raise InvalidStateError('request already pending')
770
- if self.state != Channel.OPEN:
748
+ if self.state != self.State.OPEN:
771
749
  raise InvalidStateError('channel not open')
772
750
 
773
751
  self.response = asyncio.get_running_loop().create_future()
@@ -787,14 +765,14 @@ class Channel(EventEmitter):
787
765
  )
788
766
 
789
767
  async def connect(self) -> None:
790
- if self.state != Channel.CLOSED:
768
+ if self.state != self.State.CLOSED:
791
769
  raise InvalidStateError('invalid state')
792
770
 
793
771
  # Check that we can start a new connection
794
772
  if self.connection_result:
795
773
  raise RuntimeError('connection already pending')
796
774
 
797
- self.change_state(Channel.WAIT_CONNECT_RSP)
775
+ self._change_state(self.State.WAIT_CONNECT_RSP)
798
776
  self.send_control_frame(
799
777
  L2CAP_Connection_Request(
800
778
  identifier=self.manager.next_identifier(self.connection),
@@ -814,10 +792,10 @@ class Channel(EventEmitter):
814
792
  self.connection_result = None
815
793
 
816
794
  async def disconnect(self) -> None:
817
- if self.state != Channel.OPEN:
795
+ if self.state != self.State.OPEN:
818
796
  raise InvalidStateError('invalid state')
819
797
 
820
- self.change_state(Channel.WAIT_DISCONNECT)
798
+ self._change_state(self.State.WAIT_DISCONNECT)
821
799
  self.send_control_frame(
822
800
  L2CAP_Disconnection_Request(
823
801
  identifier=self.manager.next_identifier(self.connection),
@@ -832,8 +810,8 @@ class Channel(EventEmitter):
832
810
  return await self.disconnection_result
833
811
 
834
812
  def abort(self) -> None:
835
- if self.state == self.OPEN:
836
- self.change_state(self.CLOSED)
813
+ if self.state == self.State.OPEN:
814
+ self._change_state(self.State.CLOSED)
837
815
  self.emit('close')
838
816
 
839
817
  def send_configure_request(self) -> None:
@@ -856,7 +834,7 @@ class Channel(EventEmitter):
856
834
 
857
835
  def on_connection_request(self, request) -> None:
858
836
  self.destination_cid = request.source_cid
859
- self.change_state(Channel.WAIT_CONNECT)
837
+ self._change_state(self.State.WAIT_CONNECT)
860
838
  self.send_control_frame(
861
839
  L2CAP_Connection_Response(
862
840
  identifier=request.identifier,
@@ -866,24 +844,24 @@ class Channel(EventEmitter):
866
844
  status=0x0000,
867
845
  )
868
846
  )
869
- self.change_state(Channel.WAIT_CONFIG)
847
+ self._change_state(self.State.WAIT_CONFIG)
870
848
  self.send_configure_request()
871
- self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
849
+ self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
872
850
 
873
851
  def on_connection_response(self, response):
874
- if self.state != Channel.WAIT_CONNECT_RSP:
852
+ if self.state != self.State.WAIT_CONNECT_RSP:
875
853
  logger.warning(color('invalid state', 'red'))
876
854
  return
877
855
 
878
856
  if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
879
857
  self.destination_cid = response.destination_cid
880
- self.change_state(Channel.WAIT_CONFIG)
858
+ self._change_state(self.State.WAIT_CONFIG)
881
859
  self.send_configure_request()
882
- self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
860
+ self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
883
861
  elif response.result == L2CAP_Connection_Response.CONNECTION_PENDING:
884
862
  pass
885
863
  else:
886
- self.change_state(Channel.CLOSED)
864
+ self._change_state(self.State.CLOSED)
887
865
  self.connection_result.set_exception(
888
866
  ProtocolError(
889
867
  response.result,
@@ -895,9 +873,9 @@ class Channel(EventEmitter):
895
873
 
896
874
  def on_configure_request(self, request) -> None:
897
875
  if self.state not in (
898
- Channel.WAIT_CONFIG,
899
- Channel.WAIT_CONFIG_REQ,
900
- Channel.WAIT_CONFIG_REQ_RSP,
876
+ self.State.WAIT_CONFIG,
877
+ self.State.WAIT_CONFIG_REQ,
878
+ self.State.WAIT_CONFIG_REQ_RSP,
901
879
  ):
902
880
  logger.warning(color('invalid state', 'red'))
903
881
  return
@@ -918,25 +896,28 @@ class Channel(EventEmitter):
918
896
  options=request.options, # TODO: don't accept everything blindly
919
897
  )
920
898
  )
921
- if self.state == Channel.WAIT_CONFIG:
922
- self.change_state(Channel.WAIT_SEND_CONFIG)
899
+ if self.state == self.State.WAIT_CONFIG:
900
+ self._change_state(self.State.WAIT_SEND_CONFIG)
923
901
  self.send_configure_request()
924
- self.change_state(Channel.WAIT_CONFIG_RSP)
925
- elif self.state == Channel.WAIT_CONFIG_REQ:
926
- self.change_state(Channel.OPEN)
902
+ self._change_state(self.State.WAIT_CONFIG_RSP)
903
+ elif self.state == self.State.WAIT_CONFIG_REQ:
904
+ self._change_state(self.State.OPEN)
927
905
  if self.connection_result:
928
906
  self.connection_result.set_result(None)
929
907
  self.connection_result = None
930
908
  self.emit('open')
931
- elif self.state == Channel.WAIT_CONFIG_REQ_RSP:
932
- self.change_state(Channel.WAIT_CONFIG_RSP)
909
+ elif self.state == self.State.WAIT_CONFIG_REQ_RSP:
910
+ self._change_state(self.State.WAIT_CONFIG_RSP)
933
911
 
934
912
  def on_configure_response(self, response) -> None:
935
913
  if response.result == L2CAP_Configure_Response.SUCCESS:
936
- if self.state == Channel.WAIT_CONFIG_REQ_RSP:
937
- self.change_state(Channel.WAIT_CONFIG_REQ)
938
- elif self.state in (Channel.WAIT_CONFIG_RSP, Channel.WAIT_CONTROL_IND):
939
- self.change_state(Channel.OPEN)
914
+ if self.state == self.State.WAIT_CONFIG_REQ_RSP:
915
+ self._change_state(self.State.WAIT_CONFIG_REQ)
916
+ elif self.state in (
917
+ self.State.WAIT_CONFIG_RSP,
918
+ self.State.WAIT_CONTROL_IND,
919
+ ):
920
+ self._change_state(self.State.OPEN)
940
921
  if self.connection_result:
941
922
  self.connection_result.set_result(None)
942
923
  self.connection_result = None
@@ -966,7 +947,7 @@ class Channel(EventEmitter):
966
947
  # TODO: decide how to fail gracefully
967
948
 
968
949
  def on_disconnection_request(self, request) -> None:
969
- if self.state in (Channel.OPEN, Channel.WAIT_DISCONNECT):
950
+ if self.state in (self.State.OPEN, self.State.WAIT_DISCONNECT):
970
951
  self.send_control_frame(
971
952
  L2CAP_Disconnection_Response(
972
953
  identifier=request.identifier,
@@ -974,14 +955,14 @@ class Channel(EventEmitter):
974
955
  source_cid=request.source_cid,
975
956
  )
976
957
  )
977
- self.change_state(Channel.CLOSED)
958
+ self._change_state(self.State.CLOSED)
978
959
  self.emit('close')
979
960
  self.manager.on_channel_closed(self)
980
961
  else:
981
962
  logger.warning(color('invalid state', 'red'))
982
963
 
983
964
  def on_disconnection_response(self, response) -> None:
984
- if self.state != Channel.WAIT_DISCONNECT:
965
+ if self.state != self.State.WAIT_DISCONNECT:
985
966
  logger.warning(color('invalid state', 'red'))
986
967
  return
987
968
 
@@ -992,7 +973,7 @@ class Channel(EventEmitter):
992
973
  logger.warning('unexpected source or destination CID')
993
974
  return
994
975
 
995
- self.change_state(Channel.CLOSED)
976
+ self._change_state(self.State.CLOSED)
996
977
  if self.disconnection_result:
997
978
  self.disconnection_result.set_result(None)
998
979
  self.disconnection_result = None
@@ -1004,7 +985,7 @@ class Channel(EventEmitter):
1004
985
  f'Channel({self.source_cid}->{self.destination_cid}, '
1005
986
  f'PSM={self.psm}, '
1006
987
  f'MTU={self.mtu}, '
1007
- f'state={Channel.STATE_NAMES[self.state]})'
988
+ f'state={self.state.name})'
1008
989
  )
1009
990
 
1010
991
 
@@ -1014,33 +995,21 @@ class LeConnectionOrientedChannel(EventEmitter):
1014
995
  LE Credit-based Connection Oriented Channel
1015
996
  """
1016
997
 
1017
- INIT = 0
1018
- CONNECTED = 1
1019
- CONNECTING = 2
1020
- DISCONNECTING = 3
1021
- DISCONNECTED = 4
1022
- CONNECTION_ERROR = 5
1023
-
1024
- STATE_NAMES = {
1025
- INIT: 'INIT',
1026
- CONNECTED: 'CONNECTED',
1027
- CONNECTING: 'CONNECTING',
1028
- DISCONNECTING: 'DISCONNECTING',
1029
- DISCONNECTED: 'DISCONNECTED',
1030
- CONNECTION_ERROR: 'CONNECTION_ERROR',
1031
- }
998
+ class State(enum.IntEnum):
999
+ INIT = 0
1000
+ CONNECTED = 1
1001
+ CONNECTING = 2
1002
+ DISCONNECTING = 3
1003
+ DISCONNECTED = 4
1004
+ CONNECTION_ERROR = 5
1032
1005
 
1033
1006
  out_queue: Deque[bytes]
1034
1007
  connection_result: Optional[asyncio.Future[LeConnectionOrientedChannel]]
1035
1008
  disconnection_result: Optional[asyncio.Future[None]]
1036
1009
  out_sdu: Optional[bytes]
1037
- state: int
1010
+ state: State
1038
1011
  connection: Connection
1039
1012
 
1040
- @staticmethod
1041
- def state_name(state: int) -> str:
1042
- return name_or_number(LeConnectionOrientedChannel.STATE_NAMES, state)
1043
-
1044
1013
  def __init__(
1045
1014
  self,
1046
1015
  manager: ChannelManager,
@@ -1083,19 +1052,17 @@ class LeConnectionOrientedChannel(EventEmitter):
1083
1052
  self.drained.set()
1084
1053
 
1085
1054
  if connected:
1086
- self.state = LeConnectionOrientedChannel.CONNECTED
1055
+ self.state = self.State.CONNECTED
1087
1056
  else:
1088
- self.state = LeConnectionOrientedChannel.INIT
1057
+ self.state = self.State.INIT
1089
1058
 
1090
- def change_state(self, new_state: int) -> None:
1091
- logger.debug(
1092
- f'{self} state change -> {color(self.state_name(new_state), "cyan")}'
1093
- )
1059
+ def _change_state(self, new_state: State) -> None:
1060
+ logger.debug(f'{self} state change -> {color(new_state.name, "cyan")}')
1094
1061
  self.state = new_state
1095
1062
 
1096
- if new_state == self.CONNECTED:
1063
+ if new_state == self.State.CONNECTED:
1097
1064
  self.emit('open')
1098
- elif new_state == self.DISCONNECTED:
1065
+ elif new_state == self.State.DISCONNECTED:
1099
1066
  self.emit('close')
1100
1067
 
1101
1068
  def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
@@ -1106,7 +1073,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1106
1073
 
1107
1074
  async def connect(self) -> LeConnectionOrientedChannel:
1108
1075
  # Check that we're in the right state
1109
- if self.state != self.INIT:
1076
+ if self.state != self.State.INIT:
1110
1077
  raise InvalidStateError('not in a connectable state')
1111
1078
 
1112
1079
  # Check that we can start a new connection
@@ -1114,7 +1081,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1114
1081
  if identifier in self.manager.le_coc_requests:
1115
1082
  raise RuntimeError('too many concurrent connection requests')
1116
1083
 
1117
- self.change_state(self.CONNECTING)
1084
+ self._change_state(self.State.CONNECTING)
1118
1085
  request = L2CAP_LE_Credit_Based_Connection_Request(
1119
1086
  identifier=identifier,
1120
1087
  le_psm=self.le_psm,
@@ -1134,10 +1101,10 @@ class LeConnectionOrientedChannel(EventEmitter):
1134
1101
 
1135
1102
  async def disconnect(self) -> None:
1136
1103
  # Check that we're connected
1137
- if self.state != self.CONNECTED:
1104
+ if self.state != self.State.CONNECTED:
1138
1105
  raise InvalidStateError('not connected')
1139
1106
 
1140
- self.change_state(self.DISCONNECTING)
1107
+ self._change_state(self.State.DISCONNECTING)
1141
1108
  self.flush_output()
1142
1109
  self.send_control_frame(
1143
1110
  L2CAP_Disconnection_Request(
@@ -1153,15 +1120,15 @@ class LeConnectionOrientedChannel(EventEmitter):
1153
1120
  return await self.disconnection_result
1154
1121
 
1155
1122
  def abort(self) -> None:
1156
- if self.state == self.CONNECTED:
1157
- self.change_state(self.DISCONNECTED)
1123
+ if self.state == self.State.CONNECTED:
1124
+ self._change_state(self.State.DISCONNECTED)
1158
1125
 
1159
1126
  def on_pdu(self, pdu: bytes) -> None:
1160
1127
  if self.sink is None:
1161
1128
  logger.warning('received pdu without a sink')
1162
1129
  return
1163
1130
 
1164
- if self.state != self.CONNECTED:
1131
+ if self.state != self.State.CONNECTED:
1165
1132
  logger.warning('received PDU while not connected, dropping')
1166
1133
 
1167
1134
  # Manage the peer credits
@@ -1240,7 +1207,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1240
1207
  self.credits = response.initial_credits
1241
1208
  self.connected = True
1242
1209
  self.connection_result.set_result(self)
1243
- self.change_state(self.CONNECTED)
1210
+ self._change_state(self.State.CONNECTED)
1244
1211
  else:
1245
1212
  self.connection_result.set_exception(
1246
1213
  ProtocolError(
@@ -1251,7 +1218,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1251
1218
  ),
1252
1219
  )
1253
1220
  )
1254
- self.change_state(self.CONNECTION_ERROR)
1221
+ self._change_state(self.State.CONNECTION_ERROR)
1255
1222
 
1256
1223
  # Cleanup
1257
1224
  self.connection_result = None
@@ -1271,11 +1238,11 @@ class LeConnectionOrientedChannel(EventEmitter):
1271
1238
  source_cid=request.source_cid,
1272
1239
  )
1273
1240
  )
1274
- self.change_state(self.DISCONNECTED)
1241
+ self._change_state(self.State.DISCONNECTED)
1275
1242
  self.flush_output()
1276
1243
 
1277
1244
  def on_disconnection_response(self, response) -> None:
1278
- if self.state != self.DISCONNECTING:
1245
+ if self.state != self.State.DISCONNECTING:
1279
1246
  logger.warning(color('invalid state', 'red'))
1280
1247
  return
1281
1248
 
@@ -1286,7 +1253,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1286
1253
  logger.warning('unexpected source or destination CID')
1287
1254
  return
1288
1255
 
1289
- self.change_state(self.DISCONNECTED)
1256
+ self._change_state(self.State.DISCONNECTED)
1290
1257
  if self.disconnection_result:
1291
1258
  self.disconnection_result.set_result(None)
1292
1259
  self.disconnection_result = None
@@ -1339,7 +1306,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1339
1306
  return
1340
1307
 
1341
1308
  def write(self, data: bytes) -> None:
1342
- if self.state != self.CONNECTED:
1309
+ if self.state != self.State.CONNECTED:
1343
1310
  logger.warning('not connected, dropping data')
1344
1311
  return
1345
1312
 
@@ -1367,7 +1334,7 @@ class LeConnectionOrientedChannel(EventEmitter):
1367
1334
  def __str__(self) -> str:
1368
1335
  return (
1369
1336
  f'CoC({self.source_cid}->{self.destination_cid}, '
1370
- f'State={self.state_name(self.state)}, '
1337
+ f'State={self.state.name}, '
1371
1338
  f'PSM={self.le_psm}, '
1372
1339
  f'MTU={self.mtu}/{self.peer_mtu}, '
1373
1340
  f'MPS={self.mps}/{self.peer_mps}, '
@@ -233,7 +233,11 @@ class SecurityService(SecurityServicer):
233
233
  sc=config.pairing_sc_enable,
234
234
  mitm=config.pairing_mitm_enable,
235
235
  bonding=config.pairing_bonding_enable,
236
- identity_address_type=config.identity_address_type,
236
+ identity_address_type=(
237
+ PairingConfig.AddressType.PUBLIC
238
+ if connection.self_address.is_public
239
+ else config.identity_address_type
240
+ ),
237
241
  delegate=PairingDelegate(
238
242
  connection,
239
243
  self,
@@ -97,10 +97,13 @@ async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
97
97
  raise ValueError('invalid mode')
98
98
 
99
99
  # Create the transport object
100
- transport = PumpedTransport(
101
- PumpedPacketSource(hci_device.read),
102
- PumpedPacketSink(hci_device.write),
103
- channel.close,
100
+ class EmulatorTransport(PumpedTransport):
101
+ async def close(self):
102
+ await super().close()
103
+ await channel.close()
104
+
105
+ transport = EmulatorTransport(
106
+ PumpedPacketSource(hci_device.read), PumpedPacketSink(hci_device.write)
104
107
  )
105
108
  transport.start()
106
109