bumble 0.0.212__py3-none-any.whl → 0.0.214__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 (92) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +6 -0
  3. bumble/apps/README.md +0 -3
  4. bumble/apps/auracast.py +14 -11
  5. bumble/apps/bench.py +482 -37
  6. bumble/apps/console.py +3 -3
  7. bumble/apps/controller_info.py +44 -12
  8. bumble/apps/controller_loopback.py +7 -7
  9. bumble/apps/controllers.py +4 -5
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +5 -5
  12. bumble/apps/gg_bridge.py +5 -5
  13. bumble/apps/hci_bridge.py +5 -4
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +8 -3
  16. bumble/apps/pair.py +19 -11
  17. bumble/apps/pandora_server.py +2 -2
  18. bumble/apps/player/player.py +2 -3
  19. bumble/apps/rfcomm_bridge.py +3 -4
  20. bumble/apps/scan.py +4 -5
  21. bumble/apps/show.py +6 -4
  22. bumble/apps/speaker/speaker.html +1 -0
  23. bumble/apps/speaker/speaker.js +113 -62
  24. bumble/apps/speaker/speaker.py +123 -19
  25. bumble/apps/unbond.py +2 -3
  26. bumble/apps/usb_probe.py +2 -3
  27. bumble/at.py +4 -4
  28. bumble/att.py +2 -6
  29. bumble/avc.py +7 -7
  30. bumble/avctp.py +3 -3
  31. bumble/avdtp.py +16 -20
  32. bumble/avrcp.py +42 -54
  33. bumble/colors.py +2 -2
  34. bumble/controller.py +174 -45
  35. bumble/device.py +398 -182
  36. bumble/drivers/__init__.py +2 -2
  37. bumble/drivers/common.py +0 -2
  38. bumble/drivers/intel.py +37 -40
  39. bumble/drivers/rtk.py +28 -35
  40. bumble/gatt.py +4 -4
  41. bumble/gatt_adapters.py +4 -5
  42. bumble/gatt_client.py +26 -31
  43. bumble/gatt_server.py +7 -11
  44. bumble/hci.py +2648 -2909
  45. bumble/helpers.py +4 -5
  46. bumble/hfp.py +32 -37
  47. bumble/host.py +104 -35
  48. bumble/keys.py +5 -5
  49. bumble/l2cap.py +312 -409
  50. bumble/link.py +16 -280
  51. bumble/logging.py +65 -0
  52. bumble/pairing.py +23 -20
  53. bumble/pandora/__init__.py +2 -2
  54. bumble/pandora/config.py +2 -2
  55. bumble/pandora/device.py +6 -6
  56. bumble/pandora/host.py +27 -28
  57. bumble/pandora/l2cap.py +2 -2
  58. bumble/pandora/security.py +6 -6
  59. bumble/pandora/utils.py +3 -3
  60. bumble/profiles/ams.py +404 -0
  61. bumble/profiles/ascs.py +142 -131
  62. bumble/profiles/asha.py +2 -2
  63. bumble/profiles/bap.py +3 -4
  64. bumble/profiles/csip.py +2 -2
  65. bumble/profiles/device_information_service.py +2 -2
  66. bumble/profiles/gap.py +2 -2
  67. bumble/profiles/hap.py +34 -33
  68. bumble/profiles/le_audio.py +4 -4
  69. bumble/profiles/mcp.py +4 -4
  70. bumble/profiles/vcs.py +3 -5
  71. bumble/rfcomm.py +10 -10
  72. bumble/rtp.py +1 -2
  73. bumble/sdp.py +2 -2
  74. bumble/smp.py +62 -63
  75. bumble/tools/intel_util.py +3 -2
  76. bumble/tools/rtk_util.py +6 -5
  77. bumble/transport/__init__.py +2 -16
  78. bumble/transport/android_netsim.py +5 -5
  79. bumble/transport/common.py +4 -4
  80. bumble/transport/pyusb.py +2 -2
  81. bumble/utils.py +2 -5
  82. bumble/vendor/android/hci.py +118 -200
  83. bumble/vendor/zephyr/hci.py +32 -27
  84. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/METADATA +4 -3
  85. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/RECORD +89 -90
  86. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/WHEEL +1 -1
  87. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/entry_points.txt +0 -1
  88. bumble/apps/link_relay/__init__.py +0 -0
  89. bumble/apps/link_relay/link_relay.py +0 -289
  90. bumble/apps/link_relay/logging.yml +0 -21
  91. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/licenses/LICENSE +0 -0
  92. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/top_level.txt +0 -0
