bumble 0.0.211__py3-none-any.whl → 0.0.213__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 +2 -2
- bumble/a2dp.py +6 -0
- bumble/apps/README.md +0 -3
- bumble/apps/auracast.py +11 -9
- bumble/apps/bench.py +482 -31
- bumble/apps/console.py +5 -5
- bumble/apps/controller_info.py +47 -10
- bumble/apps/controller_loopback.py +7 -3
- bumble/apps/controllers.py +2 -2
- bumble/apps/device_info.py +2 -2
- bumble/apps/gatt_dump.py +2 -2
- bumble/apps/gg_bridge.py +2 -2
- bumble/apps/hci_bridge.py +2 -2
- bumble/apps/l2cap_bridge.py +2 -2
- bumble/apps/lea_unicast/app.py +6 -1
- bumble/apps/pair.py +204 -43
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/scan.py +2 -2
- bumble/apps/show.py +4 -2
- bumble/apps/speaker/speaker.html +1 -0
- bumble/apps/speaker/speaker.js +113 -62
- bumble/apps/speaker/speaker.py +126 -18
- bumble/at.py +4 -4
- bumble/att.py +15 -18
- bumble/avc.py +7 -7
- bumble/avctp.py +5 -5
- bumble/avdtp.py +138 -88
- bumble/avrcp.py +52 -58
- bumble/colors.py +2 -2
- bumble/controller.py +84 -23
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +688 -345
- bumble/drivers/__init__.py +2 -2
- bumble/drivers/common.py +0 -2
- bumble/drivers/intel.py +40 -40
- bumble/drivers/rtk.py +28 -35
- bumble/gatt.py +7 -9
- bumble/gatt_adapters.py +4 -5
- bumble/gatt_client.py +31 -34
- bumble/gatt_server.py +15 -17
- bumble/hci.py +2635 -2878
- bumble/helpers.py +4 -5
- bumble/hfp.py +76 -57
- bumble/hid.py +24 -12
- bumble/host.py +117 -34
- bumble/keys.py +68 -52
- bumble/l2cap.py +329 -403
- bumble/link.py +6 -270
- bumble/pairing.py +23 -20
- bumble/pandora/__init__.py +1 -1
- bumble/pandora/config.py +2 -2
- bumble/pandora/device.py +6 -6
- bumble/pandora/host.py +38 -39
- bumble/pandora/l2cap.py +4 -4
- bumble/pandora/security.py +73 -57
- bumble/pandora/utils.py +3 -3
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +143 -136
- bumble/profiles/asha.py +13 -8
- bumble/profiles/bap.py +3 -4
- bumble/profiles/csip.py +3 -5
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +2 -2
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +42 -58
- bumble/profiles/le_audio.py +4 -4
- bumble/profiles/mcp.py +16 -13
- bumble/profiles/vcs.py +8 -10
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +27 -18
- bumble/rtp.py +1 -2
- bumble/sdp.py +2 -2
- bumble/smp.py +71 -69
- bumble/tools/rtk_util.py +2 -2
- bumble/transport/__init__.py +2 -16
- bumble/transport/android_netsim.py +5 -5
- bumble/transport/common.py +4 -4
- bumble/transport/pyusb.py +2 -2
- bumble/utils.py +2 -5
- bumble/vendor/android/hci.py +118 -200
- bumble/vendor/zephyr/hci.py +32 -27
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/METADATA +5 -5
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/RECORD +92 -93
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
- bumble/apps/link_relay/__init__.py +0 -0
- bumble/apps/link_relay/link_relay.py +0 -289
- bumble/apps/link_relay/logging.yml +0 -21
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
bumble/smp.py
CHANGED
|
@@ -26,18 +26,13 @@ from __future__ import annotations
|
|
|
26
26
|
import logging
|
|
27
27
|
import asyncio
|
|
28
28
|
import enum
|
|
29
|
-
import secrets
|
|
30
29
|
from dataclasses import dataclass
|
|
31
30
|
from typing import (
|
|
32
31
|
TYPE_CHECKING,
|
|
33
32
|
Any,
|
|
34
33
|
Awaitable,
|
|
35
34
|
Callable,
|
|
36
|
-
Dict,
|
|
37
|
-
List,
|
|
38
35
|
Optional,
|
|
39
|
-
Tuple,
|
|
40
|
-
Type,
|
|
41
36
|
cast,
|
|
42
37
|
)
|
|
43
38
|
|
|
@@ -210,7 +205,7 @@ class SMP_Command:
|
|
|
210
205
|
See Bluetooth spec @ Vol 3, Part H - 3 SECURITY MANAGER PROTOCOL
|
|
211
206
|
'''
|
|
212
207
|
|
|
213
|
-
smp_classes:
|
|
208
|
+
smp_classes: dict[int, type[SMP_Command]] = {}
|
|
214
209
|
fields: Any
|
|
215
210
|
code = 0
|
|
216
211
|
name = ''
|
|
@@ -254,7 +249,7 @@ class SMP_Command:
|
|
|
254
249
|
|
|
255
250
|
@staticmethod
|
|
256
251
|
def key_distribution_str(value: int) -> str:
|
|
257
|
-
key_types:
|
|
252
|
+
key_types: list[str] = []
|
|
258
253
|
if value & SMP_ENC_KEY_DISTRIBUTION_FLAG:
|
|
259
254
|
key_types.append('ENC')
|
|
260
255
|
if value & SMP_ID_KEY_DISTRIBUTION_FLAG:
|
|
@@ -706,7 +701,7 @@ class Session:
|
|
|
706
701
|
self.peer_identity_resolving_key = None
|
|
707
702
|
self.peer_bd_addr: Optional[Address] = None
|
|
708
703
|
self.peer_signature_key = None
|
|
709
|
-
self.peer_expected_distributions:
|
|
704
|
+
self.peer_expected_distributions: list[type[SMP_Command]] = []
|
|
710
705
|
self.dh_key = b''
|
|
711
706
|
self.confirm_value = None
|
|
712
707
|
self.passkey: Optional[int] = None
|
|
@@ -724,12 +719,13 @@ class Session:
|
|
|
724
719
|
self.is_responder = not self.is_initiator
|
|
725
720
|
|
|
726
721
|
# Listen for connection events
|
|
727
|
-
connection.on(
|
|
722
|
+
connection.on(connection.EVENT_DISCONNECTION, self.on_disconnection)
|
|
728
723
|
connection.on(
|
|
729
|
-
|
|
724
|
+
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE,
|
|
725
|
+
self.on_connection_encryption_change,
|
|
730
726
|
)
|
|
731
727
|
connection.on(
|
|
732
|
-
|
|
728
|
+
connection.EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH,
|
|
733
729
|
self.on_connection_encryption_key_refresh,
|
|
734
730
|
)
|
|
735
731
|
|
|
@@ -766,7 +762,9 @@ class Session:
|
|
|
766
762
|
|
|
767
763
|
# OOB
|
|
768
764
|
self.oob_data_flag = (
|
|
769
|
-
1
|
|
765
|
+
1
|
|
766
|
+
if pairing_config.oob and (not self.sc or pairing_config.oob.peer_data)
|
|
767
|
+
else 0
|
|
770
768
|
)
|
|
771
769
|
|
|
772
770
|
# Set up addresses
|
|
@@ -813,7 +811,7 @@ class Session:
|
|
|
813
811
|
self.tk = bytes(16)
|
|
814
812
|
|
|
815
813
|
@property
|
|
816
|
-
def pkx(self) ->
|
|
814
|
+
def pkx(self) -> tuple[bytes, bytes]:
|
|
817
815
|
return (self.ecc_key.x[::-1], self.peer_public_key_x)
|
|
818
816
|
|
|
819
817
|
@property
|
|
@@ -825,7 +823,7 @@ class Session:
|
|
|
825
823
|
return self.pkx[0 if self.is_responder else 1]
|
|
826
824
|
|
|
827
825
|
@property
|
|
828
|
-
def nx(self) ->
|
|
826
|
+
def nx(self) -> tuple[bytes, bytes]:
|
|
829
827
|
assert self.peer_random_value
|
|
830
828
|
return (self.r, self.peer_random_value)
|
|
831
829
|
|
|
@@ -899,7 +897,7 @@ class Session:
|
|
|
899
897
|
|
|
900
898
|
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
|
901
899
|
|
|
902
|
-
|
|
900
|
+
self.connection.cancel_on_disconnection(prompt())
|
|
903
901
|
|
|
904
902
|
def prompt_user_for_numeric_comparison(
|
|
905
903
|
self, code: int, next_steps: Callable[[], None]
|
|
@@ -918,7 +916,7 @@ class Session:
|
|
|
918
916
|
|
|
919
917
|
self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR)
|
|
920
918
|
|
|
921
|
-
|
|
919
|
+
self.connection.cancel_on_disconnection(prompt())
|
|
922
920
|
|
|
923
921
|
def prompt_user_for_number(self, next_steps: Callable[[int], None]) -> None:
|
|
924
922
|
async def prompt() -> None:
|
|
@@ -935,12 +933,11 @@ class Session:
|
|
|
935
933
|
logger.warning(f'exception while prompting: {error}')
|
|
936
934
|
self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR)
|
|
937
935
|
|
|
938
|
-
|
|
936
|
+
self.connection.cancel_on_disconnection(prompt())
|
|
939
937
|
|
|
940
|
-
def display_passkey(self) -> None:
|
|
941
|
-
#
|
|
942
|
-
self.passkey =
|
|
943
|
-
assert self.passkey is not None
|
|
938
|
+
async def display_passkey(self) -> None:
|
|
939
|
+
# Get the passkey value from the delegate
|
|
940
|
+
self.passkey = await self.pairing_config.delegate.generate_passkey()
|
|
944
941
|
logger.debug(f'Pairing PIN CODE: {self.passkey:06}')
|
|
945
942
|
self.passkey_ready.set()
|
|
946
943
|
|
|
@@ -949,14 +946,7 @@ class Session:
|
|
|
949
946
|
self.tk = self.passkey.to_bytes(16, byteorder='little')
|
|
950
947
|
logger.debug(f'TK from passkey = {self.tk.hex()}')
|
|
951
948
|
|
|
952
|
-
|
|
953
|
-
utils.cancel_on_event(
|
|
954
|
-
self.connection,
|
|
955
|
-
'disconnection',
|
|
956
|
-
self.pairing_config.delegate.display_number(self.passkey, digits=6),
|
|
957
|
-
)
|
|
958
|
-
except Exception as error:
|
|
959
|
-
logger.warning(f'exception while displaying number: {error}')
|
|
949
|
+
await self.pairing_config.delegate.display_number(self.passkey, digits=6)
|
|
960
950
|
|
|
961
951
|
def input_passkey(self, next_steps: Optional[Callable[[], None]] = None) -> None:
|
|
962
952
|
# Prompt the user for the passkey displayed on the peer
|
|
@@ -978,9 +968,16 @@ class Session:
|
|
|
978
968
|
self, next_steps: Optional[Callable[[], None]] = None
|
|
979
969
|
) -> None:
|
|
980
970
|
if self.passkey_display:
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
971
|
+
|
|
972
|
+
async def display_passkey():
|
|
973
|
+
await self.display_passkey()
|
|
974
|
+
if next_steps is not None:
|
|
975
|
+
next_steps()
|
|
976
|
+
|
|
977
|
+
try:
|
|
978
|
+
self.connection.cancel_on_disconnection(display_passkey())
|
|
979
|
+
except Exception as error:
|
|
980
|
+
logger.warning(f'exception while displaying passkey: {error}')
|
|
984
981
|
else:
|
|
985
982
|
self.input_passkey(next_steps)
|
|
986
983
|
|
|
@@ -1050,7 +1047,7 @@ class Session:
|
|
|
1050
1047
|
)
|
|
1051
1048
|
|
|
1052
1049
|
# Perform the next steps asynchronously in case we need to wait for input
|
|
1053
|
-
|
|
1050
|
+
self.connection.cancel_on_disconnection(next_steps())
|
|
1054
1051
|
else:
|
|
1055
1052
|
confirm_value = crypto.c1(
|
|
1056
1053
|
self.tk,
|
|
@@ -1173,8 +1170,8 @@ class Session:
|
|
|
1173
1170
|
self.connection.transport == PhysicalTransport.BR_EDR
|
|
1174
1171
|
and self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
|
1175
1172
|
):
|
|
1176
|
-
self.ctkd_task =
|
|
1177
|
-
self.
|
|
1173
|
+
self.ctkd_task = self.connection.cancel_on_disconnection(
|
|
1174
|
+
self.get_link_key_and_derive_ltk()
|
|
1178
1175
|
)
|
|
1179
1176
|
elif not self.sc:
|
|
1180
1177
|
# Distribute the LTK, EDIV and RAND
|
|
@@ -1212,8 +1209,8 @@ class Session:
|
|
|
1212
1209
|
self.connection.transport == PhysicalTransport.BR_EDR
|
|
1213
1210
|
and self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG
|
|
1214
1211
|
):
|
|
1215
|
-
self.ctkd_task =
|
|
1216
|
-
self.
|
|
1212
|
+
self.ctkd_task = self.connection.cancel_on_disconnection(
|
|
1213
|
+
self.get_link_key_and_derive_ltk()
|
|
1217
1214
|
)
|
|
1218
1215
|
# Distribute the LTK, EDIV and RAND
|
|
1219
1216
|
elif not self.sc:
|
|
@@ -1268,7 +1265,7 @@ class Session:
|
|
|
1268
1265
|
f'{[c.__name__ for c in self.peer_expected_distributions]}'
|
|
1269
1266
|
)
|
|
1270
1267
|
|
|
1271
|
-
def check_key_distribution(self, command_class:
|
|
1268
|
+
def check_key_distribution(self, command_class: type[SMP_Command]) -> None:
|
|
1272
1269
|
# First, check that the connection is encrypted
|
|
1273
1270
|
if not self.connection.is_encrypted:
|
|
1274
1271
|
logger.warning(
|
|
@@ -1305,17 +1302,18 @@ class Session:
|
|
|
1305
1302
|
|
|
1306
1303
|
# Wait for the pairing process to finish
|
|
1307
1304
|
assert self.pairing_result
|
|
1308
|
-
await
|
|
1309
|
-
self.connection, 'disconnection', self.pairing_result
|
|
1310
|
-
)
|
|
1305
|
+
await self.connection.cancel_on_disconnection(self.pairing_result)
|
|
1311
1306
|
|
|
1312
1307
|
def on_disconnection(self, _: int) -> None:
|
|
1313
|
-
self.connection.remove_listener('disconnection', self.on_disconnection)
|
|
1314
1308
|
self.connection.remove_listener(
|
|
1315
|
-
|
|
1309
|
+
self.connection.EVENT_DISCONNECTION, self.on_disconnection
|
|
1310
|
+
)
|
|
1311
|
+
self.connection.remove_listener(
|
|
1312
|
+
self.connection.EVENT_CONNECTION_ENCRYPTION_CHANGE,
|
|
1313
|
+
self.on_connection_encryption_change,
|
|
1316
1314
|
)
|
|
1317
1315
|
self.connection.remove_listener(
|
|
1318
|
-
|
|
1316
|
+
self.connection.EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH,
|
|
1319
1317
|
self.on_connection_encryption_key_refresh,
|
|
1320
1318
|
)
|
|
1321
1319
|
self.manager.on_session_end(self)
|
|
@@ -1325,7 +1323,7 @@ class Session:
|
|
|
1325
1323
|
if self.is_initiator:
|
|
1326
1324
|
self.distribute_keys()
|
|
1327
1325
|
|
|
1328
|
-
|
|
1326
|
+
self.connection.cancel_on_disconnection(self.on_pairing())
|
|
1329
1327
|
|
|
1330
1328
|
def on_connection_encryption_change(self) -> None:
|
|
1331
1329
|
if self.connection.is_encrypted and not self.completed:
|
|
@@ -1376,8 +1374,10 @@ class Session:
|
|
|
1376
1374
|
ediv=self.ltk_ediv,
|
|
1377
1375
|
rand=self.ltk_rand,
|
|
1378
1376
|
)
|
|
1377
|
+
if not self.peer_ltk:
|
|
1378
|
+
logger.error("peer_ltk is None")
|
|
1379
1379
|
peer_ltk_key = PairingKeys.Key(
|
|
1380
|
-
value=self.peer_ltk,
|
|
1380
|
+
value=self.peer_ltk or b'',
|
|
1381
1381
|
authenticated=authenticated,
|
|
1382
1382
|
ediv=self.peer_ediv,
|
|
1383
1383
|
rand=self.peer_rand,
|
|
@@ -1434,10 +1434,8 @@ class Session:
|
|
|
1434
1434
|
def on_smp_pairing_request_command(
|
|
1435
1435
|
self, command: SMP_Pairing_Request_Command
|
|
1436
1436
|
) -> None:
|
|
1437
|
-
|
|
1438
|
-
self.
|
|
1439
|
-
'disconnection',
|
|
1440
|
-
self.on_smp_pairing_request_command_async(command),
|
|
1437
|
+
self.connection.cancel_on_disconnection(
|
|
1438
|
+
self.on_smp_pairing_request_command_async(command)
|
|
1441
1439
|
)
|
|
1442
1440
|
|
|
1443
1441
|
async def on_smp_pairing_request_command_async(
|
|
@@ -1501,7 +1499,7 @@ class Session:
|
|
|
1501
1499
|
# Display a passkey if we need to
|
|
1502
1500
|
if not self.sc:
|
|
1503
1501
|
if self.pairing_method == PairingMethod.PASSKEY and self.passkey_display:
|
|
1504
|
-
self.display_passkey()
|
|
1502
|
+
await self.display_passkey()
|
|
1505
1503
|
|
|
1506
1504
|
# Respond
|
|
1507
1505
|
self.send_pairing_response_command()
|
|
@@ -1683,7 +1681,7 @@ class Session:
|
|
|
1683
1681
|
):
|
|
1684
1682
|
return
|
|
1685
1683
|
elif self.pairing_method == PairingMethod.PASSKEY:
|
|
1686
|
-
assert self.passkey and self.confirm_value
|
|
1684
|
+
assert self.passkey is not None and self.confirm_value is not None
|
|
1687
1685
|
# Check that the random value matches what was committed to earlier
|
|
1688
1686
|
confirm_verifier = crypto.f4(
|
|
1689
1687
|
self.pkb,
|
|
@@ -1712,7 +1710,7 @@ class Session:
|
|
|
1712
1710
|
):
|
|
1713
1711
|
self.send_pairing_random_command()
|
|
1714
1712
|
elif self.pairing_method == PairingMethod.PASSKEY:
|
|
1715
|
-
assert self.passkey and self.confirm_value
|
|
1713
|
+
assert self.passkey is not None and self.confirm_value is not None
|
|
1716
1714
|
# Check that the random value matches what was committed to earlier
|
|
1717
1715
|
confirm_verifier = crypto.f4(
|
|
1718
1716
|
self.pka,
|
|
@@ -1749,7 +1747,7 @@ class Session:
|
|
|
1749
1747
|
ra = bytes(16)
|
|
1750
1748
|
rb = ra
|
|
1751
1749
|
elif self.pairing_method == PairingMethod.PASSKEY:
|
|
1752
|
-
assert self.passkey
|
|
1750
|
+
assert self.passkey is not None
|
|
1753
1751
|
ra = self.passkey.to_bytes(16, byteorder='little')
|
|
1754
1752
|
rb = ra
|
|
1755
1753
|
elif self.pairing_method == PairingMethod.OOB:
|
|
@@ -1848,19 +1846,23 @@ class Session:
|
|
|
1848
1846
|
elif self.pairing_method == PairingMethod.PASSKEY:
|
|
1849
1847
|
self.send_pairing_confirm_command()
|
|
1850
1848
|
else:
|
|
1851
|
-
if self.pairing_method == PairingMethod.PASSKEY:
|
|
1852
|
-
self.display_or_input_passkey()
|
|
1853
1849
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1850
|
+
def next_steps() -> None:
|
|
1851
|
+
# Send our public key back to the initiator
|
|
1852
|
+
self.send_public_key_command()
|
|
1856
1853
|
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1854
|
+
if self.pairing_method in (
|
|
1855
|
+
PairingMethod.JUST_WORKS,
|
|
1856
|
+
PairingMethod.NUMERIC_COMPARISON,
|
|
1857
|
+
PairingMethod.OOB,
|
|
1858
|
+
):
|
|
1859
|
+
# We can now send the confirmation value
|
|
1860
|
+
self.send_pairing_confirm_command()
|
|
1861
|
+
|
|
1862
|
+
if self.pairing_method == PairingMethod.PASSKEY:
|
|
1863
|
+
self.display_or_input_passkey(next_steps)
|
|
1864
|
+
else:
|
|
1865
|
+
next_steps()
|
|
1864
1866
|
|
|
1865
1867
|
def on_smp_pairing_dhkey_check_command(
|
|
1866
1868
|
self, command: SMP_Pairing_DHKey_Check_Command
|
|
@@ -1882,7 +1884,7 @@ class Session:
|
|
|
1882
1884
|
self.wait_before_continuing = None
|
|
1883
1885
|
self.send_pairing_dhkey_check_command()
|
|
1884
1886
|
|
|
1885
|
-
|
|
1887
|
+
self.connection.cancel_on_disconnection(next_steps())
|
|
1886
1888
|
else:
|
|
1887
1889
|
self.send_pairing_dhkey_check_command()
|
|
1888
1890
|
else:
|
|
@@ -1932,9 +1934,9 @@ class Manager(utils.EventEmitter):
|
|
|
1932
1934
|
'''
|
|
1933
1935
|
|
|
1934
1936
|
device: Device
|
|
1935
|
-
sessions:
|
|
1937
|
+
sessions: dict[int, Session]
|
|
1936
1938
|
pairing_config_factory: Callable[[Connection], PairingConfig]
|
|
1937
|
-
session_proxy:
|
|
1939
|
+
session_proxy: type[Session]
|
|
1938
1940
|
_ecc_key: Optional[crypto.EccKey]
|
|
1939
1941
|
|
|
1940
1942
|
def __init__(
|
|
@@ -1962,7 +1964,7 @@ class Manager(utils.EventEmitter):
|
|
|
1962
1964
|
def on_smp_security_request_command(
|
|
1963
1965
|
self, connection: Connection, request: SMP_Security_Request_Command
|
|
1964
1966
|
) -> None:
|
|
1965
|
-
connection.emit(
|
|
1967
|
+
connection.emit(connection.EVENT_SECURITY_REQUEST, request.auth_req)
|
|
1966
1968
|
|
|
1967
1969
|
def on_smp_pdu(self, connection: Connection, pdu: bytes) -> None:
|
|
1968
1970
|
# Parse the L2CAP payload into an SMP Command object
|
bumble/tools/rtk_util.py
CHANGED
|
@@ -50,7 +50,7 @@ def do_parse(firmware_path):
|
|
|
50
50
|
|
|
51
51
|
# -----------------------------------------------------------------------------
|
|
52
52
|
async def do_load(usb_transport, force):
|
|
53
|
-
async with await transport.
|
|
53
|
+
async with await transport.open_transport(usb_transport) as (
|
|
54
54
|
hci_source,
|
|
55
55
|
hci_sink,
|
|
56
56
|
):
|
|
@@ -69,7 +69,7 @@ async def do_load(usb_transport, force):
|
|
|
69
69
|
|
|
70
70
|
# -----------------------------------------------------------------------------
|
|
71
71
|
async def do_drop(usb_transport):
|
|
72
|
-
async with await transport.
|
|
72
|
+
async with await transport.open_transport(usb_transport) as (
|
|
73
73
|
hci_source,
|
|
74
74
|
hci_sink,
|
|
75
75
|
):
|
bumble/transport/__init__.py
CHANGED
|
@@ -20,9 +20,9 @@ import logging
|
|
|
20
20
|
import os
|
|
21
21
|
from typing import Optional
|
|
22
22
|
|
|
23
|
+
from bumble import utils
|
|
23
24
|
from bumble.transport.common import (
|
|
24
25
|
Transport,
|
|
25
|
-
AsyncPipeSink,
|
|
26
26
|
SnoopingTransport,
|
|
27
27
|
TransportSpecError,
|
|
28
28
|
)
|
|
@@ -195,6 +195,7 @@ async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
|
|
|
195
195
|
|
|
196
196
|
|
|
197
197
|
# -----------------------------------------------------------------------------
|
|
198
|
+
@utils.deprecated("RemoteLink has been removed. Use open_transport instead.")
|
|
198
199
|
async def open_transport_or_link(name: str) -> Transport:
|
|
199
200
|
"""
|
|
200
201
|
Open a transport or a link relay.
|
|
@@ -205,21 +206,6 @@ async def open_transport_or_link(name: str) -> Transport:
|
|
|
205
206
|
When the name starts with "link-relay:", open a link relay (see RemoteLink
|
|
206
207
|
for details on what the arguments are).
|
|
207
208
|
For other namespaces, see `open_transport`.
|
|
208
|
-
|
|
209
209
|
"""
|
|
210
|
-
if name.startswith('link-relay:'):
|
|
211
|
-
logger.warning('Link Relay has been deprecated.')
|
|
212
|
-
from bumble.controller import Controller
|
|
213
|
-
from bumble.link import RemoteLink # lazy import
|
|
214
|
-
|
|
215
|
-
link = RemoteLink(name[11:])
|
|
216
|
-
await link.wait_until_connected()
|
|
217
|
-
controller = Controller('remote', link=link) # type:ignore[arg-type]
|
|
218
|
-
|
|
219
|
-
class LinkTransport(Transport):
|
|
220
|
-
async def close(self):
|
|
221
|
-
link.close()
|
|
222
|
-
|
|
223
|
-
return _wrap_transport(LinkTransport(controller, AsyncPipeSink(controller)))
|
|
224
210
|
|
|
225
211
|
return await open_transport(name)
|
|
@@ -22,7 +22,7 @@ import os
|
|
|
22
22
|
import pathlib
|
|
23
23
|
import platform
|
|
24
24
|
import sys
|
|
25
|
-
from typing import
|
|
25
|
+
from typing import Optional
|
|
26
26
|
|
|
27
27
|
import grpc.aio
|
|
28
28
|
|
|
@@ -143,7 +143,7 @@ def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
|
|
|
143
143
|
|
|
144
144
|
# -----------------------------------------------------------------------------
|
|
145
145
|
async def open_android_netsim_controller_transport(
|
|
146
|
-
server_host: Optional[str], server_port: int, options:
|
|
146
|
+
server_host: Optional[str], server_port: int, options: dict[str, str]
|
|
147
147
|
) -> Transport:
|
|
148
148
|
if not server_port:
|
|
149
149
|
raise TransportSpecError('invalid port')
|
|
@@ -288,7 +288,7 @@ async def open_android_netsim_controller_transport(
|
|
|
288
288
|
async def open_android_netsim_host_transport_with_address(
|
|
289
289
|
server_host: Optional[str],
|
|
290
290
|
server_port: int,
|
|
291
|
-
options: Optional[
|
|
291
|
+
options: Optional[dict[str, str]] = None,
|
|
292
292
|
):
|
|
293
293
|
if server_host == '_' or not server_host:
|
|
294
294
|
server_host = 'localhost'
|
|
@@ -313,7 +313,7 @@ async def open_android_netsim_host_transport_with_address(
|
|
|
313
313
|
|
|
314
314
|
# -----------------------------------------------------------------------------
|
|
315
315
|
async def open_android_netsim_host_transport_with_channel(
|
|
316
|
-
channel, options: Optional[
|
|
316
|
+
channel, options: Optional[dict[str, str]] = None
|
|
317
317
|
):
|
|
318
318
|
# Wrapper for I/O operations
|
|
319
319
|
class HciDevice:
|
|
@@ -451,7 +451,7 @@ async def open_android_netsim_transport(spec: Optional[str]) -> Transport:
|
|
|
451
451
|
port = 0
|
|
452
452
|
params_offset = 0
|
|
453
453
|
|
|
454
|
-
options:
|
|
454
|
+
options: dict[str, str] = {}
|
|
455
455
|
for param in params[params_offset:]:
|
|
456
456
|
if '=' not in param:
|
|
457
457
|
raise TransportSpecError('invalid parameter, expected <name>=<value>')
|
bumble/transport/common.py
CHANGED
|
@@ -21,7 +21,7 @@ import struct
|
|
|
21
21
|
import asyncio
|
|
22
22
|
import logging
|
|
23
23
|
import io
|
|
24
|
-
from typing import Any, ContextManager,
|
|
24
|
+
from typing import Any, ContextManager, Optional, Protocol
|
|
25
25
|
|
|
26
26
|
from bumble import core
|
|
27
27
|
from bumble import hci
|
|
@@ -38,7 +38,7 @@ logger = logging.getLogger(__name__)
|
|
|
38
38
|
# Information needed to parse HCI packets with a generic parser:
|
|
39
39
|
# For each packet type, the info represents:
|
|
40
40
|
# (length-size, length-offset, unpack-type)
|
|
41
|
-
HCI_PACKET_INFO:
|
|
41
|
+
HCI_PACKET_INFO: dict[int, tuple[int, int, str]] = {
|
|
42
42
|
hci.HCI_COMMAND_PACKET: (1, 2, 'B'),
|
|
43
43
|
hci.HCI_ACL_DATA_PACKET: (2, 2, 'H'),
|
|
44
44
|
hci.HCI_SYNCHRONOUS_DATA_PACKET: (1, 2, 'B'),
|
|
@@ -108,8 +108,8 @@ class PacketParser:
|
|
|
108
108
|
NEED_BODY = 2
|
|
109
109
|
|
|
110
110
|
sink: Optional[TransportSink]
|
|
111
|
-
extended_packet_info:
|
|
112
|
-
packet_info: Optional[
|
|
111
|
+
extended_packet_info: dict[int, tuple[int, int, str]]
|
|
112
|
+
packet_info: Optional[tuple[int, int, str]] = None
|
|
113
113
|
|
|
114
114
|
def __init__(self, sink: Optional[TransportSink] = None) -> None:
|
|
115
115
|
self.sink = sink
|
bumble/transport/pyusb.py
CHANGED
|
@@ -23,7 +23,7 @@ import time
|
|
|
23
23
|
import usb.core
|
|
24
24
|
import usb.util
|
|
25
25
|
|
|
26
|
-
from typing import Optional
|
|
26
|
+
from typing import Optional
|
|
27
27
|
from usb.core import Device as UsbDevice
|
|
28
28
|
from usb.core import USBError
|
|
29
29
|
from usb.util import CTRL_TYPE_CLASS, CTRL_RECIPIENT_OTHER
|
|
@@ -49,7 +49,7 @@ logger = logging.getLogger(__name__)
|
|
|
49
49
|
# -----------------------------------------------------------------------------
|
|
50
50
|
# Global
|
|
51
51
|
# -----------------------------------------------------------------------------
|
|
52
|
-
devices_in_use:
|
|
52
|
+
devices_in_use: set[int] = set()
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
# -----------------------------------------------------------------------------
|
bumble/utils.py
CHANGED
|
@@ -27,11 +27,8 @@ from typing import (
|
|
|
27
27
|
Any,
|
|
28
28
|
Awaitable,
|
|
29
29
|
Callable,
|
|
30
|
-
List,
|
|
31
30
|
Optional,
|
|
32
31
|
Protocol,
|
|
33
|
-
Set,
|
|
34
|
-
Tuple,
|
|
35
32
|
TypeVar,
|
|
36
33
|
Union,
|
|
37
34
|
overload,
|
|
@@ -156,7 +153,7 @@ class EventWatcher:
|
|
|
156
153
|
```
|
|
157
154
|
'''
|
|
158
155
|
|
|
159
|
-
handlers:
|
|
156
|
+
handlers: list[tuple[pyee.EventEmitter, str, Callable[..., Any]]]
|
|
160
157
|
|
|
161
158
|
def __init__(self) -> None:
|
|
162
159
|
self.handlers = []
|
|
@@ -329,7 +326,7 @@ class AsyncRunner:
|
|
|
329
326
|
default_queue = WorkQueue()
|
|
330
327
|
|
|
331
328
|
# Shared set of running tasks
|
|
332
|
-
running_tasks:
|
|
329
|
+
running_tasks: set[Awaitable] = set()
|
|
333
330
|
|
|
334
331
|
@staticmethod
|
|
335
332
|
def run_in_task(queue=None):
|