bumble 0.0.210__py3-none-any.whl → 0.0.212__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/apps/bench.py +8 -4
- bumble/apps/console.py +2 -2
- bumble/apps/pair.py +185 -32
- bumble/att.py +13 -12
- bumble/avctp.py +2 -2
- bumble/avdtp.py +122 -68
- bumble/avrcp.py +11 -5
- 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 +365 -185
- bumble/drivers/intel.py +3 -0
- bumble/gatt.py +3 -5
- bumble/gatt_client.py +5 -3
- bumble/gatt_server.py +8 -6
- bumble/hci.py +81 -4
- bumble/hfp.py +44 -20
- bumble/hid.py +24 -12
- bumble/host.py +24 -0
- bumble/keys.py +64 -48
- bumble/l2cap.py +19 -9
- bumble/pandora/host.py +11 -11
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +72 -56
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +11 -5
- bumble/profiles/asha.py +11 -6
- bumble/profiles/csip.py +1 -3
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +16 -33
- bumble/profiles/mcp.py +12 -9
- bumble/profiles/vcs.py +5 -5
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +17 -8
- bumble/smp.py +14 -8
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/METADATA +4 -4
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/RECORD +44 -42
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/WHEEL +1 -1
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.210.dist-info → bumble-0.0.212.dist-info}/top_level.txt +0 -0
bumble/device.py
CHANGED
|
@@ -61,7 +61,6 @@ from bumble.core import (
|
|
|
61
61
|
BaseBumbleError,
|
|
62
62
|
ConnectionParameterUpdateError,
|
|
63
63
|
CommandTimeoutError,
|
|
64
|
-
ConnectionParameters,
|
|
65
64
|
ConnectionPHY,
|
|
66
65
|
InvalidArgumentError,
|
|
67
66
|
InvalidOperationError,
|
|
@@ -484,7 +483,7 @@ class BIGInfoAdvertisement:
|
|
|
484
483
|
sid: int
|
|
485
484
|
num_bis: int
|
|
486
485
|
nse: int
|
|
487
|
-
iso_interval:
|
|
486
|
+
iso_interval: float
|
|
488
487
|
bn: int
|
|
489
488
|
pto: int
|
|
490
489
|
irc: int
|
|
@@ -502,7 +501,7 @@ class BIGInfoAdvertisement:
|
|
|
502
501
|
sid,
|
|
503
502
|
report.num_bis,
|
|
504
503
|
report.nse,
|
|
505
|
-
report.iso_interval,
|
|
504
|
+
report.iso_interval * 1.25,
|
|
506
505
|
report.bn,
|
|
507
506
|
report.pto,
|
|
508
507
|
report.irc,
|
|
@@ -529,8 +528,8 @@ class AdvertisingParameters:
|
|
|
529
528
|
advertising_event_properties: AdvertisingEventProperties = field(
|
|
530
529
|
default_factory=AdvertisingEventProperties
|
|
531
530
|
)
|
|
532
|
-
primary_advertising_interval_min:
|
|
533
|
-
primary_advertising_interval_max:
|
|
531
|
+
primary_advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
532
|
+
primary_advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
534
533
|
primary_advertising_channel_map: (
|
|
535
534
|
hci.HCI_LE_Set_Extended_Advertising_Parameters_Command.ChannelMap
|
|
536
535
|
) = (
|
|
@@ -554,8 +553,8 @@ class AdvertisingParameters:
|
|
|
554
553
|
# -----------------------------------------------------------------------------
|
|
555
554
|
@dataclass
|
|
556
555
|
class PeriodicAdvertisingParameters:
|
|
557
|
-
periodic_advertising_interval_min:
|
|
558
|
-
periodic_advertising_interval_max:
|
|
556
|
+
periodic_advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
557
|
+
periodic_advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
559
558
|
periodic_advertising_properties: (
|
|
560
559
|
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties
|
|
561
560
|
) = field(
|
|
@@ -581,6 +580,12 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
581
580
|
enabled: bool = False
|
|
582
581
|
periodic_enabled: bool = False
|
|
583
582
|
|
|
583
|
+
EVENT_START = "start"
|
|
584
|
+
EVENT_STOP = "stop"
|
|
585
|
+
EVENT_START_PERIODIC = "start_periodic"
|
|
586
|
+
EVENT_STOP_PERIODIC = "stop_periodic"
|
|
587
|
+
EVENT_TERMINATION = "termination"
|
|
588
|
+
|
|
584
589
|
def __post_init__(self) -> None:
|
|
585
590
|
super().__init__()
|
|
586
591
|
|
|
@@ -679,8 +684,12 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
679
684
|
await self.device.send_command(
|
|
680
685
|
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command(
|
|
681
686
|
advertising_handle=self.advertising_handle,
|
|
682
|
-
periodic_advertising_interval_min=
|
|
683
|
-
|
|
687
|
+
periodic_advertising_interval_min=int(
|
|
688
|
+
advertising_parameters.periodic_advertising_interval_min / 1.25
|
|
689
|
+
),
|
|
690
|
+
periodic_advertising_interval_max=int(
|
|
691
|
+
advertising_parameters.periodic_advertising_interval_max / 1.25
|
|
692
|
+
),
|
|
684
693
|
periodic_advertising_properties=advertising_parameters.periodic_advertising_properties,
|
|
685
694
|
),
|
|
686
695
|
check_result=True,
|
|
@@ -731,7 +740,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
731
740
|
)
|
|
732
741
|
self.enabled = True
|
|
733
742
|
|
|
734
|
-
self.emit(
|
|
743
|
+
self.emit(self.EVENT_START)
|
|
735
744
|
|
|
736
745
|
async def stop(self) -> None:
|
|
737
746
|
await self.device.send_command(
|
|
@@ -745,7 +754,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
745
754
|
)
|
|
746
755
|
self.enabled = False
|
|
747
756
|
|
|
748
|
-
self.emit(
|
|
757
|
+
self.emit(self.EVENT_STOP)
|
|
749
758
|
|
|
750
759
|
async def start_periodic(self, include_adi: bool = False) -> None:
|
|
751
760
|
if self.periodic_enabled:
|
|
@@ -759,7 +768,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
759
768
|
)
|
|
760
769
|
self.periodic_enabled = True
|
|
761
770
|
|
|
762
|
-
self.emit(
|
|
771
|
+
self.emit(self.EVENT_START_PERIODIC)
|
|
763
772
|
|
|
764
773
|
async def stop_periodic(self) -> None:
|
|
765
774
|
if not self.periodic_enabled:
|
|
@@ -773,7 +782,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
773
782
|
)
|
|
774
783
|
self.periodic_enabled = False
|
|
775
784
|
|
|
776
|
-
self.emit(
|
|
785
|
+
self.emit(self.EVENT_STOP_PERIODIC)
|
|
777
786
|
|
|
778
787
|
async def remove(self) -> None:
|
|
779
788
|
await self.device.send_command(
|
|
@@ -797,7 +806,7 @@ class AdvertisingSet(utils.EventEmitter):
|
|
|
797
806
|
|
|
798
807
|
def on_termination(self, status: int) -> None:
|
|
799
808
|
self.enabled = False
|
|
800
|
-
self.emit(
|
|
809
|
+
self.emit(self.EVENT_TERMINATION, status)
|
|
801
810
|
|
|
802
811
|
|
|
803
812
|
# -----------------------------------------------------------------------------
|
|
@@ -820,9 +829,17 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
820
829
|
filter_duplicates: bool
|
|
821
830
|
status: int
|
|
822
831
|
advertiser_phy: int
|
|
823
|
-
periodic_advertising_interval:
|
|
832
|
+
periodic_advertising_interval: float # Advertising interval, in milliseconds
|
|
824
833
|
advertiser_clock_accuracy: int
|
|
825
834
|
|
|
835
|
+
EVENT_STATE_CHANGE = "state_change"
|
|
836
|
+
EVENT_ESTABLISHMENT = "establishment"
|
|
837
|
+
EVENT_CANCELLATION = "cancellation"
|
|
838
|
+
EVENT_ERROR = "error"
|
|
839
|
+
EVENT_LOSS = "loss"
|
|
840
|
+
EVENT_PERIODIC_ADVERTISEMENT = "periodic_advertisement"
|
|
841
|
+
EVENT_BIGINFO_ADVERTISEMENT = "biginfo_advertisement"
|
|
842
|
+
|
|
826
843
|
def __init__(
|
|
827
844
|
self,
|
|
828
845
|
device: Device,
|
|
@@ -855,7 +872,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
855
872
|
def state(self, state: State) -> None:
|
|
856
873
|
logger.debug(f'{self} -> {state.name}')
|
|
857
874
|
self._state = state
|
|
858
|
-
self.emit(
|
|
875
|
+
self.emit(self.EVENT_STATE_CHANGE)
|
|
859
876
|
|
|
860
877
|
async def establish(self) -> None:
|
|
861
878
|
if self.state != self.State.INIT:
|
|
@@ -936,10 +953,10 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
936
953
|
if status == hci.HCI_SUCCESS:
|
|
937
954
|
self.sync_handle = sync_handle
|
|
938
955
|
self.advertiser_phy = advertiser_phy
|
|
939
|
-
self.periodic_advertising_interval = periodic_advertising_interval
|
|
956
|
+
self.periodic_advertising_interval = periodic_advertising_interval * 1.25
|
|
940
957
|
self.advertiser_clock_accuracy = advertiser_clock_accuracy
|
|
941
958
|
self.state = self.State.ESTABLISHED
|
|
942
|
-
self.emit(
|
|
959
|
+
self.emit(self.EVENT_ESTABLISHMENT)
|
|
943
960
|
return
|
|
944
961
|
|
|
945
962
|
# We don't need to keep a reference anymore
|
|
@@ -948,15 +965,15 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
948
965
|
|
|
949
966
|
if status == hci.HCI_OPERATION_CANCELLED_BY_HOST_ERROR:
|
|
950
967
|
self.state = self.State.CANCELLED
|
|
951
|
-
self.emit(
|
|
968
|
+
self.emit(self.EVENT_CANCELLATION)
|
|
952
969
|
return
|
|
953
970
|
|
|
954
971
|
self.state = self.State.ERROR
|
|
955
|
-
self.emit(
|
|
972
|
+
self.emit(self.EVENT_ERROR)
|
|
956
973
|
|
|
957
974
|
def on_loss(self):
|
|
958
975
|
self.state = self.State.LOST
|
|
959
|
-
self.emit(
|
|
976
|
+
self.emit(self.EVENT_LOSS)
|
|
960
977
|
|
|
961
978
|
def on_periodic_advertising_report(self, report) -> None:
|
|
962
979
|
self.data_accumulator += report.data
|
|
@@ -967,7 +984,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
967
984
|
return
|
|
968
985
|
|
|
969
986
|
self.emit(
|
|
970
|
-
|
|
987
|
+
self.EVENT_PERIODIC_ADVERTISEMENT,
|
|
971
988
|
PeriodicAdvertisement(
|
|
972
989
|
self.advertiser_address,
|
|
973
990
|
self.sid,
|
|
@@ -984,7 +1001,7 @@ class PeriodicAdvertisingSync(utils.EventEmitter):
|
|
|
984
1001
|
|
|
985
1002
|
def on_biginfo_advertising_report(self, report) -> None:
|
|
986
1003
|
self.emit(
|
|
987
|
-
|
|
1004
|
+
self.EVENT_BIGINFO_ADVERTISEMENT,
|
|
988
1005
|
BIGInfoAdvertisement.from_report(self.advertiser_address, self.sid, report),
|
|
989
1006
|
)
|
|
990
1007
|
|
|
@@ -1041,7 +1058,7 @@ class Big(utils.EventEmitter):
|
|
|
1041
1058
|
pto: int = 0
|
|
1042
1059
|
irc: int = 0
|
|
1043
1060
|
max_pdu: int = 0
|
|
1044
|
-
iso_interval:
|
|
1061
|
+
iso_interval: float = 0.0
|
|
1045
1062
|
bis_links: Sequence[BisLink] = ()
|
|
1046
1063
|
|
|
1047
1064
|
def __post_init__(self) -> None:
|
|
@@ -1102,7 +1119,7 @@ class BigSync(utils.EventEmitter):
|
|
|
1102
1119
|
pto: int = 0
|
|
1103
1120
|
irc: int = 0
|
|
1104
1121
|
max_pdu: int = 0
|
|
1105
|
-
iso_interval:
|
|
1122
|
+
iso_interval: float = 0.0
|
|
1106
1123
|
bis_links: Sequence[BisLink] = ()
|
|
1107
1124
|
|
|
1108
1125
|
def __post_init__(self) -> None:
|
|
@@ -1183,11 +1200,11 @@ class ChannelSoundingProcedure:
|
|
|
1183
1200
|
selected_tx_power: int
|
|
1184
1201
|
subevent_len: int
|
|
1185
1202
|
subevents_per_event: int
|
|
1186
|
-
subevent_interval:
|
|
1203
|
+
subevent_interval: float # milliseconds.
|
|
1187
1204
|
event_interval: int
|
|
1188
1205
|
procedure_interval: int
|
|
1189
1206
|
procedure_count: int
|
|
1190
|
-
max_procedure_len:
|
|
1207
|
+
max_procedure_len: float # milliseconds.
|
|
1191
1208
|
|
|
1192
1209
|
|
|
1193
1210
|
# -----------------------------------------------------------------------------
|
|
@@ -1212,9 +1229,8 @@ class Peer:
|
|
|
1212
1229
|
def __init__(self, connection: Connection) -> None:
|
|
1213
1230
|
self.connection = connection
|
|
1214
1231
|
|
|
1215
|
-
#
|
|
1216
|
-
self.gatt_client = gatt_client
|
|
1217
|
-
connection.gatt_client = self.gatt_client
|
|
1232
|
+
# Shortcut to the connection's GATT client
|
|
1233
|
+
self.gatt_client = connection.gatt_client
|
|
1218
1234
|
|
|
1219
1235
|
@property
|
|
1220
1236
|
def services(self) -> list[gatt_client.ServiceProxy]:
|
|
@@ -1222,7 +1238,7 @@ class Peer:
|
|
|
1222
1238
|
|
|
1223
1239
|
async def request_mtu(self, mtu: int) -> int:
|
|
1224
1240
|
mtu = await self.gatt_client.request_mtu(mtu)
|
|
1225
|
-
self.connection.emit(
|
|
1241
|
+
self.connection.emit(self.connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
|
|
1226
1242
|
return mtu
|
|
1227
1243
|
|
|
1228
1244
|
async def discover_service(
|
|
@@ -1390,6 +1406,9 @@ class ScoLink(utils.CompositeEventEmitter):
|
|
|
1390
1406
|
link_type: int
|
|
1391
1407
|
sink: Optional[Callable[[hci.HCI_SynchronousDataPacket], Any]] = None
|
|
1392
1408
|
|
|
1409
|
+
EVENT_DISCONNECTION: ClassVar[str] = "disconnection"
|
|
1410
|
+
EVENT_DISCONNECTION_FAILURE: ClassVar[str] = "disconnection_failure"
|
|
1411
|
+
|
|
1393
1412
|
def __post_init__(self) -> None:
|
|
1394
1413
|
super().__init__()
|
|
1395
1414
|
|
|
@@ -1487,6 +1506,11 @@ class CisLink(utils.EventEmitter, _IsoLink):
|
|
|
1487
1506
|
state: State = State.PENDING
|
|
1488
1507
|
sink: Callable[[hci.HCI_IsoDataPacket], Any] | None = None
|
|
1489
1508
|
|
|
1509
|
+
EVENT_DISCONNECTION: ClassVar[str] = "disconnection"
|
|
1510
|
+
EVENT_DISCONNECTION_FAILURE: ClassVar[str] = "disconnection_failure"
|
|
1511
|
+
EVENT_ESTABLISHMENT: ClassVar[str] = "establishment"
|
|
1512
|
+
EVENT_ESTABLISHMENT_FAILURE: ClassVar[str] = "establishment_failure"
|
|
1513
|
+
|
|
1490
1514
|
def __post_init__(self) -> None:
|
|
1491
1515
|
super().__init__()
|
|
1492
1516
|
|
|
@@ -1562,15 +1586,50 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1562
1586
|
peer_le_features: Optional[hci.LeFeatureMask]
|
|
1563
1587
|
role: hci.Role
|
|
1564
1588
|
encryption: int
|
|
1589
|
+
encryption_key_size: int
|
|
1565
1590
|
authenticated: bool
|
|
1566
1591
|
sc: bool
|
|
1567
|
-
link_key_type: int
|
|
1568
1592
|
gatt_client: gatt_client.Client
|
|
1569
1593
|
pairing_peer_io_capability: Optional[int]
|
|
1570
1594
|
pairing_peer_authentication_requirements: Optional[int]
|
|
1571
1595
|
cs_configs: dict[int, ChannelSoundingConfig] # Config ID to Configuration
|
|
1572
1596
|
cs_procedures: dict[int, ChannelSoundingProcedure] # Config ID to Procedures
|
|
1573
1597
|
|
|
1598
|
+
EVENT_CONNECTION_ATT_MTU_UPDATE = "connection_att_mtu_update"
|
|
1599
|
+
EVENT_DISCONNECTION = "disconnection"
|
|
1600
|
+
EVENT_DISCONNECTION_FAILURE = "disconnection_failure"
|
|
1601
|
+
EVENT_CONNECTION_AUTHENTICATION = "connection_authentication"
|
|
1602
|
+
EVENT_CONNECTION_AUTHENTICATION_FAILURE = "connection_authentication_failure"
|
|
1603
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
1604
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
1605
|
+
EVENT_CONNECTION_ENCRYPTION_CHANGE = "connection_encryption_change"
|
|
1606
|
+
EVENT_CONNECTION_ENCRYPTION_FAILURE = "connection_encryption_failure"
|
|
1607
|
+
EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH = "connection_encryption_key_refresh"
|
|
1608
|
+
EVENT_CONNECTION_PARAMETERS_UPDATE = "connection_parameters_update"
|
|
1609
|
+
EVENT_CONNECTION_PARAMETERS_UPDATE_FAILURE = "connection_parameters_update_failure"
|
|
1610
|
+
EVENT_CONNECTION_PHY_UPDATE = "connection_phy_update"
|
|
1611
|
+
EVENT_CONNECTION_PHY_UPDATE_FAILURE = "connection_phy_update_failure"
|
|
1612
|
+
EVENT_CONNECTION_ATT_MTU_UPDATE = "connection_att_mtu_update"
|
|
1613
|
+
EVENT_CONNECTION_DATA_LENGTH_CHANGE = "connection_data_length_change"
|
|
1614
|
+
EVENT_CHANNEL_SOUNDING_CAPABILITIES_FAILURE = (
|
|
1615
|
+
"channel_sounding_capabilities_failure"
|
|
1616
|
+
)
|
|
1617
|
+
EVENT_CHANNEL_SOUNDING_CAPABILITIES = "channel_sounding_capabilities"
|
|
1618
|
+
EVENT_CHANNEL_SOUNDING_CONFIG_FAILURE = "channel_sounding_config_failure"
|
|
1619
|
+
EVENT_CHANNEL_SOUNDING_CONFIG = "channel_sounding_config"
|
|
1620
|
+
EVENT_CHANNEL_SOUNDING_CONFIG_REMOVED = "channel_sounding_config_removed"
|
|
1621
|
+
EVENT_CHANNEL_SOUNDING_PROCEDURE_FAILURE = "channel_sounding_procedure_failure"
|
|
1622
|
+
EVENT_CHANNEL_SOUNDING_PROCEDURE = "channel_sounding_procedure"
|
|
1623
|
+
EVENT_ROLE_CHANGE = "role_change"
|
|
1624
|
+
EVENT_ROLE_CHANGE_FAILURE = "role_change_failure"
|
|
1625
|
+
EVENT_CLASSIC_PAIRING = "classic_pairing"
|
|
1626
|
+
EVENT_CLASSIC_PAIRING_FAILURE = "classic_pairing_failure"
|
|
1627
|
+
EVENT_PAIRING_START = "pairing_start"
|
|
1628
|
+
EVENT_PAIRING = "pairing"
|
|
1629
|
+
EVENT_PAIRING_FAILURE = "pairing_failure"
|
|
1630
|
+
EVENT_SECURITY_REQUEST = "security_request"
|
|
1631
|
+
EVENT_LINK_KEY = "link_key"
|
|
1632
|
+
|
|
1574
1633
|
@utils.composite_listener
|
|
1575
1634
|
class Listener:
|
|
1576
1635
|
def on_disconnection(self, reason):
|
|
@@ -1600,17 +1659,23 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1600
1659
|
def on_connection_encryption_key_refresh(self):
|
|
1601
1660
|
pass
|
|
1602
1661
|
|
|
1662
|
+
@dataclass
|
|
1663
|
+
class Parameters:
|
|
1664
|
+
connection_interval: float # Connection interval, in milliseconds. [LE only]
|
|
1665
|
+
peripheral_latency: int # Peripheral latency, in number of intervals. [LE only]
|
|
1666
|
+
supervision_timeout: float # Supervision timeout, in milliseconds.
|
|
1667
|
+
|
|
1603
1668
|
def __init__(
|
|
1604
1669
|
self,
|
|
1605
|
-
device,
|
|
1606
|
-
handle,
|
|
1607
|
-
transport,
|
|
1608
|
-
self_address,
|
|
1609
|
-
self_resolvable_address,
|
|
1610
|
-
peer_address,
|
|
1611
|
-
peer_resolvable_address,
|
|
1612
|
-
role,
|
|
1613
|
-
parameters,
|
|
1670
|
+
device: Device,
|
|
1671
|
+
handle: int,
|
|
1672
|
+
transport: core.PhysicalTransport,
|
|
1673
|
+
self_address: hci.Address,
|
|
1674
|
+
self_resolvable_address: Optional[hci.Address],
|
|
1675
|
+
peer_address: hci.Address,
|
|
1676
|
+
peer_resolvable_address: Optional[hci.Address],
|
|
1677
|
+
role: hci.Role,
|
|
1678
|
+
parameters: Parameters,
|
|
1614
1679
|
):
|
|
1615
1680
|
super().__init__()
|
|
1616
1681
|
self.device = device
|
|
@@ -1624,12 +1689,12 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1624
1689
|
self.role = role
|
|
1625
1690
|
self.parameters = parameters
|
|
1626
1691
|
self.encryption = 0
|
|
1692
|
+
self.encryption_key_size = 0
|
|
1627
1693
|
self.authenticated = False
|
|
1628
1694
|
self.sc = False
|
|
1629
|
-
self.link_key_type = None
|
|
1630
1695
|
self.att_mtu = ATT_DEFAULT_MTU
|
|
1631
1696
|
self.data_length = DEVICE_DEFAULT_DATA_LENGTH
|
|
1632
|
-
self.gatt_client =
|
|
1697
|
+
self.gatt_client = gatt_client.Client(self) # Per-connection client
|
|
1633
1698
|
self.gatt_server = (
|
|
1634
1699
|
device.gatt_server
|
|
1635
1700
|
) # By default, use the device's shared server
|
|
@@ -1740,28 +1805,38 @@ class Connection(utils.CompositeEventEmitter):
|
|
|
1740
1805
|
"""Idles the current task waiting for a disconnect or timeout"""
|
|
1741
1806
|
|
|
1742
1807
|
abort = asyncio.get_running_loop().create_future()
|
|
1743
|
-
self.on(
|
|
1744
|
-
self.on(
|
|
1808
|
+
self.on(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1809
|
+
self.on(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1745
1810
|
|
|
1746
1811
|
try:
|
|
1747
1812
|
await asyncio.wait_for(
|
|
1748
|
-
utils.cancel_on_event(self.device,
|
|
1813
|
+
utils.cancel_on_event(self.device, Device.EVENT_FLUSH, abort), timeout
|
|
1749
1814
|
)
|
|
1750
1815
|
finally:
|
|
1751
|
-
self.remove_listener(
|
|
1752
|
-
self.remove_listener(
|
|
1816
|
+
self.remove_listener(self.EVENT_DISCONNECTION, abort.set_result)
|
|
1817
|
+
self.remove_listener(self.EVENT_DISCONNECTION_FAILURE, abort.set_exception)
|
|
1753
1818
|
|
|
1754
1819
|
async def set_data_length(self, tx_octets, tx_time) -> None:
|
|
1755
1820
|
return await self.device.set_data_length(self, tx_octets, tx_time)
|
|
1756
1821
|
|
|
1757
1822
|
async def update_parameters(
|
|
1758
1823
|
self,
|
|
1759
|
-
connection_interval_min,
|
|
1760
|
-
connection_interval_max,
|
|
1761
|
-
max_latency,
|
|
1762
|
-
supervision_timeout,
|
|
1824
|
+
connection_interval_min: float,
|
|
1825
|
+
connection_interval_max: float,
|
|
1826
|
+
max_latency: int,
|
|
1827
|
+
supervision_timeout: float,
|
|
1763
1828
|
use_l2cap=False,
|
|
1764
|
-
):
|
|
1829
|
+
) -> None:
|
|
1830
|
+
"""
|
|
1831
|
+
Request an update of the connection parameters.
|
|
1832
|
+
|
|
1833
|
+
Args:
|
|
1834
|
+
connection_interval_min: Minimum interval, in milliseconds.
|
|
1835
|
+
connection_interval_max: Maximum interval, in milliseconds.
|
|
1836
|
+
max_latency: Latency, in number of intervals.
|
|
1837
|
+
supervision_timeout: Timeout, in milliseconds.
|
|
1838
|
+
use_l2cap: Request the update via L2CAP.
|
|
1839
|
+
"""
|
|
1765
1840
|
return await self.device.update_connection_parameters(
|
|
1766
1841
|
self,
|
|
1767
1842
|
connection_interval_min,
|
|
@@ -1848,8 +1923,8 @@ class DeviceConfiguration:
|
|
|
1848
1923
|
address: hci.Address = hci.Address(DEVICE_DEFAULT_ADDRESS)
|
|
1849
1924
|
class_of_device: int = DEVICE_DEFAULT_CLASS_OF_DEVICE
|
|
1850
1925
|
scan_response_data: bytes = DEVICE_DEFAULT_SCAN_RESPONSE_DATA
|
|
1851
|
-
advertising_interval_min:
|
|
1852
|
-
advertising_interval_max:
|
|
1926
|
+
advertising_interval_min: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
1927
|
+
advertising_interval_max: float = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
|
1853
1928
|
le_enabled: bool = True
|
|
1854
1929
|
le_simultaneous_enabled: bool = False
|
|
1855
1930
|
le_privacy_enabled: bool = False
|
|
@@ -2062,6 +2137,26 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2062
2137
|
_pending_cis: Dict[int, tuple[int, int]]
|
|
2063
2138
|
gatt_service: gatt_service.GenericAttributeProfileService | None = None
|
|
2064
2139
|
|
|
2140
|
+
EVENT_ADVERTISEMENT = "advertisement"
|
|
2141
|
+
EVENT_PERIODIC_ADVERTISING_SYNC_TRANSFER = "periodic_advertising_sync_transfer"
|
|
2142
|
+
EVENT_KEY_STORE_UPDATE = "key_store_update"
|
|
2143
|
+
EVENT_FLUSH = "flush"
|
|
2144
|
+
EVENT_CONNECTION = "connection"
|
|
2145
|
+
EVENT_CONNECTION_FAILURE = "connection_failure"
|
|
2146
|
+
EVENT_SCO_REQUEST = "sco_request"
|
|
2147
|
+
EVENT_INQUIRY_COMPLETE = "inquiry_complete"
|
|
2148
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
2149
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
2150
|
+
EVENT_SCO_CONNECTION = "sco_connection"
|
|
2151
|
+
EVENT_SCO_CONNECTION_FAILURE = "sco_connection_failure"
|
|
2152
|
+
EVENT_CIS_REQUEST = "cis_request"
|
|
2153
|
+
EVENT_CIS_ESTABLISHMENT = "cis_establishment"
|
|
2154
|
+
EVENT_CIS_ESTABLISHMENT_FAILURE = "cis_establishment_failure"
|
|
2155
|
+
EVENT_ROLE_CHANGE_FAILURE = "role_change_failure"
|
|
2156
|
+
EVENT_INQUIRY_RESULT = "inquiry_result"
|
|
2157
|
+
EVENT_REMOTE_NAME = "remote_name"
|
|
2158
|
+
EVENT_REMOTE_NAME_FAILURE = "remote_name_failure"
|
|
2159
|
+
|
|
2065
2160
|
@utils.composite_listener
|
|
2066
2161
|
class Listener:
|
|
2067
2162
|
def on_advertisement(self, advertisement):
|
|
@@ -2748,8 +2843,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
2748
2843
|
auto_restart: bool = False,
|
|
2749
2844
|
advertising_data: Optional[bytes] = None,
|
|
2750
2845
|
scan_response_data: Optional[bytes] = None,
|
|
2751
|
-
advertising_interval_min: Optional[
|
|
2752
|
-
advertising_interval_max: Optional[
|
|
2846
|
+
advertising_interval_min: Optional[float] = None,
|
|
2847
|
+
advertising_interval_max: Optional[float] = None,
|
|
2753
2848
|
) -> None:
|
|
2754
2849
|
"""Start legacy advertising.
|
|
2755
2850
|
|
|
@@ -3149,7 +3244,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3149
3244
|
accumulator = AdvertisementDataAccumulator(passive=self.scanning_is_passive)
|
|
3150
3245
|
self.advertisement_accumulators[report.address] = accumulator
|
|
3151
3246
|
if advertisement := accumulator.update(report):
|
|
3152
|
-
self.emit(
|
|
3247
|
+
self.emit(self.EVENT_ADVERTISEMENT, advertisement)
|
|
3153
3248
|
|
|
3154
3249
|
async def create_periodic_advertising_sync(
|
|
3155
3250
|
self,
|
|
@@ -3273,7 +3368,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3273
3368
|
periodic_advertising_interval=periodic_advertising_interval,
|
|
3274
3369
|
advertiser_clock_accuracy=advertiser_clock_accuracy,
|
|
3275
3370
|
)
|
|
3276
|
-
self.emit(
|
|
3371
|
+
self.emit(self.EVENT_PERIODIC_ADVERTISING_SYNC_TRANSFER, pa_sync, connection)
|
|
3277
3372
|
|
|
3278
3373
|
@host_event_handler
|
|
3279
3374
|
@with_periodic_advertising_sync_from_handle
|
|
@@ -3331,7 +3426,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3331
3426
|
@host_event_handler
|
|
3332
3427
|
def on_inquiry_result(self, address, class_of_device, data, rssi):
|
|
3333
3428
|
self.emit(
|
|
3334
|
-
|
|
3429
|
+
self.EVENT_INQUIRY_RESULT,
|
|
3335
3430
|
address,
|
|
3336
3431
|
class_of_device,
|
|
3337
3432
|
AdvertisingData.from_bytes(data),
|
|
@@ -3508,8 +3603,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3508
3603
|
|
|
3509
3604
|
# Create a future so that we can wait for the connection's result
|
|
3510
3605
|
pending_connection = asyncio.get_running_loop().create_future()
|
|
3511
|
-
self.on(
|
|
3512
|
-
self.on(
|
|
3606
|
+
self.on(self.EVENT_CONNECTION, on_connection)
|
|
3607
|
+
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3513
3608
|
|
|
3514
3609
|
try:
|
|
3515
3610
|
# Tell the controller to connect
|
|
@@ -3662,7 +3757,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3662
3757
|
self.le_connecting = True
|
|
3663
3758
|
|
|
3664
3759
|
if timeout is None:
|
|
3665
|
-
return await utils.cancel_on_event(
|
|
3760
|
+
return await utils.cancel_on_event(
|
|
3761
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3762
|
+
)
|
|
3666
3763
|
|
|
3667
3764
|
try:
|
|
3668
3765
|
return await asyncio.wait_for(
|
|
@@ -3680,13 +3777,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3680
3777
|
|
|
3681
3778
|
try:
|
|
3682
3779
|
return await utils.cancel_on_event(
|
|
3683
|
-
self,
|
|
3780
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3684
3781
|
)
|
|
3685
3782
|
except core.ConnectionError as error:
|
|
3686
3783
|
raise core.TimeoutError() from error
|
|
3687
3784
|
finally:
|
|
3688
|
-
self.remove_listener(
|
|
3689
|
-
self.remove_listener(
|
|
3785
|
+
self.remove_listener(self.EVENT_CONNECTION, on_connection)
|
|
3786
|
+
self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3690
3787
|
if transport == PhysicalTransport.LE:
|
|
3691
3788
|
self.le_connecting = False
|
|
3692
3789
|
self.connect_own_address_type = None
|
|
@@ -3737,7 +3834,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3737
3834
|
|
|
3738
3835
|
try:
|
|
3739
3836
|
# Wait for a request or a completed connection
|
|
3740
|
-
pending_request = utils.cancel_on_event(
|
|
3837
|
+
pending_request = utils.cancel_on_event(
|
|
3838
|
+
self, Device.EVENT_FLUSH, pending_request_fut
|
|
3839
|
+
)
|
|
3741
3840
|
result = await (
|
|
3742
3841
|
asyncio.wait_for(pending_request, timeout)
|
|
3743
3842
|
if timeout
|
|
@@ -3779,8 +3878,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3779
3878
|
):
|
|
3780
3879
|
pending_connection.set_exception(error)
|
|
3781
3880
|
|
|
3782
|
-
self.on(
|
|
3783
|
-
self.on(
|
|
3881
|
+
self.on(self.EVENT_CONNECTION, on_connection)
|
|
3882
|
+
self.on(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3784
3883
|
|
|
3785
3884
|
# Save pending connection, with the Peripheral hci.role.
|
|
3786
3885
|
# Even if we requested a role switch in the hci.HCI_Accept_Connection_Request
|
|
@@ -3799,11 +3898,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3799
3898
|
)
|
|
3800
3899
|
|
|
3801
3900
|
# Wait for connection complete
|
|
3802
|
-
return await utils.cancel_on_event(
|
|
3901
|
+
return await utils.cancel_on_event(
|
|
3902
|
+
self, Device.EVENT_FLUSH, pending_connection
|
|
3903
|
+
)
|
|
3803
3904
|
|
|
3804
3905
|
finally:
|
|
3805
|
-
self.remove_listener(
|
|
3806
|
-
self.remove_listener(
|
|
3906
|
+
self.remove_listener(self.EVENT_CONNECTION, on_connection)
|
|
3907
|
+
self.remove_listener(self.EVENT_CONNECTION_FAILURE, on_connection_failure)
|
|
3807
3908
|
self.pending_connections.pop(peer_address, None)
|
|
3808
3909
|
|
|
3809
3910
|
@asynccontextmanager
|
|
@@ -3857,8 +3958,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3857
3958
|
) -> None:
|
|
3858
3959
|
# Create a future so that we can wait for the disconnection's result
|
|
3859
3960
|
pending_disconnection = asyncio.get_running_loop().create_future()
|
|
3860
|
-
connection.on(
|
|
3861
|
-
connection.on(
|
|
3961
|
+
connection.on(connection.EVENT_DISCONNECTION, pending_disconnection.set_result)
|
|
3962
|
+
connection.on(
|
|
3963
|
+
connection.EVENT_DISCONNECTION_FAILURE, pending_disconnection.set_exception
|
|
3964
|
+
)
|
|
3862
3965
|
|
|
3863
3966
|
# Request a disconnection
|
|
3864
3967
|
result = await self.send_command(
|
|
@@ -3873,13 +3976,16 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3873
3976
|
|
|
3874
3977
|
# Wait for the disconnection process to complete
|
|
3875
3978
|
self.disconnecting = True
|
|
3876
|
-
return await utils.cancel_on_event(
|
|
3979
|
+
return await utils.cancel_on_event(
|
|
3980
|
+
self, Device.EVENT_FLUSH, pending_disconnection
|
|
3981
|
+
)
|
|
3877
3982
|
finally:
|
|
3878
3983
|
connection.remove_listener(
|
|
3879
|
-
|
|
3984
|
+
connection.EVENT_DISCONNECTION, pending_disconnection.set_result
|
|
3880
3985
|
)
|
|
3881
3986
|
connection.remove_listener(
|
|
3882
|
-
|
|
3987
|
+
connection.EVENT_DISCONNECTION_FAILURE,
|
|
3988
|
+
pending_disconnection.set_exception,
|
|
3883
3989
|
)
|
|
3884
3990
|
self.disconnecting = False
|
|
3885
3991
|
|
|
@@ -3901,20 +4007,39 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3901
4007
|
|
|
3902
4008
|
async def update_connection_parameters(
|
|
3903
4009
|
self,
|
|
3904
|
-
connection,
|
|
3905
|
-
connection_interval_min,
|
|
3906
|
-
connection_interval_max,
|
|
3907
|
-
max_latency,
|
|
3908
|
-
supervision_timeout,
|
|
3909
|
-
min_ce_length=0,
|
|
3910
|
-
max_ce_length=0,
|
|
3911
|
-
use_l2cap=False,
|
|
4010
|
+
connection: Connection,
|
|
4011
|
+
connection_interval_min: float,
|
|
4012
|
+
connection_interval_max: float,
|
|
4013
|
+
max_latency: int,
|
|
4014
|
+
supervision_timeout: float,
|
|
4015
|
+
min_ce_length: float = 0.0,
|
|
4016
|
+
max_ce_length: float = 0.0,
|
|
4017
|
+
use_l2cap: bool = False,
|
|
3912
4018
|
) -> None:
|
|
3913
4019
|
'''
|
|
4020
|
+
Request an update of the connection parameters.
|
|
4021
|
+
|
|
4022
|
+
Args:
|
|
4023
|
+
connection: The connection to update
|
|
4024
|
+
connection_interval_min: Minimum interval, in milliseconds.
|
|
4025
|
+
connection_interval_max: Maximum interval, in milliseconds.
|
|
4026
|
+
max_latency: Latency, in number of intervals.
|
|
4027
|
+
supervision_timeout: Timeout, in milliseconds.
|
|
4028
|
+
min_ce_length: Minimum connection event length, in milliseconds.
|
|
4029
|
+
max_ce_length: Maximum connection event length, in milliseconds.
|
|
4030
|
+
use_l2cap: Request the update via L2CAP.
|
|
4031
|
+
|
|
3914
4032
|
NOTE: the name of the parameters may look odd, but it just follows the names
|
|
3915
4033
|
used in the Bluetooth spec.
|
|
3916
4034
|
'''
|
|
3917
4035
|
|
|
4036
|
+
# Convert the input parameters
|
|
4037
|
+
connection_interval_min = int(connection_interval_min / 1.25)
|
|
4038
|
+
connection_interval_max = int(connection_interval_max / 1.25)
|
|
4039
|
+
supervision_timeout = int(supervision_timeout / 10)
|
|
4040
|
+
min_ce_length = int(min_ce_length / 0.625)
|
|
4041
|
+
max_ce_length = int(max_ce_length / 0.625)
|
|
4042
|
+
|
|
3918
4043
|
if use_l2cap:
|
|
3919
4044
|
if connection.role != hci.Role.PERIPHERAL:
|
|
3920
4045
|
raise InvalidStateError(
|
|
@@ -3932,6 +4057,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3932
4057
|
if l2cap_result != l2cap.L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT:
|
|
3933
4058
|
raise ConnectionParameterUpdateError(l2cap_result)
|
|
3934
4059
|
|
|
4060
|
+
return
|
|
4061
|
+
|
|
3935
4062
|
result = await self.send_command(
|
|
3936
4063
|
hci.HCI_LE_Connection_Update_Command(
|
|
3937
4064
|
connection_handle=connection.handle,
|
|
@@ -3953,6 +4080,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
3953
4080
|
return result.return_parameters.rssi
|
|
3954
4081
|
|
|
3955
4082
|
async def get_connection_phy(self, connection: Connection) -> ConnectionPHY:
|
|
4083
|
+
if not self.host.supports_command(hci.HCI_LE_READ_PHY_COMMAND):
|
|
4084
|
+
return ConnectionPHY(hci.Phy.LE_1M, hci.Phy.LE_1M)
|
|
4085
|
+
|
|
3956
4086
|
result = await self.send_command(
|
|
3957
4087
|
hci.HCI_LE_Read_PHY_Command(connection_handle=connection.handle),
|
|
3958
4088
|
check_result=True,
|
|
@@ -4072,7 +4202,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4072
4202
|
else:
|
|
4073
4203
|
return None
|
|
4074
4204
|
|
|
4075
|
-
return await utils.cancel_on_event(self,
|
|
4205
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
|
|
4076
4206
|
finally:
|
|
4077
4207
|
if listener is not None:
|
|
4078
4208
|
self.remove_listener(event_name, listener)
|
|
@@ -4122,7 +4252,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4122
4252
|
if not self.scanning:
|
|
4123
4253
|
await self.start_scanning(filter_duplicates=True)
|
|
4124
4254
|
|
|
4125
|
-
return await utils.cancel_on_event(self,
|
|
4255
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, peer_address)
|
|
4126
4256
|
finally:
|
|
4127
4257
|
if listener is not None:
|
|
4128
4258
|
self.remove_listener(event_name, listener)
|
|
@@ -4198,7 +4328,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4198
4328
|
return keys.link_key.value
|
|
4199
4329
|
|
|
4200
4330
|
# [Classic only]
|
|
4201
|
-
async def authenticate(self, connection):
|
|
4331
|
+
async def authenticate(self, connection: Connection) -> None:
|
|
4202
4332
|
# Set up event handlers
|
|
4203
4333
|
pending_authentication = asyncio.get_running_loop().create_future()
|
|
4204
4334
|
|
|
@@ -4208,8 +4338,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4208
4338
|
def on_authentication_failure(error_code):
|
|
4209
4339
|
pending_authentication.set_exception(hci.HCI_Error(error_code))
|
|
4210
4340
|
|
|
4211
|
-
connection.on(
|
|
4212
|
-
connection.on(
|
|
4341
|
+
connection.on(connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication)
|
|
4342
|
+
connection.on(
|
|
4343
|
+
connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
|
|
4344
|
+
on_authentication_failure,
|
|
4345
|
+
)
|
|
4213
4346
|
|
|
4214
4347
|
# Request the authentication
|
|
4215
4348
|
try:
|
|
@@ -4227,12 +4360,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4227
4360
|
|
|
4228
4361
|
# Wait for the authentication to complete
|
|
4229
4362
|
await utils.cancel_on_event(
|
|
4230
|
-
connection,
|
|
4363
|
+
connection, Connection.EVENT_DISCONNECTION, pending_authentication
|
|
4231
4364
|
)
|
|
4232
4365
|
finally:
|
|
4233
|
-
connection.remove_listener('connection_authentication', on_authentication)
|
|
4234
4366
|
connection.remove_listener(
|
|
4235
|
-
|
|
4367
|
+
connection.EVENT_CONNECTION_AUTHENTICATION, on_authentication
|
|
4368
|
+
)
|
|
4369
|
+
connection.remove_listener(
|
|
4370
|
+
connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE,
|
|
4371
|
+
on_authentication_failure,
|
|
4236
4372
|
)
|
|
4237
4373
|
|
|
4238
4374
|
async def encrypt(self, connection, enable=True):
|
|
@@ -4248,8 +4384,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4248
4384
|
def on_encryption_failure(error_code):
|
|
4249
4385
|
pending_encryption.set_exception(hci.HCI_Error(error_code))
|
|
4250
4386
|
|
|
4251
|
-
connection.on(
|
|
4252
|
-
|
|
4387
|
+
connection.on(
|
|
4388
|
+
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
|
|
4389
|
+
)
|
|
4390
|
+
connection.on(
|
|
4391
|
+
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4392
|
+
)
|
|
4253
4393
|
|
|
4254
4394
|
# Request the encryption
|
|
4255
4395
|
try:
|
|
@@ -4308,13 +4448,15 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4308
4448
|
raise hci.HCI_StatusError(result)
|
|
4309
4449
|
|
|
4310
4450
|
# Wait for the result
|
|
4311
|
-
await utils.cancel_on_event(
|
|
4451
|
+
await utils.cancel_on_event(
|
|
4452
|
+
connection, Connection.EVENT_DISCONNECTION, pending_encryption
|
|
4453
|
+
)
|
|
4312
4454
|
finally:
|
|
4313
4455
|
connection.remove_listener(
|
|
4314
|
-
|
|
4456
|
+
connection.EVENT_CONNECTION_ENCRYPTION_CHANGE, on_encryption_change
|
|
4315
4457
|
)
|
|
4316
4458
|
connection.remove_listener(
|
|
4317
|
-
|
|
4459
|
+
connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, on_encryption_failure
|
|
4318
4460
|
)
|
|
4319
4461
|
|
|
4320
4462
|
async def update_keys(self, address: str, keys: PairingKeys) -> None:
|
|
@@ -4327,7 +4469,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4327
4469
|
except Exception as error:
|
|
4328
4470
|
logger.warning(f'!!! error while storing keys: {error}')
|
|
4329
4471
|
else:
|
|
4330
|
-
self.emit(
|
|
4472
|
+
self.emit(self.EVENT_KEY_STORE_UPDATE)
|
|
4331
4473
|
|
|
4332
4474
|
# [Classic only]
|
|
4333
4475
|
async def switch_role(self, connection: Connection, role: hci.Role):
|
|
@@ -4339,8 +4481,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4339
4481
|
def on_role_change_failure(error_code):
|
|
4340
4482
|
pending_role_change.set_exception(hci.HCI_Error(error_code))
|
|
4341
4483
|
|
|
4342
|
-
connection.on(
|
|
4343
|
-
connection.on(
|
|
4484
|
+
connection.on(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
4485
|
+
connection.on(connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure)
|
|
4344
4486
|
|
|
4345
4487
|
try:
|
|
4346
4488
|
result = await self.send_command(
|
|
@@ -4353,11 +4495,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4353
4495
|
)
|
|
4354
4496
|
raise hci.HCI_StatusError(result)
|
|
4355
4497
|
await utils.cancel_on_event(
|
|
4356
|
-
connection,
|
|
4498
|
+
connection, Connection.EVENT_DISCONNECTION, pending_role_change
|
|
4357
4499
|
)
|
|
4358
4500
|
finally:
|
|
4359
|
-
connection.remove_listener(
|
|
4360
|
-
connection.remove_listener(
|
|
4501
|
+
connection.remove_listener(connection.EVENT_ROLE_CHANGE, on_role_change)
|
|
4502
|
+
connection.remove_listener(
|
|
4503
|
+
connection.EVENT_ROLE_CHANGE_FAILURE, on_role_change_failure
|
|
4504
|
+
)
|
|
4361
4505
|
|
|
4362
4506
|
# [Classic only]
|
|
4363
4507
|
async def request_remote_name(self, remote: Union[hci.Address, Connection]) -> str:
|
|
@@ -4369,7 +4513,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4369
4513
|
)
|
|
4370
4514
|
|
|
4371
4515
|
handler = self.on(
|
|
4372
|
-
|
|
4516
|
+
self.EVENT_REMOTE_NAME,
|
|
4373
4517
|
lambda address, remote_name: (
|
|
4374
4518
|
pending_name.set_result(remote_name)
|
|
4375
4519
|
if address == peer_address
|
|
@@ -4377,7 +4521,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4377
4521
|
),
|
|
4378
4522
|
)
|
|
4379
4523
|
failure_handler = self.on(
|
|
4380
|
-
|
|
4524
|
+
self.EVENT_REMOTE_NAME_FAILURE,
|
|
4381
4525
|
lambda address, error_code: (
|
|
4382
4526
|
pending_name.set_exception(hci.HCI_Error(error_code))
|
|
4383
4527
|
if address == peer_address
|
|
@@ -4403,10 +4547,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4403
4547
|
raise hci.HCI_StatusError(result)
|
|
4404
4548
|
|
|
4405
4549
|
# Wait for the result
|
|
4406
|
-
return await utils.cancel_on_event(self,
|
|
4550
|
+
return await utils.cancel_on_event(self, Device.EVENT_FLUSH, pending_name)
|
|
4407
4551
|
finally:
|
|
4408
|
-
self.remove_listener(
|
|
4409
|
-
self.remove_listener(
|
|
4552
|
+
self.remove_listener(self.EVENT_REMOTE_NAME, handler)
|
|
4553
|
+
self.remove_listener(self.EVENT_REMOTE_NAME_FAILURE, failure_handler)
|
|
4410
4554
|
|
|
4411
4555
|
# [LE only]
|
|
4412
4556
|
@utils.experimental('Only for testing.')
|
|
@@ -4497,8 +4641,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4497
4641
|
if pending_future := pending_cis_establishments.get(cis_handle):
|
|
4498
4642
|
pending_future.set_exception(hci.HCI_Error(status))
|
|
4499
4643
|
|
|
4500
|
-
watcher.on(self,
|
|
4501
|
-
watcher.on(
|
|
4644
|
+
watcher.on(self, self.EVENT_CIS_ESTABLISHMENT, on_cis_establishment)
|
|
4645
|
+
watcher.on(
|
|
4646
|
+
self, self.EVENT_CIS_ESTABLISHMENT_FAILURE, on_cis_establishment_failure
|
|
4647
|
+
)
|
|
4502
4648
|
await self.send_command(
|
|
4503
4649
|
hci.HCI_LE_Create_CIS_Command(
|
|
4504
4650
|
cis_connection_handle=[p[0] for p in cis_acl_pairs],
|
|
@@ -4541,8 +4687,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4541
4687
|
def on_establishment_failure(status: int) -> None:
|
|
4542
4688
|
pending_establishment.set_exception(hci.HCI_Error(status))
|
|
4543
4689
|
|
|
4544
|
-
watcher.on(cis_link,
|
|
4545
|
-
watcher.on(
|
|
4690
|
+
watcher.on(cis_link, cis_link.EVENT_ESTABLISHMENT, on_establishment)
|
|
4691
|
+
watcher.on(
|
|
4692
|
+
cis_link,
|
|
4693
|
+
cis_link.EVENT_ESTABLISHMENT_FAILURE,
|
|
4694
|
+
on_establishment_failure,
|
|
4695
|
+
)
|
|
4546
4696
|
|
|
4547
4697
|
await self.send_command(
|
|
4548
4698
|
hci.HCI_LE_Accept_CIS_Request_Command(connection_handle=handle),
|
|
@@ -4910,33 +5060,33 @@ class Device(utils.CompositeEventEmitter):
|
|
|
4910
5060
|
|
|
4911
5061
|
@host_event_handler
|
|
4912
5062
|
def on_flush(self):
|
|
4913
|
-
self.emit(
|
|
5063
|
+
self.emit(self.EVENT_FLUSH)
|
|
4914
5064
|
for _, connection in self.connections.items():
|
|
4915
|
-
connection.emit(
|
|
5065
|
+
connection.emit(connection.EVENT_DISCONNECTION, 0)
|
|
4916
5066
|
self.connections = {}
|
|
4917
5067
|
|
|
4918
5068
|
# [Classic only]
|
|
4919
5069
|
@host_event_handler
|
|
4920
|
-
def on_link_key(self, bd_addr, link_key, key_type):
|
|
5070
|
+
def on_link_key(self, bd_addr: hci.Address, link_key: bytes, key_type: int) -> None:
|
|
4921
5071
|
# Store the keys in the key store
|
|
4922
5072
|
if self.keystore:
|
|
4923
5073
|
authenticated = key_type in (
|
|
4924
5074
|
hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE,
|
|
4925
5075
|
hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE,
|
|
4926
5076
|
)
|
|
4927
|
-
pairing_keys = PairingKeys(
|
|
4928
|
-
|
|
4929
|
-
|
|
5077
|
+
pairing_keys = PairingKeys(
|
|
5078
|
+
link_key=PairingKeys.Key(value=link_key, authenticated=authenticated),
|
|
5079
|
+
link_key_type=key_type,
|
|
4930
5080
|
)
|
|
4931
5081
|
|
|
4932
5082
|
utils.cancel_on_event(
|
|
4933
|
-
self,
|
|
5083
|
+
self, Device.EVENT_FLUSH, self.update_keys(str(bd_addr), pairing_keys)
|
|
4934
5084
|
)
|
|
4935
5085
|
|
|
4936
5086
|
if connection := self.find_connection_by_bd_addr(
|
|
4937
5087
|
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
4938
5088
|
):
|
|
4939
|
-
connection.
|
|
5089
|
+
connection.emit(connection.EVENT_LINK_KEY)
|
|
4940
5090
|
|
|
4941
5091
|
def add_service(self, service):
|
|
4942
5092
|
self.gatt_server.add_service(service)
|
|
@@ -5108,7 +5258,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5108
5258
|
big.pto = pto
|
|
5109
5259
|
big.irc = irc
|
|
5110
5260
|
big.max_pdu = max_pdu
|
|
5111
|
-
big.iso_interval = iso_interval
|
|
5261
|
+
big.iso_interval = iso_interval * 1.25
|
|
5112
5262
|
big.state = Big.State.ACTIVE
|
|
5113
5263
|
|
|
5114
5264
|
for bis_link in big.bis_links:
|
|
@@ -5157,7 +5307,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5157
5307
|
big_sync.pto = pto
|
|
5158
5308
|
big_sync.irc = irc
|
|
5159
5309
|
big_sync.max_pdu = max_pdu
|
|
5160
|
-
big_sync.iso_interval = iso_interval
|
|
5310
|
+
big_sync.iso_interval = iso_interval * 1.25
|
|
5161
5311
|
big_sync.bis_links = [
|
|
5162
5312
|
BisLink(handle=handle, big=big_sync) for handle in bis_handles
|
|
5163
5313
|
]
|
|
@@ -5199,11 +5349,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5199
5349
|
# Setup auto-restart of the advertising set if needed.
|
|
5200
5350
|
if advertising_set.auto_restart:
|
|
5201
5351
|
connection.once(
|
|
5202
|
-
|
|
5203
|
-
lambda _: utils.cancel_on_event(
|
|
5352
|
+
Connection.EVENT_DISCONNECTION,
|
|
5353
|
+
lambda _: utils.cancel_on_event(
|
|
5354
|
+
self, Device.EVENT_FLUSH, advertising_set.start()
|
|
5355
|
+
),
|
|
5204
5356
|
)
|
|
5205
5357
|
|
|
5206
|
-
self.emit(
|
|
5358
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5207
5359
|
|
|
5208
5360
|
@host_event_handler
|
|
5209
5361
|
def on_connection(
|
|
@@ -5214,7 +5366,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5214
5366
|
self_resolvable_address: Optional[hci.Address],
|
|
5215
5367
|
peer_resolvable_address: Optional[hci.Address],
|
|
5216
5368
|
role: hci.Role,
|
|
5217
|
-
connection_parameters: ConnectionParameters,
|
|
5369
|
+
connection_parameters: Optional[core.ConnectionParameters],
|
|
5218
5370
|
) -> None:
|
|
5219
5371
|
# Convert all-zeros addresses into None.
|
|
5220
5372
|
if self_resolvable_address == hci.Address.ANY_RANDOM:
|
|
@@ -5241,10 +5393,12 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5241
5393
|
self.connections[connection_handle] = connection
|
|
5242
5394
|
|
|
5243
5395
|
# Emit an event to notify listeners of the new connection
|
|
5244
|
-
self.emit(
|
|
5396
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5245
5397
|
|
|
5246
5398
|
return
|
|
5247
5399
|
|
|
5400
|
+
assert connection_parameters is not None
|
|
5401
|
+
|
|
5248
5402
|
if peer_resolvable_address is None:
|
|
5249
5403
|
# Resolve the peer address if we can
|
|
5250
5404
|
if self.address_resolver:
|
|
@@ -5300,7 +5454,11 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5300
5454
|
peer_address,
|
|
5301
5455
|
peer_resolvable_address,
|
|
5302
5456
|
role,
|
|
5303
|
-
|
|
5457
|
+
Connection.Parameters(
|
|
5458
|
+
connection_parameters.connection_interval * 1.25,
|
|
5459
|
+
connection_parameters.peripheral_latency,
|
|
5460
|
+
connection_parameters.supervision_timeout * 10.0,
|
|
5461
|
+
),
|
|
5304
5462
|
)
|
|
5305
5463
|
self.connections[connection_handle] = connection
|
|
5306
5464
|
|
|
@@ -5308,15 +5466,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5308
5466
|
if self.legacy_advertiser.auto_restart:
|
|
5309
5467
|
advertiser = self.legacy_advertiser
|
|
5310
5468
|
connection.once(
|
|
5311
|
-
|
|
5312
|
-
lambda _: utils.cancel_on_event(
|
|
5469
|
+
Connection.EVENT_DISCONNECTION,
|
|
5470
|
+
lambda _: utils.cancel_on_event(
|
|
5471
|
+
self, Device.EVENT_FLUSH, advertiser.start()
|
|
5472
|
+
),
|
|
5313
5473
|
)
|
|
5314
5474
|
else:
|
|
5315
5475
|
self.legacy_advertiser = None
|
|
5316
5476
|
|
|
5317
5477
|
if role == hci.Role.CENTRAL or not self.supports_le_extended_advertising:
|
|
5318
5478
|
# We can emit now, we have all the info we need
|
|
5319
|
-
self.emit(
|
|
5479
|
+
self.emit(self.EVENT_CONNECTION, connection)
|
|
5320
5480
|
return
|
|
5321
5481
|
|
|
5322
5482
|
if role == hci.Role.PERIPHERAL and self.supports_le_extended_advertising:
|
|
@@ -5350,7 +5510,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5350
5510
|
'hci',
|
|
5351
5511
|
hci.HCI_Constant.error_name(error_code),
|
|
5352
5512
|
)
|
|
5353
|
-
self.emit(
|
|
5513
|
+
self.emit(self.EVENT_CONNECTION_FAILURE, error)
|
|
5354
5514
|
|
|
5355
5515
|
# FIXME: Explore a delegate-model for BR/EDR wait connection #56.
|
|
5356
5516
|
@host_event_handler
|
|
@@ -5365,7 +5525,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5365
5525
|
if connection := self.find_connection_by_bd_addr(
|
|
5366
5526
|
bd_addr, transport=PhysicalTransport.BR_EDR
|
|
5367
5527
|
):
|
|
5368
|
-
self.emit(
|
|
5528
|
+
self.emit(self.EVENT_SCO_REQUEST, connection, link_type)
|
|
5369
5529
|
else:
|
|
5370
5530
|
logger.error(f'SCO request from a non-connected device {bd_addr}')
|
|
5371
5531
|
return
|
|
@@ -5409,14 +5569,14 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5409
5569
|
f'*** Disconnection: [0x{connection.handle:04X}] '
|
|
5410
5570
|
f'{connection.peer_address} as {connection.role_name}, reason={reason}'
|
|
5411
5571
|
)
|
|
5412
|
-
connection.emit(
|
|
5572
|
+
connection.emit(connection.EVENT_DISCONNECTION, reason)
|
|
5413
5573
|
|
|
5414
5574
|
# Cleanup subsystems that maintain per-connection state
|
|
5415
5575
|
self.gatt_server.on_disconnection(connection)
|
|
5416
5576
|
elif sco_link := self.sco_links.pop(connection_handle, None):
|
|
5417
|
-
sco_link.emit(
|
|
5577
|
+
sco_link.emit(sco_link.EVENT_DISCONNECTION, reason)
|
|
5418
5578
|
elif cis_link := self.cis_links.pop(connection_handle, None):
|
|
5419
|
-
cis_link.emit(
|
|
5579
|
+
cis_link.emit(cis_link.EVENT_DISCONNECTION, reason)
|
|
5420
5580
|
else:
|
|
5421
5581
|
logger.error(
|
|
5422
5582
|
f'*** Unknown disconnection handle=0x{connection_handle}, reason={reason} ***'
|
|
@@ -5424,7 +5584,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5424
5584
|
|
|
5425
5585
|
@host_event_handler
|
|
5426
5586
|
@with_connection_from_handle
|
|
5427
|
-
def on_disconnection_failure(self, connection, error_code):
|
|
5587
|
+
def on_disconnection_failure(self, connection: Connection, error_code: int):
|
|
5428
5588
|
logger.debug(f'*** Disconnection failed: {error_code}')
|
|
5429
5589
|
error = core.ConnectionError(
|
|
5430
5590
|
error_code,
|
|
@@ -5433,7 +5593,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5433
5593
|
'hci',
|
|
5434
5594
|
hci.HCI_Constant.error_name(error_code),
|
|
5435
5595
|
)
|
|
5436
|
-
connection.emit(
|
|
5596
|
+
connection.emit(connection.EVENT_DISCONNECTION_FAILURE, error)
|
|
5437
5597
|
|
|
5438
5598
|
@host_event_handler
|
|
5439
5599
|
@utils.AsyncRunner.run_in_task()
|
|
@@ -5444,7 +5604,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5444
5604
|
else:
|
|
5445
5605
|
self.auto_restart_inquiry = True
|
|
5446
5606
|
self.discovering = False
|
|
5447
|
-
self.emit(
|
|
5607
|
+
self.emit(self.EVENT_INQUIRY_COMPLETE)
|
|
5448
5608
|
|
|
5449
5609
|
@host_event_handler
|
|
5450
5610
|
@with_connection_from_handle
|
|
@@ -5454,7 +5614,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5454
5614
|
f'{connection.peer_address} as {connection.role_name}'
|
|
5455
5615
|
)
|
|
5456
5616
|
connection.authenticated = True
|
|
5457
|
-
connection.emit(
|
|
5617
|
+
connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION)
|
|
5458
5618
|
|
|
5459
5619
|
@host_event_handler
|
|
5460
5620
|
@with_connection_from_handle
|
|
@@ -5463,7 +5623,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5463
5623
|
f'*** Connection Authentication Failure: [0x{connection.handle:04X}] '
|
|
5464
5624
|
f'{connection.peer_address} as {connection.role_name}, error={error}'
|
|
5465
5625
|
)
|
|
5466
|
-
connection.emit(
|
|
5626
|
+
connection.emit(connection.EVENT_CONNECTION_AUTHENTICATION_FAILURE, error)
|
|
5467
5627
|
|
|
5468
5628
|
# [Classic only]
|
|
5469
5629
|
@host_event_handler
|
|
@@ -5568,7 +5728,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5568
5728
|
|
|
5569
5729
|
async def reply() -> None:
|
|
5570
5730
|
try:
|
|
5571
|
-
if await utils.cancel_on_event(
|
|
5731
|
+
if await utils.cancel_on_event(
|
|
5732
|
+
connection, Connection.EVENT_DISCONNECTION, method()
|
|
5733
|
+
):
|
|
5572
5734
|
await self.host.send_command(
|
|
5573
5735
|
hci.HCI_User_Confirmation_Request_Reply_Command(
|
|
5574
5736
|
bd_addr=connection.peer_address
|
|
@@ -5596,7 +5758,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5596
5758
|
async def reply() -> None:
|
|
5597
5759
|
try:
|
|
5598
5760
|
number = await utils.cancel_on_event(
|
|
5599
|
-
connection,
|
|
5761
|
+
connection,
|
|
5762
|
+
Connection.EVENT_DISCONNECTION,
|
|
5763
|
+
pairing_config.delegate.get_number(),
|
|
5600
5764
|
)
|
|
5601
5765
|
if number is not None:
|
|
5602
5766
|
await self.host.send_command(
|
|
@@ -5630,7 +5794,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5630
5794
|
# Ask the user to enter a string
|
|
5631
5795
|
async def get_pin_code():
|
|
5632
5796
|
pin_code = await utils.cancel_on_event(
|
|
5633
|
-
connection,
|
|
5797
|
+
connection,
|
|
5798
|
+
Connection.EVENT_DISCONNECTION,
|
|
5799
|
+
pairing_config.delegate.get_string(16),
|
|
5634
5800
|
)
|
|
5635
5801
|
|
|
5636
5802
|
if pin_code is not None:
|
|
@@ -5669,7 +5835,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5669
5835
|
|
|
5670
5836
|
# Show the passkey to the user
|
|
5671
5837
|
utils.cancel_on_event(
|
|
5672
|
-
connection,
|
|
5838
|
+
connection,
|
|
5839
|
+
Connection.EVENT_DISCONNECTION,
|
|
5840
|
+
pairing_config.delegate.display_number(passkey, digits=6),
|
|
5673
5841
|
)
|
|
5674
5842
|
|
|
5675
5843
|
# [Classic only]
|
|
@@ -5681,22 +5849,22 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5681
5849
|
remote_name = remote_name.decode('utf-8')
|
|
5682
5850
|
if connection:
|
|
5683
5851
|
connection.peer_name = remote_name
|
|
5684
|
-
connection.emit(
|
|
5685
|
-
self.emit(
|
|
5852
|
+
connection.emit(connection.EVENT_REMOTE_NAME)
|
|
5853
|
+
self.emit(self.EVENT_REMOTE_NAME, address, remote_name)
|
|
5686
5854
|
except UnicodeDecodeError as error:
|
|
5687
5855
|
logger.warning('peer name is not valid UTF-8')
|
|
5688
5856
|
if connection:
|
|
5689
|
-
connection.emit(
|
|
5857
|
+
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5690
5858
|
else:
|
|
5691
|
-
self.emit(
|
|
5859
|
+
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
5692
5860
|
|
|
5693
5861
|
# [Classic only]
|
|
5694
5862
|
@host_event_handler
|
|
5695
5863
|
@try_with_connection_from_address
|
|
5696
5864
|
def on_remote_name_failure(self, connection: Connection, address, error):
|
|
5697
5865
|
if connection:
|
|
5698
|
-
connection.emit(
|
|
5699
|
-
self.emit(
|
|
5866
|
+
connection.emit(connection.EVENT_REMOTE_NAME_FAILURE, error)
|
|
5867
|
+
self.emit(self.EVENT_REMOTE_NAME_FAILURE, address, error)
|
|
5700
5868
|
|
|
5701
5869
|
# [Classic only]
|
|
5702
5870
|
@host_event_handler
|
|
@@ -5716,7 +5884,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5716
5884
|
handle=sco_handle,
|
|
5717
5885
|
link_type=link_type,
|
|
5718
5886
|
)
|
|
5719
|
-
self.emit(
|
|
5887
|
+
self.emit(self.EVENT_SCO_CONNECTION, sco_link)
|
|
5720
5888
|
|
|
5721
5889
|
# [Classic only]
|
|
5722
5890
|
@host_event_handler
|
|
@@ -5726,7 +5894,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5726
5894
|
self, acl_connection: Connection, status: int
|
|
5727
5895
|
) -> None:
|
|
5728
5896
|
logger.debug(f'*** SCO connection failure: {acl_connection.peer_address}***')
|
|
5729
|
-
self.emit(
|
|
5897
|
+
self.emit(self.EVENT_SCO_CONNECTION_FAILURE)
|
|
5730
5898
|
|
|
5731
5899
|
# [Classic only]
|
|
5732
5900
|
@host_event_handler
|
|
@@ -5763,7 +5931,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5763
5931
|
cig_id=cig_id,
|
|
5764
5932
|
cis_id=cis_id,
|
|
5765
5933
|
)
|
|
5766
|
-
self.emit(
|
|
5934
|
+
self.emit(self.EVENT_CIS_REQUEST, acl_connection, cis_handle, cig_id, cis_id)
|
|
5767
5935
|
|
|
5768
5936
|
# [LE only]
|
|
5769
5937
|
@host_event_handler
|
|
@@ -5782,8 +5950,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5782
5950
|
f'cis_id=[0x{cis_link.cis_id:02X}] ***'
|
|
5783
5951
|
)
|
|
5784
5952
|
|
|
5785
|
-
cis_link.emit(
|
|
5786
|
-
self.emit(
|
|
5953
|
+
cis_link.emit(cis_link.EVENT_ESTABLISHMENT)
|
|
5954
|
+
self.emit(self.EVENT_CIS_ESTABLISHMENT, cis_link)
|
|
5787
5955
|
|
|
5788
5956
|
# [LE only]
|
|
5789
5957
|
@host_event_handler
|
|
@@ -5791,8 +5959,8 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5791
5959
|
def on_cis_establishment_failure(self, cis_handle: int, status: int) -> None:
|
|
5792
5960
|
logger.debug(f'*** CIS Establishment Failure: cis=[0x{cis_handle:04X}] ***')
|
|
5793
5961
|
if cis_link := self.cis_links.pop(cis_handle):
|
|
5794
|
-
cis_link.emit(
|
|
5795
|
-
self.emit(
|
|
5962
|
+
cis_link.emit(cis_link.EVENT_ESTABLISHMENT_FAILURE, status)
|
|
5963
|
+
self.emit(self.EVENT_CIS_ESTABLISHMENT_FAILURE, cis_handle, status)
|
|
5796
5964
|
|
|
5797
5965
|
# [LE only]
|
|
5798
5966
|
@host_event_handler
|
|
@@ -5805,13 +5973,17 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5805
5973
|
|
|
5806
5974
|
@host_event_handler
|
|
5807
5975
|
@with_connection_from_handle
|
|
5808
|
-
def on_connection_encryption_change(
|
|
5976
|
+
def on_connection_encryption_change(
|
|
5977
|
+
self, connection, encryption, encryption_key_size
|
|
5978
|
+
):
|
|
5809
5979
|
logger.debug(
|
|
5810
5980
|
f'*** Connection Encryption Change: [0x{connection.handle:04X}] '
|
|
5811
5981
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5812
|
-
f'encryption={encryption}'
|
|
5982
|
+
f'encryption={encryption}, '
|
|
5983
|
+
f'key_size={encryption_key_size}'
|
|
5813
5984
|
)
|
|
5814
5985
|
connection.encryption = encryption
|
|
5986
|
+
connection.encryption_key_size = encryption_key_size
|
|
5815
5987
|
if (
|
|
5816
5988
|
not connection.authenticated
|
|
5817
5989
|
and connection.transport == PhysicalTransport.BR_EDR
|
|
@@ -5826,7 +5998,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5826
5998
|
):
|
|
5827
5999
|
connection.authenticated = True
|
|
5828
6000
|
connection.sc = True
|
|
5829
|
-
connection.emit(
|
|
6001
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_CHANGE)
|
|
5830
6002
|
|
|
5831
6003
|
@host_event_handler
|
|
5832
6004
|
@with_connection_from_handle
|
|
@@ -5836,7 +6008,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5836
6008
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5837
6009
|
f'error={error}'
|
|
5838
6010
|
)
|
|
5839
|
-
connection.emit(
|
|
6011
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_FAILURE, error)
|
|
5840
6012
|
|
|
5841
6013
|
@host_event_handler
|
|
5842
6014
|
@with_connection_from_handle
|
|
@@ -5845,7 +6017,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5845
6017
|
f'*** Connection Key Refresh: [0x{connection.handle:04X}] '
|
|
5846
6018
|
f'{connection.peer_address} as {connection.role_name}'
|
|
5847
6019
|
)
|
|
5848
|
-
connection.emit(
|
|
6020
|
+
connection.emit(connection.EVENT_CONNECTION_ENCRYPTION_KEY_REFRESH)
|
|
5849
6021
|
|
|
5850
6022
|
@host_event_handler
|
|
5851
6023
|
@with_connection_from_handle
|
|
@@ -5856,7 +6028,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5856
6028
|
f'{connection_parameters}'
|
|
5857
6029
|
)
|
|
5858
6030
|
connection.parameters = connection_parameters
|
|
5859
|
-
connection.emit(
|
|
6031
|
+
connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE)
|
|
5860
6032
|
|
|
5861
6033
|
@host_event_handler
|
|
5862
6034
|
@with_connection_from_handle
|
|
@@ -5866,7 +6038,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5866
6038
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5867
6039
|
f'error={error}'
|
|
5868
6040
|
)
|
|
5869
|
-
connection.emit(
|
|
6041
|
+
connection.emit(connection.EVENT_CONNECTION_PARAMETERS_UPDATE_FAILURE, error)
|
|
5870
6042
|
|
|
5871
6043
|
@host_event_handler
|
|
5872
6044
|
@with_connection_from_handle
|
|
@@ -5876,7 +6048,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5876
6048
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5877
6049
|
f'{phy}'
|
|
5878
6050
|
)
|
|
5879
|
-
connection.emit(
|
|
6051
|
+
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE, phy)
|
|
5880
6052
|
|
|
5881
6053
|
@host_event_handler
|
|
5882
6054
|
@with_connection_from_handle
|
|
@@ -5886,7 +6058,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5886
6058
|
f'{connection.peer_address} as {connection.role_name}, '
|
|
5887
6059
|
f'error={error}'
|
|
5888
6060
|
)
|
|
5889
|
-
connection.emit(
|
|
6061
|
+
connection.emit(connection.EVENT_CONNECTION_PHY_UPDATE_FAILURE, error)
|
|
5890
6062
|
|
|
5891
6063
|
@host_event_handler
|
|
5892
6064
|
@with_connection_from_handle
|
|
@@ -5897,7 +6069,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5897
6069
|
f'{att_mtu}'
|
|
5898
6070
|
)
|
|
5899
6071
|
connection.att_mtu = att_mtu
|
|
5900
|
-
connection.emit(
|
|
6072
|
+
connection.emit(connection.EVENT_CONNECTION_ATT_MTU_UPDATE)
|
|
5901
6073
|
|
|
5902
6074
|
@host_event_handler
|
|
5903
6075
|
@with_connection_from_handle
|
|
@@ -5914,7 +6086,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5914
6086
|
max_rx_octets,
|
|
5915
6087
|
max_rx_time,
|
|
5916
6088
|
)
|
|
5917
|
-
connection.emit(
|
|
6089
|
+
connection.emit(connection.EVENT_CONNECTION_DATA_LENGTH_CHANGE)
|
|
5918
6090
|
|
|
5919
6091
|
@host_event_handler
|
|
5920
6092
|
def on_cs_remote_supported_capabilities(
|
|
@@ -5924,7 +6096,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5924
6096
|
return
|
|
5925
6097
|
|
|
5926
6098
|
if event.status != hci.HCI_SUCCESS:
|
|
5927
|
-
connection.emit(
|
|
6099
|
+
connection.emit(
|
|
6100
|
+
connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES_FAILURE, event.status
|
|
6101
|
+
)
|
|
5928
6102
|
return
|
|
5929
6103
|
|
|
5930
6104
|
capabilities = ChannelSoundingCapabilities(
|
|
@@ -5949,7 +6123,7 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5949
6123
|
t_sw_time_supported=event.t_sw_time_supported,
|
|
5950
6124
|
tx_snr_capability=event.tx_snr_capability,
|
|
5951
6125
|
)
|
|
5952
|
-
connection.emit(
|
|
6126
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_CAPABILITIES, capabilities)
|
|
5953
6127
|
|
|
5954
6128
|
@host_event_handler
|
|
5955
6129
|
def on_cs_config(self, event: hci.HCI_LE_CS_Config_Complete_Event):
|
|
@@ -5957,7 +6131,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5957
6131
|
return
|
|
5958
6132
|
|
|
5959
6133
|
if event.status != hci.HCI_SUCCESS:
|
|
5960
|
-
connection.emit(
|
|
6134
|
+
connection.emit(
|
|
6135
|
+
connection.EVENT_CHANNEL_SOUNDING_CONFIG_FAILURE, event.status
|
|
6136
|
+
)
|
|
5961
6137
|
return
|
|
5962
6138
|
if event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.CREATED:
|
|
5963
6139
|
config = ChannelSoundingConfig(
|
|
@@ -5983,11 +6159,13 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5983
6159
|
t_pm_time=event.t_pm_time,
|
|
5984
6160
|
)
|
|
5985
6161
|
connection.cs_configs[event.config_id] = config
|
|
5986
|
-
connection.emit(
|
|
6162
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_CONFIG, config)
|
|
5987
6163
|
elif event.action == hci.HCI_LE_CS_Config_Complete_Event.Action.REMOVED:
|
|
5988
6164
|
try:
|
|
5989
6165
|
config = connection.cs_configs.pop(event.config_id)
|
|
5990
|
-
connection.emit(
|
|
6166
|
+
connection.emit(
|
|
6167
|
+
connection.EVENT_CHANNEL_SOUNDING_CONFIG_REMOVED, config.config_id
|
|
6168
|
+
)
|
|
5991
6169
|
except KeyError:
|
|
5992
6170
|
logger.error('Removing unknown config %d', event.config_id)
|
|
5993
6171
|
|
|
@@ -5997,7 +6175,9 @@ class Device(utils.CompositeEventEmitter):
|
|
|
5997
6175
|
return
|
|
5998
6176
|
|
|
5999
6177
|
if event.status != hci.HCI_SUCCESS:
|
|
6000
|
-
connection.emit(
|
|
6178
|
+
connection.emit(
|
|
6179
|
+
connection.EVENT_CHANNEL_SOUNDING_PROCEDURE_FAILURE, event.status
|
|
6180
|
+
)
|
|
6001
6181
|
return
|
|
6002
6182
|
|
|
6003
6183
|
procedure = ChannelSoundingProcedure(
|
|
@@ -6014,37 +6194,37 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6014
6194
|
max_procedure_len=event.max_procedure_len,
|
|
6015
6195
|
)
|
|
6016
6196
|
connection.cs_procedures[procedure.config_id] = procedure
|
|
6017
|
-
connection.emit(
|
|
6197
|
+
connection.emit(connection.EVENT_CHANNEL_SOUNDING_PROCEDURE, procedure)
|
|
6018
6198
|
|
|
6019
6199
|
# [Classic only]
|
|
6020
6200
|
@host_event_handler
|
|
6021
6201
|
@with_connection_from_address
|
|
6022
6202
|
def on_role_change(self, connection, new_role):
|
|
6023
6203
|
connection.role = new_role
|
|
6024
|
-
connection.emit(
|
|
6204
|
+
connection.emit(connection.EVENT_ROLE_CHANGE, new_role)
|
|
6025
6205
|
|
|
6026
6206
|
# [Classic only]
|
|
6027
6207
|
@host_event_handler
|
|
6028
6208
|
@try_with_connection_from_address
|
|
6029
6209
|
def on_role_change_failure(self, connection, address, error):
|
|
6030
6210
|
if connection:
|
|
6031
|
-
connection.emit(
|
|
6032
|
-
self.emit(
|
|
6211
|
+
connection.emit(connection.EVENT_ROLE_CHANGE_FAILURE, error)
|
|
6212
|
+
self.emit(self.EVENT_ROLE_CHANGE_FAILURE, address, error)
|
|
6033
6213
|
|
|
6034
6214
|
# [Classic only]
|
|
6035
6215
|
@host_event_handler
|
|
6036
6216
|
@with_connection_from_address
|
|
6037
6217
|
def on_classic_pairing(self, connection: Connection) -> None:
|
|
6038
|
-
connection.emit(
|
|
6218
|
+
connection.emit(connection.EVENT_CLASSIC_PAIRING)
|
|
6039
6219
|
|
|
6040
6220
|
# [Classic only]
|
|
6041
6221
|
@host_event_handler
|
|
6042
6222
|
@with_connection_from_address
|
|
6043
6223
|
def on_classic_pairing_failure(self, connection: Connection, status) -> None:
|
|
6044
|
-
connection.emit(
|
|
6224
|
+
connection.emit(connection.EVENT_CLASSIC_PAIRING_FAILURE, status)
|
|
6045
6225
|
|
|
6046
6226
|
def on_pairing_start(self, connection: Connection) -> None:
|
|
6047
|
-
connection.emit(
|
|
6227
|
+
connection.emit(connection.EVENT_PAIRING_START)
|
|
6048
6228
|
|
|
6049
6229
|
def on_pairing(
|
|
6050
6230
|
self,
|
|
@@ -6058,10 +6238,10 @@ class Device(utils.CompositeEventEmitter):
|
|
|
6058
6238
|
connection.peer_address = identity_address
|
|
6059
6239
|
connection.sc = sc
|
|
6060
6240
|
connection.authenticated = True
|
|
6061
|
-
connection.emit(
|
|
6241
|
+
connection.emit(connection.EVENT_PAIRING, keys)
|
|
6062
6242
|
|
|
6063
6243
|
def on_pairing_failure(self, connection: Connection, reason: int) -> None:
|
|
6064
|
-
connection.emit(
|
|
6244
|
+
connection.emit(connection.EVENT_PAIRING_FAILURE, reason)
|
|
6065
6245
|
|
|
6066
6246
|
@with_connection_from_handle
|
|
6067
6247
|
def on_gatt_pdu(self, connection, pdu):
|