bumble 0.0.213__py3-none-any.whl → 0.0.215__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.
Files changed (123) hide show
  1. bumble/_version.py +16 -3
  2. bumble/a2dp.py +15 -16
  3. bumble/apps/auracast.py +14 -38
  4. bumble/apps/bench.py +10 -15
  5. bumble/apps/ble_rpa_tool.py +1 -0
  6. bumble/apps/console.py +22 -25
  7. bumble/apps/controller_info.py +20 -25
  8. bumble/apps/controller_loopback.py +6 -10
  9. bumble/apps/controllers.py +2 -3
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +3 -3
  12. bumble/apps/gg_bridge.py +7 -8
  13. bumble/apps/hci_bridge.py +4 -3
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +16 -26
  16. bumble/apps/pair.py +30 -43
  17. bumble/apps/pandora_server.py +5 -4
  18. bumble/apps/player/player.py +20 -24
  19. bumble/apps/rfcomm_bridge.py +4 -10
  20. bumble/apps/scan.py +17 -8
  21. bumble/apps/show.py +4 -5
  22. bumble/apps/speaker/speaker.py +23 -27
  23. bumble/apps/unbond.py +3 -3
  24. bumble/apps/usb_probe.py +2 -4
  25. bumble/att.py +241 -246
  26. bumble/audio/io.py +5 -9
  27. bumble/avc.py +2 -2
  28. bumble/avctp.py +6 -7
  29. bumble/avdtp.py +19 -22
  30. bumble/avrcp.py +1097 -589
  31. bumble/codecs.py +2 -0
  32. bumble/controller.py +142 -35
  33. bumble/core.py +567 -248
  34. bumble/crypto/__init__.py +2 -2
  35. bumble/crypto/builtin.py +1 -1
  36. bumble/crypto/cryptography.py +2 -4
  37. bumble/data_types.py +1025 -0
  38. bumble/device.py +319 -267
  39. bumble/drivers/__init__.py +3 -2
  40. bumble/drivers/intel.py +3 -4
  41. bumble/drivers/rtk.py +26 -9
  42. bumble/gap.py +4 -4
  43. bumble/gatt.py +3 -2
  44. bumble/gatt_adapters.py +3 -11
  45. bumble/gatt_client.py +69 -81
  46. bumble/gatt_server.py +124 -124
  47. bumble/hci.py +114 -18
  48. bumble/helpers.py +19 -26
  49. bumble/hfp.py +10 -21
  50. bumble/hid.py +22 -16
  51. bumble/host.py +191 -103
  52. bumble/keys.py +5 -3
  53. bumble/l2cap.py +138 -104
  54. bumble/link.py +18 -19
  55. bumble/logging.py +65 -0
  56. bumble/pairing.py +7 -6
  57. bumble/pandora/__init__.py +9 -8
  58. bumble/pandora/config.py +3 -1
  59. bumble/pandora/device.py +3 -2
  60. bumble/pandora/host.py +38 -36
  61. bumble/pandora/l2cap.py +22 -21
  62. bumble/pandora/security.py +15 -15
  63. bumble/pandora/utils.py +5 -3
  64. bumble/profiles/aics.py +11 -11
  65. bumble/profiles/ams.py +403 -0
  66. bumble/profiles/ancs.py +6 -7
  67. bumble/profiles/ascs.py +14 -9
  68. bumble/profiles/asha.py +8 -12
  69. bumble/profiles/bap.py +11 -23
  70. bumble/profiles/bass.py +2 -7
  71. bumble/profiles/battery_service.py +3 -4
  72. bumble/profiles/cap.py +1 -2
  73. bumble/profiles/csip.py +2 -6
  74. bumble/profiles/device_information_service.py +2 -2
  75. bumble/profiles/gap.py +4 -4
  76. bumble/profiles/gatt_service.py +1 -4
  77. bumble/profiles/gmap.py +5 -5
  78. bumble/profiles/hap.py +62 -59
  79. bumble/profiles/heart_rate_service.py +5 -4
  80. bumble/profiles/le_audio.py +3 -1
  81. bumble/profiles/mcp.py +3 -7
  82. bumble/profiles/pacs.py +3 -6
  83. bumble/profiles/pbp.py +2 -0
  84. bumble/profiles/tmap.py +2 -3
  85. bumble/profiles/vcs.py +2 -8
  86. bumble/profiles/vocs.py +8 -8
  87. bumble/rfcomm.py +11 -14
  88. bumble/rtp.py +1 -0
  89. bumble/sdp.py +10 -8
  90. bumble/smp.py +151 -159
  91. bumble/snoop.py +5 -5
  92. bumble/tools/generate_company_id_list.py +1 -0
  93. bumble/tools/intel_fw_download.py +3 -3
  94. bumble/tools/intel_util.py +5 -4
  95. bumble/tools/rtk_fw_download.py +6 -3
  96. bumble/tools/rtk_util.py +26 -8
  97. bumble/transport/__init__.py +19 -15
  98. bumble/transport/android_emulator.py +8 -13
  99. bumble/transport/android_netsim.py +19 -18
  100. bumble/transport/common.py +12 -15
  101. bumble/transport/file.py +1 -1
  102. bumble/transport/hci_socket.py +4 -6
  103. bumble/transport/pty.py +5 -6
  104. bumble/transport/pyusb.py +7 -10
  105. bumble/transport/serial.py +2 -1
  106. bumble/transport/tcp_client.py +2 -2
  107. bumble/transport/tcp_server.py +11 -14
  108. bumble/transport/udp.py +3 -3
  109. bumble/transport/unix.py +67 -1
  110. bumble/transport/usb.py +6 -6
  111. bumble/transport/vhci.py +0 -1
  112. bumble/transport/ws_client.py +2 -1
  113. bumble/transport/ws_server.py +3 -2
  114. bumble/utils.py +20 -5
  115. bumble/vendor/android/hci.py +1 -2
  116. bumble/vendor/zephyr/hci.py +0 -1
  117. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/METADATA +4 -2
  118. bumble-0.0.215.dist-info/RECORD +183 -0
  119. bumble-0.0.213.dist-info/RECORD +0 -180
  120. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
  121. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
  122. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
  123. {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/top_level.txt +0 -0
bumble/codecs.py CHANGED
@@ -16,7 +16,9 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
+
19
20
  from dataclasses import dataclass
21
+
20
22
  from typing_extensions import Self
21
23
 
22
24
  from bumble import core
bumble/controller.py CHANGED
@@ -17,34 +17,32 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
 
20
- import logging
21
20
  import asyncio
22
21
  import dataclasses
23
22
  import itertools
23
+ import logging
24
24
  import random
25
25
  import struct
26
- from bumble.colors import color
27
- from bumble.core import (
28
- PhysicalTransport,
29
- )
26
+ from typing import TYPE_CHECKING, Any, Optional, Union
27
+
30
28
  from bumble import hci
29
+ from bumble.colors import color
30
+ from bumble.core import PhysicalTransport
31
31
  from bumble.hci import (
32
32
  HCI_ACL_DATA_PACKET,
33
33
  HCI_COMMAND_DISALLOWED_ERROR,
34
34
  HCI_COMMAND_PACKET,
35
35
  HCI_COMMAND_STATUS_PENDING,
36
- HCI_CONNECTION_TIMEOUT_ERROR,
37
36
  HCI_CONTROLLER_BUSY_ERROR,
38
37
  HCI_EVENT_PACKET,
39
38
  HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR,
40
39
  HCI_LE_1M_PHY,
40
+ HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
41
41
  HCI_SUCCESS,
42
- HCI_UNKNOWN_HCI_COMMAND_ERROR,
43
42
  HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
44
- HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
43
+ HCI_UNKNOWN_HCI_COMMAND_ERROR,
45
44
  HCI_VERSION_BLUETOOTH_CORE_5_0,
46
45
  Address,
47
- Role,
48
46
  HCI_AclDataPacket,
49
47
  HCI_AclDataPacketAssembler,
50
48
  HCI_Command_Complete_Event,
@@ -53,7 +51,6 @@ from bumble.hci import (
53
51
  HCI_Connection_Request_Event,
54
52
  HCI_Disconnection_Complete_Event,
55
53
  HCI_Encryption_Change_Event,
56
- HCI_Synchronous_Connection_Complete_Event,
57
54
  HCI_LE_Advertising_Report_Event,
58
55
  HCI_LE_CIS_Established_Event,
59
56
  HCI_LE_CIS_Request_Event,
@@ -62,8 +59,9 @@ from bumble.hci import (
62
59
  HCI_Number_Of_Completed_Packets_Event,
63
60
  HCI_Packet,
64
61
  HCI_Role_Change_Event,
62
+ HCI_Synchronous_Connection_Complete_Event,
63
+ Role,
65
64
  )
66
- from typing import Optional, Union, Any, TYPE_CHECKING
67
65
 
68
66
  if TYPE_CHECKING:
69
67
  from bumble.link import LocalLink
@@ -89,6 +87,7 @@ class CisLink:
89
87
  cis_id: int
90
88
  cig_id: int
91
89
  acl_connection: Optional[Connection] = None
90
+ data_paths: set[int] = dataclasses.field(default_factory=set)
92
91
 
93
92
 
94
93
  # -----------------------------------------------------------------------------
@@ -370,12 +369,23 @@ class Controller:
370
369
  return connection
371
370
  return None
372
371
 
372
+ def find_peripheral_connection_by_handle(self, handle):
373
+ for connection in self.peripheral_connections.values():
374
+ if connection.handle == handle:
375
+ return connection
376
+ return None
377
+
373
378
  def find_classic_connection_by_handle(self, handle):
374
379
  for connection in self.classic_connections.values():
375
380
  if connection.handle == handle:
376
381
  return connection
377
382
  return None
378
383
 
384
+ def find_iso_link_by_handle(self, handle: int) -> Optional[CisLink]:
385
+ return self.central_cis_links.get(handle) or self.peripheral_cis_links.get(
386
+ handle
387
+ )
388
+
379
389
  def on_link_central_connected(self, central_address):
380
390
  '''
381
391
  Called when an incoming connection occurs from a central on the link
@@ -414,7 +424,7 @@ class Controller:
414
424
  )
415
425
  )
416
426
 
417
- def on_link_central_disconnected(self, peer_address, reason):
427
+ def on_link_disconnected(self, peer_address, reason):
418
428
  '''
419
429
  Called when an active disconnection occurs from a peer
420
430
  '''
@@ -431,6 +441,17 @@ class Controller:
431
441
 
432
442
  # Remove the connection
433
443
  del self.peripheral_connections[peer_address]
444
+ elif connection := self.central_connections.get(peer_address):
445
+ self.send_hci_packet(
446
+ HCI_Disconnection_Complete_Event(
447
+ status=HCI_SUCCESS,
448
+ connection_handle=connection.handle,
449
+ reason=reason,
450
+ )
451
+ )
452
+
453
+ # Remove the connection
454
+ del self.central_connections[peer_address]
434
455
  else:
435
456
  logger.warning(f'!!! No peripheral connection found for {peer_address}')
436
457
 
@@ -479,7 +500,7 @@ class Controller:
479
500
  )
480
501
  )
481
502
 
482
- def on_link_peripheral_disconnection_complete(self, disconnection_command, status):
503
+ def on_link_disconnection_complete(self, disconnection_command, status):
483
504
  '''
484
505
  Called when a disconnection has been completed
485
506
  '''
@@ -499,26 +520,11 @@ class Controller:
499
520
  ):
500
521
  logger.debug(f'CENTRAL Connection removed: {connection}')
501
522
  del self.central_connections[connection.peer_address]
502
-
503
- def on_link_peripheral_disconnected(self, peer_address):
504
- '''
505
- Called when a connection to a peripheral is broken
506
- '''
507
-
508
- # Send a disconnection complete event
509
- if connection := self.central_connections.get(peer_address):
510
- self.send_hci_packet(
511
- HCI_Disconnection_Complete_Event(
512
- status=HCI_SUCCESS,
513
- connection_handle=connection.handle,
514
- reason=HCI_CONNECTION_TIMEOUT_ERROR,
515
- )
516
- )
517
-
518
- # Remove the connection
519
- del self.central_connections[peer_address]
520
- else:
521
- logger.warning(f'!!! No central connection found for {peer_address}')
523
+ elif connection := self.find_peripheral_connection_by_handle(
524
+ disconnection_command.connection_handle
525
+ ):
526
+ logger.debug(f'PERIPHERAL Connection removed: {connection}')
527
+ del self.peripheral_connections[connection.peer_address]
522
528
 
523
529
  def on_link_encrypted(self, peer_address, _rand, _ediv, _ltk):
524
530
  # For now, just setup the encryption without asking the host
@@ -877,6 +883,14 @@ class Controller:
877
883
  else:
878
884
  # Remove the connection
879
885
  del self.central_connections[connection.peer_address]
886
+ elif connection := self.find_peripheral_connection_by_handle(handle):
887
+ if self.link:
888
+ self.link.disconnect(
889
+ self.random_address, connection.peer_address, command
890
+ )
891
+ else:
892
+ # Remove the connection
893
+ del self.peripheral_connections[connection.peer_address]
880
894
  elif connection := self.find_classic_connection_by_handle(handle):
881
895
  if self.link:
882
896
  self.link.classic_disconnect(
@@ -1259,6 +1273,56 @@ class Controller:
1259
1273
  )
1260
1274
  return bytes([HCI_SUCCESS]) + bd_addr
1261
1275
 
1276
+ def on_hci_le_set_default_subrate_command(
1277
+ self, command: hci.HCI_LE_Set_Default_Subrate_Command
1278
+ ):
1279
+ '''
1280
+ See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
1281
+ '''
1282
+
1283
+ if (
1284
+ command.subrate_max * (command.max_latency) > 500
1285
+ or command.subrate_max < command.subrate_min
1286
+ or command.continuation_number >= command.subrate_max
1287
+ ):
1288
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1289
+
1290
+ return bytes([HCI_SUCCESS])
1291
+
1292
+ def on_hci_le_subrate_request_command(
1293
+ self, command: hci.HCI_LE_Subrate_Request_Command
1294
+ ):
1295
+ '''
1296
+ See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
1297
+ '''
1298
+ if (
1299
+ command.subrate_max * (command.max_latency) > 500
1300
+ or command.continuation_number < command.continuation_number
1301
+ or command.subrate_max < command.subrate_min
1302
+ or command.continuation_number >= command.subrate_max
1303
+ ):
1304
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1305
+
1306
+ self.send_hci_packet(
1307
+ hci.HCI_Command_Status_Event(
1308
+ status=hci.HCI_SUCCESS,
1309
+ num_hci_command_packets=1,
1310
+ command_opcode=command.op_code,
1311
+ )
1312
+ )
1313
+
1314
+ self.send_hci_packet(
1315
+ hci.HCI_LE_Subrate_Change_Event(
1316
+ status=hci.HCI_SUCCESS,
1317
+ connection_handle=command.connection_handle,
1318
+ subrate_factor=2,
1319
+ peripheral_latency=2,
1320
+ continuation_number=command.continuation_number,
1321
+ supervision_timeout=command.supervision_timeout,
1322
+ )
1323
+ )
1324
+ return None
1325
+
1262
1326
  def on_hci_le_set_event_mask_command(self, command):
1263
1327
  '''
1264
1328
  See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
@@ -1794,14 +1858,57 @@ class Controller:
1794
1858
  )
1795
1859
  )
1796
1860
 
1797
- def on_hci_le_setup_iso_data_path_command(self, command):
1861
+ def on_hci_le_setup_iso_data_path_command(
1862
+ self, command: hci.HCI_LE_Setup_ISO_Data_Path_Command
1863
+ ) -> bytes:
1798
1864
  '''
1799
1865
  See Bluetooth spec Vol 4, Part E - 7.8.109 LE Setup ISO Data Path Command
1800
1866
  '''
1867
+ if not (iso_link := self.find_iso_link_by_handle(command.connection_handle)):
1868
+ return struct.pack(
1869
+ '<BH',
1870
+ HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1871
+ command.connection_handle,
1872
+ )
1873
+ if command.data_path_direction in iso_link.data_paths:
1874
+ return struct.pack(
1875
+ '<BH',
1876
+ HCI_COMMAND_DISALLOWED_ERROR,
1877
+ command.connection_handle,
1878
+ )
1879
+ iso_link.data_paths.add(command.data_path_direction)
1801
1880
  return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
1802
1881
 
1803
- def on_hci_le_remove_iso_data_path_command(self, command):
1882
+ def on_hci_le_remove_iso_data_path_command(
1883
+ self, command: hci.HCI_LE_Remove_ISO_Data_Path_Command
1884
+ ) -> bytes:
1804
1885
  '''
1805
1886
  See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
1806
1887
  '''
1888
+ if not (iso_link := self.find_iso_link_by_handle(command.connection_handle)):
1889
+ return struct.pack(
1890
+ '<BH',
1891
+ HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1892
+ command.connection_handle,
1893
+ )
1894
+ data_paths: set[int] = set(
1895
+ direction
1896
+ for direction in hci.HCI_LE_Setup_ISO_Data_Path_Command.Direction
1897
+ if (1 << direction) & command.data_path_direction
1898
+ )
1899
+ if not data_paths.issubset(iso_link.data_paths):
1900
+ return struct.pack(
1901
+ '<BH',
1902
+ HCI_COMMAND_DISALLOWED_ERROR,
1903
+ command.connection_handle,
1904
+ )
1905
+ iso_link.data_paths.difference_update(data_paths)
1807
1906
  return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
1907
+
1908
+ def on_hci_le_set_host_feature_command(
1909
+ self, _command: hci.HCI_LE_Set_Host_Feature_Command
1910
+ ):
1911
+ '''
1912
+ See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
1913
+ '''
1914
+ return bytes([HCI_SUCCESS])