bumble/controller.py CHANGED
@@ -27,7 +27,7 @@ from bumble.colors import color
27
27
  from bumble.core import (
28
28
  PhysicalTransport,
29
29
  )
30
-
30
+ from bumble import hci
31
31
  from bumble.hci import (
32
32
  HCI_ACL_DATA_PACKET,
33
33
  HCI_COMMAND_DISALLOWED_ERROR,
@@ -63,7 +63,7 @@ from bumble.hci import (
63
63
  HCI_Packet,
64
64
  HCI_Role_Change_Event,
65
65
  )
66
- from typing import Optional, Union, Dict, Any, TYPE_CHECKING
66
+ from typing import Optional, Union, Any, TYPE_CHECKING
67
67
 
68
68
  if TYPE_CHECKING:
69
69
  from bumble.link import LocalLink
@@ -108,7 +108,9 @@ class Connection:
108
108
  def on_hci_acl_data_packet(self, packet):
109
109
  self.assembler.feed_packet(packet)
110
110
  self.controller.send_hci_packet(
111
- HCI_Number_Of_Completed_Packets_Event([(self.handle, 1)])
111
+ HCI_Number_Of_Completed_Packets_Event(
112
+ connection_handles=[self.handle], num_completed_packets=[1]
113
+ )
112
114
  )
113
115
 
114
116
  def on_acl_pdu(self, data):
@@ -132,17 +134,17 @@ class Controller:
132
134
  self.hci_sink = None
133
135
  self.link = link
134
136
 
135
- self.central_connections: Dict[Address, Connection] = (
137
+ self.central_connections: dict[Address, Connection] = (
136
138
  {}
137
139
  ) # Connections where this controller is the central
138
- self.peripheral_connections: Dict[Address, Connection] = (
140
+ self.peripheral_connections: dict[Address, Connection] = (
139
141
  {}
140
142
  ) # Connections where this controller is the peripheral
141
- self.classic_connections: Dict[Address, Connection] = (
143
+ self.classic_connections: dict[Address, Connection] = (
142
144
  {}
143
145
  ) # Connections in BR/EDR
144
- self.central_cis_links: Dict[int, CisLink] = {} # CIS links by handle
145
- self.peripheral_cis_links: Dict[int, CisLink] = {} # CIS links by handle
146
+ self.central_cis_links: dict[int, CisLink] = {} # CIS links by handle
147
+ self.peripheral_cis_links: dict[int, CisLink] = {} # CIS links by handle
146
148
 
147
149
  self.hci_version = HCI_VERSION_BLUETOOTH_CORE_5_0
148
150
  self.hci_revision = 0
@@ -368,6 +370,12 @@ class Controller:
368
370
  return connection
369
371
  return None
370
372
 
373
+ def find_peripheral_connection_by_handle(self, handle):
374
+ for connection in self.peripheral_connections.values():
375
+ if connection.handle == handle:
376
+ return connection
377
+ return None
378
+
371
379
  def find_classic_connection_by_handle(self, handle):
372
380
  for connection in self.classic_connections.values():
373
381
  if connection.handle == handle:
@@ -392,7 +400,7 @@ class Controller:
392
400
  peer_address=peer_address,
393
401
  link=self.link,
394
402
  transport=PhysicalTransport.LE,
395
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
403
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
396
404
  )
397
405
  self.peripheral_connections[peer_address] = connection
398
406
  logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
@@ -412,7 +420,7 @@ class Controller:
412
420
  )
413
421
  )
414
422
 
415
- def on_link_central_disconnected(self, peer_address, reason):
423
+ def on_link_disconnected(self, peer_address, reason):
416
424
  '''
417
425
  Called when an active disconnection occurs from a peer
418
426
  '''
@@ -429,6 +437,17 @@ class Controller:
429
437
 
430
438
  # Remove the connection
431
439
  del self.peripheral_connections[peer_address]
440
+ elif connection := self.central_connections.get(peer_address):
441
+ self.send_hci_packet(
442
+ HCI_Disconnection_Complete_Event(
443
+ status=HCI_SUCCESS,
444
+ connection_handle=connection.handle,
445
+ reason=reason,
446
+ )
447
+ )
448
+
449
+ # Remove the connection
450
+ del self.central_connections[peer_address]
432
451
  else:
433
452
  logger.warning(f'!!! No peripheral connection found for {peer_address}')
434
453
 
@@ -452,7 +471,7 @@ class Controller:
452
471
  peer_address=peer_address,
453
472
  link=self.link,
454
473
  transport=PhysicalTransport.LE,
455
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
474
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
456
475
  )
