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.
- bumble/_version.py +16 -3
- bumble/a2dp.py +15 -16
- bumble/apps/auracast.py +14 -38
- bumble/apps/bench.py +10 -15
- bumble/apps/ble_rpa_tool.py +1 -0
- bumble/apps/console.py +22 -25
- bumble/apps/controller_info.py +20 -25
- bumble/apps/controller_loopback.py +6 -10
- bumble/apps/controllers.py +2 -3
- bumble/apps/device_info.py +4 -5
- bumble/apps/gatt_dump.py +3 -3
- bumble/apps/gg_bridge.py +7 -8
- bumble/apps/hci_bridge.py +4 -3
- bumble/apps/l2cap_bridge.py +5 -5
- bumble/apps/lea_unicast/app.py +16 -26
- bumble/apps/pair.py +30 -43
- bumble/apps/pandora_server.py +5 -4
- bumble/apps/player/player.py +20 -24
- bumble/apps/rfcomm_bridge.py +4 -10
- bumble/apps/scan.py +17 -8
- bumble/apps/show.py +4 -5
- bumble/apps/speaker/speaker.py +23 -27
- bumble/apps/unbond.py +3 -3
- bumble/apps/usb_probe.py +2 -4
- bumble/att.py +241 -246
- bumble/audio/io.py +5 -9
- bumble/avc.py +2 -2
- bumble/avctp.py +6 -7
- bumble/avdtp.py +19 -22
- bumble/avrcp.py +1097 -589
- bumble/codecs.py +2 -0
- bumble/controller.py +142 -35
- bumble/core.py +567 -248
- bumble/crypto/__init__.py +2 -2
- bumble/crypto/builtin.py +1 -1
- bumble/crypto/cryptography.py +2 -4
- bumble/data_types.py +1025 -0
- bumble/device.py +319 -267
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +3 -4
- bumble/drivers/rtk.py +26 -9
- bumble/gap.py +4 -4
- bumble/gatt.py +3 -2
- bumble/gatt_adapters.py +3 -11
- bumble/gatt_client.py +69 -81
- bumble/gatt_server.py +124 -124
- bumble/hci.py +114 -18
- bumble/helpers.py +19 -26
- bumble/hfp.py +10 -21
- bumble/hid.py +22 -16
- bumble/host.py +191 -103
- bumble/keys.py +5 -3
- bumble/l2cap.py +138 -104
- bumble/link.py +18 -19
- bumble/logging.py +65 -0
- bumble/pairing.py +7 -6
- bumble/pandora/__init__.py +9 -8
- bumble/pandora/config.py +3 -1
- bumble/pandora/device.py +3 -2
- bumble/pandora/host.py +38 -36
- bumble/pandora/l2cap.py +22 -21
- bumble/pandora/security.py +15 -15
- bumble/pandora/utils.py +5 -3
- bumble/profiles/aics.py +11 -11
- bumble/profiles/ams.py +403 -0
- bumble/profiles/ancs.py +6 -7
- bumble/profiles/ascs.py +14 -9
- bumble/profiles/asha.py +8 -12
- bumble/profiles/bap.py +11 -23
- bumble/profiles/bass.py +2 -7
- bumble/profiles/battery_service.py +3 -4
- bumble/profiles/cap.py +1 -2
- bumble/profiles/csip.py +2 -6
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +4 -4
- bumble/profiles/gatt_service.py +1 -4
- bumble/profiles/gmap.py +5 -5
- bumble/profiles/hap.py +62 -59
- bumble/profiles/heart_rate_service.py +5 -4
- bumble/profiles/le_audio.py +3 -1
- bumble/profiles/mcp.py +3 -7
- bumble/profiles/pacs.py +3 -6
- bumble/profiles/pbp.py +2 -0
- bumble/profiles/tmap.py +2 -3
- bumble/profiles/vcs.py +2 -8
- bumble/profiles/vocs.py +8 -8
- bumble/rfcomm.py +11 -14
- bumble/rtp.py +1 -0
- bumble/sdp.py +10 -8
- bumble/smp.py +151 -159
- bumble/snoop.py +5 -5
- bumble/tools/generate_company_id_list.py +1 -0
- bumble/tools/intel_fw_download.py +3 -3
- bumble/tools/intel_util.py +5 -4
- bumble/tools/rtk_fw_download.py +6 -3
- bumble/tools/rtk_util.py +26 -8
- bumble/transport/__init__.py +19 -15
- bumble/transport/android_emulator.py +8 -13
- bumble/transport/android_netsim.py +19 -18
- bumble/transport/common.py +12 -15
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +4 -6
- bumble/transport/pty.py +5 -6
- bumble/transport/pyusb.py +7 -10
- bumble/transport/serial.py +2 -1
- bumble/transport/tcp_client.py +2 -2
- bumble/transport/tcp_server.py +11 -14
- bumble/transport/udp.py +3 -3
- bumble/transport/unix.py +67 -1
- bumble/transport/usb.py +6 -6
- bumble/transport/vhci.py +0 -1
- bumble/transport/ws_client.py +2 -1
- bumble/transport/ws_server.py +3 -2
- bumble/utils.py +20 -5
- bumble/vendor/android/hci.py +1 -2
- bumble/vendor/zephyr/hci.py +0 -1
- {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/METADATA +4 -2
- bumble-0.0.215.dist-info/RECORD +183 -0
- bumble-0.0.213.dist-info/RECORD +0 -180
- {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/WHEEL +0 -0
- {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.213.dist-info → bumble-0.0.215.dist-info}/top_level.txt +0 -0
bumble/codecs.py
CHANGED
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
|
|
27
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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(
|
|
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(
|
|
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])
|