457
476
  self.central_connections[peer_address] = connection
458
477
  logger.debug(
@@ -477,7 +496,7 @@ class Controller:
477
496
  )
478
497
  )
479
498
 
480
- def on_link_peripheral_disconnection_complete(self, disconnection_command, status):
499
+ def on_link_disconnection_complete(self, disconnection_command, status):
481
500
  '''
482
501
  Called when a disconnection has been completed
483
502
  '''
@@ -497,26 +516,11 @@ class Controller:
497
516
  ):
498
517
  logger.debug(f'CENTRAL Connection removed: {connection}')
499
518
  del self.central_connections[connection.peer_address]
500
-
501
- def on_link_peripheral_disconnected(self, peer_address):
502
- '''
503
- Called when a connection to a peripheral is broken
504
- '''
505
-
506
- # Send a disconnection complete event
507
- if connection := self.central_connections.get(peer_address):
508
- self.send_hci_packet(
509
- HCI_Disconnection_Complete_Event(
510
- status=HCI_SUCCESS,
511
- connection_handle=connection.handle,
512
- reason=HCI_CONNECTION_TIMEOUT_ERROR,
513
- )
514
- )
515
-
516
- # Remove the connection
517
- del self.central_connections[peer_address]
518
- else:
519
- logger.warning(f'!!! No central connection found for {peer_address}')
519
+ elif connection := self.find_peripheral_connection_by_handle(
520
+ disconnection_command.connection_handle
521
+ ):
522
+ logger.debug(f'PERIPHERAL Connection removed: {connection}')
523
+ del self.peripheral_connections[connection.peer_address]
520
524
 
521
525
  def on_link_encrypted(self, peer_address, _rand, _ediv, _ltk):
522
526
  # For now, just setup the encryption without asking the host
@@ -542,15 +546,14 @@ class Controller:
542
546
  acl_packet = HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
543
547
  self.send_hci_packet(acl_packet)
544
548
 
545
- def on_link_advertising_data(self, sender_address, data):
549
+ def on_link_advertising_data(self, sender_address: Address, data: bytes):
546
550
  # Ignore if we're not scanning
547
551
  if self.le_scan_enable == 0:
548
552
  return
549
553
 
550
554
  # Send a scan report
551
555
  report = HCI_LE_Advertising_Report_Event.Report(
552
- HCI_LE_Advertising_Report_Event.Report.FIELDS,
553
- event_type=HCI_LE_Advertising_Report_Event.ADV_IND,
556
+ event_type=HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
554
557
  address_type=sender_address.address_type,
555
558
  address=sender_address,
556
559
  data=data,
@@ -560,8 +563,7 @@ class Controller:
560
563
 
561
564
  # Simulate a scan response
562
565
  report = HCI_LE_Advertising_Report_Event.Report(
563
- HCI_LE_Advertising_Report_Event.Report.FIELDS,
564
- event_type=HCI_LE_Advertising_Report_Event.SCAN_RSP,
566
+ event_type=HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
565
567
  address_type=sender_address.address_type,
566
568
  address=sender_address,
567
569
  data=data,
@@ -618,8 +620,8 @@ class Controller:
618
620
  cis_sync_delay=0,
619
621
  transport_latency_c_to_p=0,
620
622
  transport_latency_p_to_c=0,
621
- phy_c_to_p=0,
622
- phy_p_to_c=0,
623
+ phy_c_to_p=1,
624
+ phy_p_to_c=1,
623
625
  nse=0,
624
626
  bn_c_to_p=0,
625
627
  bn_p_to_c=0,
@@ -695,7 +697,7 @@ class Controller:
695
697
  peer_address=peer_address,
696
698
  link=self.link,
697
699
  transport=PhysicalTransport.BR_EDR,
698
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
700
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
699
701
  )
700
702
  self.classic_connections[peer_address] = connection
701
703
  logger.debug(
@@ -709,7 +711,7 @@ class Controller:
709
711
  connection_handle=connection_handle,
710
712
  bd_addr=peer_address,
711
713
  encryption_enabled=False,
712
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
714
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
713
715
  )
714
716
  )
715
717
  else:
@@ -720,7 +722,7 @@ class Controller:
720
722
  connection_handle=0,
721
723
  bd_addr=peer_address,
722
724
  encryption_enabled=False,
723
- link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE,
725
+ link_type=HCI_Connection_Complete_Event.LinkType.ACL,
724
726
  )
725
727
  )
726
728
 
@@ -877,6 +879,14 @@ class Controller:
877
879
  else:
878
880
  # Remove the connection
879
881
  del self.central_connections[connection.peer_address]
882
+ elif connection := self.find_peripheral_connection_by_handle(handle):
883
+ if self.link:
884
+ self.link.disconnect(
885
+ self.random_address, connection.peer_address, command
886
+ )
887
+ else:
888
+ # Remove the connection
889
+ del self.peripheral_connections[connection.peer_address]
880
890
  elif connection := self.find_classic_connection_by_handle(handle):
881
891
  if self.link:
882
892
  self.link.classic_disconnect(
@@ -945,7 +955,7 @@ class Controller:
945
955
  )
946
956
  )
947
957
  self.link.classic_sco_connect(
948
- self, connection.peer_address, HCI_Connection_Complete_Event.ESCO_LINK_TYPE
958
+ self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
949
959
  )
950
960
 
951
961
  def on_hci_enhanced_accept_synchronous_connection_request_command(self, command):
@@ -974,10 +984,71 @@ class Controller:
974
984
  )
975
985
  )
976
986
  self.link.classic_accept_sco_connection(
977
- self, connection.peer_address, HCI_Connection_Complete_Event.ESCO_LINK_TYPE
987
+ self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
978
988
  )
979
989
 
980
- def on_hci_switch_role_command(self, command):
990
+ def on_hci_sniff_mode_command(self, command: hci.HCI_Sniff_Mode_Command):
991
+ '''
992
+ See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
993
+ '''
994
+ if self.link is None:
995
+ self.send_hci_packet(
996
+ hci.HCI_Command_Status_Event(
997
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
998
+ num_hci_command_packets=1,
999
+ command_opcode=command.op_code,
1000
+ )
1001
+ )
1002
+ return
1003
+
1004
+ self.send_hci_packet(
1005
+ hci.HCI_Command_Status_Event(
1006
+ status=HCI_SUCCESS,
1007
+ num_hci_command_packets=1,
1008
+ command_opcode=command.op_code,
1009
+ )
1010
+ )
1011
+ self.send_hci_packet(
1012
+ hci.HCI_Mode_Change_Event(
1013
+ status=HCI_SUCCESS,
1014
+ connection_handle=command.connection_handle,
1015
+ current_mode=hci.HCI_Mode_Change_Event.Mode.SNIFF,
1016
+ interval=2,
1017
+ )
1018
+ )
1019
+
1020
+ def on_hci_exit_sniff_mode_command(self, command: hci.HCI_Exit_Sniff_Mode_Command):
1021
+ '''
1022
+ See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
1023
+ '''
1024
+
1025
+ if self.link is None:
1026
+ self.send_hci_packet(
1027
+ hci.HCI_Command_Status_Event(
1028
+ status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
1029
+ num_hci_command_packets=1,
1030
+ command_opcode=command.op_code,
1031
+ )
1032
+ )
1033
+ return
1034
+
1035
+ self.send_hci_packet(
1036
+ hci.HCI_Command_Status_Event(
1037
+ status=HCI_SUCCESS,
1038
+ num_hci_command_packets=1,
1039
+ command_opcode=command.op_code,
1040
+ )
1041
+ )
1042
+ self.send_hci_packet(
1043
+ hci.HCI_Mode_Change_Event(
1044
+ status=HCI_SUCCESS,
1045
+ connection_handle=command.connection_handle,
1046
+ current_mode=hci.HCI_Mode_Change_Event.Mode.ACTIVE,
1047
+ interval=2,
1048
+ )
1049
+ )
1050
+
1051
+ def on_hci_switch_role_command(self, command: hci.HCI_Switch_Role_Command):
981
1052
  '''
982
1053
  See Bluetooth spec Vol 4, Part E - 7.2.8 Switch Role command
983
1054
  '''
@@ -1198,6 +1269,56 @@ class Controller:
1198
1269
  )
1199
1270
  return bytes([HCI_SUCCESS]) + bd_addr
1200
1271
 
1272
+ def on_hci_le_set_default_subrate_command(
1273
+ self, command: hci.HCI_LE_Set_Default_Subrate_Command
1274
+ ):
1275
+ '''
1276
+ See Bluetooth spec Vol 6, Part E - 7.8.123 LE Set Event Mask Command
1277
+ '''
1278
+
1279
+ if (
1280
+ command.subrate_max * (command.max_latency) > 500
1281
+ or command.subrate_max < command.subrate_min
1282
+ or command.continuation_number >= command.subrate_max
1283
+ ):
1284
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1285
+
1286
+ return bytes([HCI_SUCCESS])
1287
+
1288
+ def on_hci_le_subrate_request_command(
1289
+ self, command: hci.HCI_LE_Subrate_Request_Command
1290
+ ):
1291
+ '''
1292
+ See Bluetooth spec Vol 6, Part E - 7.8.124 LE Subrate Request command
1293
+ '''
1294
+ if (
1295
+ command.subrate_max * (command.max_latency) > 500
1296
+ or command.continuation_number < command.continuation_number
1297
+ or command.subrate_max < command.subrate_min
1298
+ or command.continuation_number >= command.subrate_max
1299
+ ):
1300
+ return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR])
1301
+
1302
+ self.send_hci_packet(
1303
+ hci.HCI_Command_Status_Event(
1304
+ status=hci.HCI_SUCCESS,
1305
+ num_hci_command_packets=1,
1306
+ command_opcode=command.op_code,
1307
+ )
1308
+ )
1309
+
1310
+ self.send_hci_packet(
1311
+ hci.HCI_LE_Subrate_Change_Event(
1312
+ status=hci.HCI_SUCCESS,
1313
+ connection_handle=command.connection_handle,
1314
+ subrate_factor=2,
1315
+ peripheral_latency=2,
1316
+ continuation_number=command.continuation_number,
1317
+ supervision_timeout=command.supervision_timeout,
1318
+ )
1319
+ )
1320
+ return None
1321
+
1201
1322
  def on_hci_le_set_event_mask_command(self, command):
1202
1323
  '''
1203
1324
  See Bluetooth spec Vol 4, Part E - 7.8.1 LE Set Event Mask Command
@@ -1744,3 +1865,11 @@ class Controller:
1744
1865
  See Bluetooth spec Vol 4, Part E - 7.8.110 LE Remove ISO Data Path Command
1745
1866
  '''
1746
1867
  return struct.pack('<BH', HCI_SUCCESS, command.connection_handle)
1868
+
1869
+ def on_hci_le_set_host_feature_command(
1870
+ self, _command: hci.HCI_LE_Set_Host_Feature_Command
1871
+ ):
1872
+ '''
1873
+ See Bluetooth spec Vol 4, Part E - 7.8.115 LE Set Host Feature command
1874
+ '''
1875
+ return bytes([HCI_SUCCESS])