bumble 0.0.208__py3-none-any.whl → 0.0.210__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 +7 -7
- bumble/apps/auracast.py +37 -29
- bumble/apps/bench.py +9 -7
- bumble/apps/console.py +1 -1
- bumble/apps/lea_unicast/app.py +6 -2
- bumble/apps/pair.py +4 -3
- bumble/apps/player/player.py +3 -3
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/speaker/speaker.py +4 -2
- bumble/att.py +12 -5
- bumble/avc.py +5 -5
- bumble/avdtp.py +9 -10
- bumble/avrcp.py +18 -19
- bumble/bridge.py +2 -2
- bumble/controller.py +13 -15
- bumble/core.py +61 -60
- bumble/device.py +193 -162
- bumble/drivers/__init__.py +2 -2
- bumble/gap.py +1 -1
- bumble/gatt.py +16 -0
- bumble/gatt_adapters.py +3 -3
- bumble/gatt_client.py +27 -21
- bumble/gatt_server.py +9 -10
- bumble/hci.py +109 -90
- bumble/hfp.py +3 -3
- bumble/hid.py +4 -3
- bumble/host.py +30 -19
- bumble/keys.py +3 -3
- bumble/l2cap.py +21 -19
- bumble/link.py +5 -6
- bumble/pairing.py +3 -3
- bumble/pandora/__init__.py +5 -5
- bumble/pandora/host.py +30 -23
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +17 -19
- bumble/pandora/utils.py +2 -2
- bumble/profiles/aics.py +6 -6
- bumble/profiles/ancs.py +513 -0
- bumble/profiles/ascs.py +17 -10
- bumble/profiles/asha.py +5 -5
- bumble/profiles/bass.py +1 -1
- bumble/profiles/csip.py +10 -10
- bumble/profiles/gatt_service.py +12 -12
- bumble/profiles/hap.py +16 -16
- bumble/profiles/mcp.py +26 -24
- bumble/profiles/pacs.py +6 -6
- bumble/profiles/pbp.py +1 -1
- bumble/profiles/vcs.py +6 -4
- bumble/profiles/vocs.py +3 -3
- bumble/rfcomm.py +8 -8
- bumble/sdp.py +1 -1
- bumble/smp.py +39 -33
- bumble/transport/__init__.py +24 -19
- bumble/transport/android_emulator.py +8 -4
- bumble/transport/android_netsim.py +8 -5
- bumble/transport/common.py +5 -1
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +1 -1
- bumble/transport/pty.py +1 -1
- bumble/transport/pyusb.py +3 -3
- bumble/transport/serial.py +1 -1
- bumble/transport/tcp_client.py +1 -1
- bumble/transport/tcp_server.py +1 -1
- bumble/transport/udp.py +1 -1
- bumble/transport/unix.py +1 -1
- bumble/transport/usb.py +1 -3
- bumble/transport/vhci.py +2 -2
- bumble/transport/ws_client.py +6 -1
- bumble/transport/ws_server.py +1 -1
- bumble/utils.py +89 -76
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/METADATA +3 -2
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/RECORD +77 -76
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/WHEEL +1 -1
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info/licenses}/LICENSE +0 -0
- {bumble-0.0.208.dist-info → bumble-0.0.210.dist-info}/top_level.txt +0 -0
bumble/l2cap.py
CHANGED
|
@@ -23,7 +23,6 @@ import logging
|
|
|
23
23
|
import struct
|
|
24
24
|
|
|
25
25
|
from collections import deque
|
|
26
|
-
from pyee import EventEmitter
|
|
27
26
|
from typing import (
|
|
28
27
|
Dict,
|
|
29
28
|
Type,
|
|
@@ -39,19 +38,19 @@ from typing import (
|
|
|
39
38
|
TYPE_CHECKING,
|
|
40
39
|
)
|
|
41
40
|
|
|
42
|
-
from
|
|
43
|
-
from .colors import color
|
|
44
|
-
from .core import (
|
|
45
|
-
BT_CENTRAL_ROLE,
|
|
41
|
+
from bumble import utils
|
|
42
|
+
from bumble.colors import color
|
|
43
|
+
from bumble.core import (
|
|
46
44
|
InvalidStateError,
|
|
47
45
|
InvalidArgumentError,
|
|
48
46
|
InvalidPacketError,
|
|
49
47
|
OutOfResourcesError,
|
|
50
48
|
ProtocolError,
|
|
51
49
|
)
|
|
52
|
-
from .hci import (
|
|
50
|
+
from bumble.hci import (
|
|
53
51
|
HCI_LE_Connection_Update_Command,
|
|
54
52
|
HCI_Object,
|
|
53
|
+
Role,
|
|
55
54
|
key_with_value,
|
|
56
55
|
name_or_number,
|
|
57
56
|
)
|
|
@@ -720,7 +719,7 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
|
|
|
720
719
|
|
|
721
720
|
|
|
722
721
|
# -----------------------------------------------------------------------------
|
|
723
|
-
class ClassicChannel(EventEmitter):
|
|
722
|
+
class ClassicChannel(utils.EventEmitter):
|
|
724
723
|
class State(enum.IntEnum):
|
|
725
724
|
# States
|
|
726
725
|
CLOSED = 0x00
|
|
@@ -821,8 +820,8 @@ class ClassicChannel(EventEmitter):
|
|
|
821
820
|
|
|
822
821
|
# Wait for the connection to succeed or fail
|
|
823
822
|
try:
|
|
824
|
-
return await
|
|
825
|
-
'disconnection', self.connection_result
|
|
823
|
+
return await utils.cancel_on_event(
|
|
824
|
+
self.connection, 'disconnection', self.connection_result
|
|
826
825
|
)
|
|
827
826
|
finally:
|
|
828
827
|
self.connection_result = None
|
|
@@ -1026,7 +1025,7 @@ class ClassicChannel(EventEmitter):
|
|
|
1026
1025
|
|
|
1027
1026
|
|
|
1028
1027
|
# -----------------------------------------------------------------------------
|
|
1029
|
-
class LeCreditBasedChannel(EventEmitter):
|
|
1028
|
+
class LeCreditBasedChannel(utils.EventEmitter):
|
|
1030
1029
|
"""
|
|
1031
1030
|
LE Credit-based Connection Oriented Channel
|
|
1032
1031
|
"""
|
|
@@ -1381,7 +1380,7 @@ class LeCreditBasedChannel(EventEmitter):
|
|
|
1381
1380
|
|
|
1382
1381
|
|
|
1383
1382
|
# -----------------------------------------------------------------------------
|
|
1384
|
-
class ClassicChannelServer(EventEmitter):
|
|
1383
|
+
class ClassicChannelServer(utils.EventEmitter):
|
|
1385
1384
|
def __init__(
|
|
1386
1385
|
self,
|
|
1387
1386
|
manager: ChannelManager,
|
|
@@ -1406,7 +1405,7 @@ class ClassicChannelServer(EventEmitter):
|
|
|
1406
1405
|
|
|
1407
1406
|
|
|
1408
1407
|
# -----------------------------------------------------------------------------
|
|
1409
|
-
class LeCreditBasedChannelServer(EventEmitter):
|
|
1408
|
+
class LeCreditBasedChannelServer(utils.EventEmitter):
|
|
1410
1409
|
def __init__(
|
|
1411
1410
|
self,
|
|
1412
1411
|
manager: ChannelManager,
|
|
@@ -1521,6 +1520,9 @@ class ChannelManager:
|
|
|
1521
1520
|
|
|
1522
1521
|
def next_identifier(self, connection: Connection) -> int:
|
|
1523
1522
|
identifier = (self.identifiers.setdefault(connection.handle, 0) + 1) % 256
|
|
1523
|
+
# 0x00 is an invalid ID (BT Core Spec, Vol 3, Part A, Sect 4
|
|
1524
|
+
if identifier == 0:
|
|
1525
|
+
identifier = 1
|
|
1524
1526
|
self.identifiers[connection.handle] = identifier
|
|
1525
1527
|
return identifier
|
|
1526
1528
|
|
|
@@ -1533,7 +1535,7 @@ class ChannelManager:
|
|
|
1533
1535
|
if cid in self.fixed_channels:
|
|
1534
1536
|
del self.fixed_channels[cid]
|
|
1535
1537
|
|
|
1536
|
-
@deprecated("Please use create_classic_server")
|
|
1538
|
+
@utils.deprecated("Please use create_classic_server")
|
|
1537
1539
|
def register_server(
|
|
1538
1540
|
self,
|
|
1539
1541
|
psm: int,
|
|
@@ -1579,7 +1581,7 @@ class ChannelManager:
|
|
|
1579
1581
|
|
|
1580
1582
|
return self.servers[spec.psm]
|
|
1581
1583
|
|
|
1582
|
-
@deprecated("Please use create_le_credit_based_server()")
|
|
1584
|
+
@utils.deprecated("Please use create_le_credit_based_server()")
|
|
1583
1585
|
def register_le_coc_server(
|
|
1584
1586
|
self,
|
|
1585
1587
|
psm: int,
|
|
@@ -1908,7 +1910,7 @@ class ChannelManager:
|
|
|
1908
1910
|
def on_l2cap_connection_parameter_update_request(
|
|
1909
1911
|
self, connection: Connection, cid: int, request
|
|
1910
1912
|
):
|
|
1911
|
-
if connection.role ==
|
|
1913
|
+
if connection.role == Role.CENTRAL:
|
|
1912
1914
|
self.send_control_frame(
|
|
1913
1915
|
connection,
|
|
1914
1916
|
cid,
|
|
@@ -2123,7 +2125,7 @@ class ChannelManager:
|
|
|
2123
2125
|
if channel.source_cid in connection_channels:
|
|
2124
2126
|
del connection_channels[channel.source_cid]
|
|
2125
2127
|
|
|
2126
|
-
@deprecated("Please use create_le_credit_based_channel()")
|
|
2128
|
+
@utils.deprecated("Please use create_le_credit_based_channel()")
|
|
2127
2129
|
async def open_le_coc(
|
|
2128
2130
|
self, connection: Connection, psm: int, max_credits: int, mtu: int, mps: int
|
|
2129
2131
|
) -> LeCreditBasedChannel:
|
|
@@ -2180,7 +2182,7 @@ class ChannelManager:
|
|
|
2180
2182
|
|
|
2181
2183
|
return channel
|
|
2182
2184
|
|
|
2183
|
-
@deprecated("Please use create_classic_channel()")
|
|
2185
|
+
@utils.deprecated("Please use create_classic_channel()")
|
|
2184
2186
|
async def connect(self, connection: Connection, psm: int) -> ClassicChannel:
|
|
2185
2187
|
return await self.create_classic_channel(
|
|
2186
2188
|
connection=connection, spec=ClassicChannelSpec(psm=psm)
|
|
@@ -2230,12 +2232,12 @@ class ChannelManager:
|
|
|
2230
2232
|
|
|
2231
2233
|
|
|
2232
2234
|
class Channel(ClassicChannel):
|
|
2233
|
-
@deprecated("Please use ClassicChannel")
|
|
2235
|
+
@utils.deprecated("Please use ClassicChannel")
|
|
2234
2236
|
def __init__(self, *args, **kwargs) -> None:
|
|
2235
2237
|
super().__init__(*args, **kwargs)
|
|
2236
2238
|
|
|
2237
2239
|
|
|
2238
2240
|
class LeConnectionOrientedChannel(LeCreditBasedChannel):
|
|
2239
|
-
@deprecated("Please use LeCreditBasedChannel")
|
|
2241
|
+
@utils.deprecated("Please use LeCreditBasedChannel")
|
|
2240
2242
|
def __init__(self, *args, **kwargs) -> None:
|
|
2241
2243
|
super().__init__(*args, **kwargs)
|
bumble/link.py
CHANGED
|
@@ -20,14 +20,13 @@ import asyncio
|
|
|
20
20
|
from functools import partial
|
|
21
21
|
|
|
22
22
|
from bumble.core import (
|
|
23
|
-
|
|
24
|
-
BT_BR_EDR_TRANSPORT,
|
|
25
|
-
BT_LE_TRANSPORT,
|
|
23
|
+
PhysicalTransport,
|
|
26
24
|
InvalidStateError,
|
|
27
25
|
)
|
|
28
26
|
from bumble.colors import color
|
|
29
27
|
from bumble.hci import (
|
|
30
28
|
Address,
|
|
29
|
+
Role,
|
|
31
30
|
HCI_SUCCESS,
|
|
32
31
|
HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR,
|
|
33
32
|
HCI_CONNECTION_TIMEOUT_ERROR,
|
|
@@ -116,10 +115,10 @@ class LocalLink:
|
|
|
116
115
|
|
|
117
116
|
def send_acl_data(self, sender_controller, destination_address, transport, data):
|
|
118
117
|
# Send the data to the first controller with a matching address
|
|
119
|
-
if transport ==
|
|
118
|
+
if transport == PhysicalTransport.LE:
|
|
120
119
|
destination_controller = self.find_controller(destination_address)
|
|
121
120
|
source_address = sender_controller.random_address
|
|
122
|
-
elif transport ==
|
|
121
|
+
elif transport == PhysicalTransport.BR_EDR:
|
|
123
122
|
destination_controller = self.find_classic_controller(destination_address)
|
|
124
123
|
source_address = sender_controller.public_address
|
|
125
124
|
else:
|
|
@@ -292,7 +291,7 @@ class LocalLink:
|
|
|
292
291
|
return
|
|
293
292
|
|
|
294
293
|
async def task():
|
|
295
|
-
if responder_role !=
|
|
294
|
+
if responder_role != Role.PERIPHERAL:
|
|
296
295
|
initiator_controller.on_classic_role_change(
|
|
297
296
|
responder_controller.public_address, int(not (responder_role))
|
|
298
297
|
)
|
bumble/pairing.py
CHANGED
|
@@ -20,14 +20,14 @@ import enum
|
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from typing import Optional, Tuple
|
|
22
22
|
|
|
23
|
-
from .hci import (
|
|
23
|
+
from bumble.hci import (
|
|
24
24
|
Address,
|
|
25
25
|
HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
26
26
|
HCI_DISPLAY_ONLY_IO_CAPABILITY,
|
|
27
27
|
HCI_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
28
28
|
HCI_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
29
29
|
)
|
|
30
|
-
from .smp import (
|
|
30
|
+
from bumble.smp import (
|
|
31
31
|
SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
32
32
|
SMP_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
33
33
|
SMP_DISPLAY_ONLY_IO_CAPABILITY,
|
|
@@ -41,7 +41,7 @@ from .smp import (
|
|
|
41
41
|
OobLegacyContext,
|
|
42
42
|
OobSharedData,
|
|
43
43
|
)
|
|
44
|
-
from .core import AdvertisingData, LeRole
|
|
44
|
+
from bumble.core import AdvertisingData, LeRole
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
# -----------------------------------------------------------------------------
|
bumble/pandora/__init__.py
CHANGED
|
@@ -22,11 +22,11 @@ __version__ = "0.0.1"
|
|
|
22
22
|
import grpc
|
|
23
23
|
import grpc.aio
|
|
24
24
|
|
|
25
|
-
from .config import Config
|
|
26
|
-
from .device import PandoraDevice
|
|
27
|
-
from .host import HostService
|
|
28
|
-
from .l2cap import L2CAPService
|
|
29
|
-
from .security import SecurityService, SecurityStorageService
|
|
25
|
+
from bumble.pandora.config import Config
|
|
26
|
+
from bumble.pandora.device import PandoraDevice
|
|
27
|
+
from bumble.pandora.host import HostService
|
|
28
|
+
from bumble.pandora.l2cap import L2CAPService
|
|
29
|
+
from bumble.pandora.security import SecurityService, SecurityStorageService
|
|
30
30
|
from pandora.host_grpc_aio import add_HostServicer_to_server
|
|
31
31
|
from pandora.l2cap_grpc_aio import add_L2CAPServicer_to_server
|
|
32
32
|
from pandora.security_grpc_aio import (
|
bumble/pandora/host.py
CHANGED
|
@@ -20,12 +20,11 @@ import grpc.aio
|
|
|
20
20
|
import logging
|
|
21
21
|
import struct
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
from .
|
|
23
|
+
import bumble.utils
|
|
24
|
+
from bumble.pandora import utils
|
|
25
|
+
from bumble.pandora.config import Config
|
|
25
26
|
from bumble.core import (
|
|
26
|
-
|
|
27
|
-
BT_LE_TRANSPORT,
|
|
28
|
-
BT_PERIPHERAL_ROLE,
|
|
27
|
+
PhysicalTransport,
|
|
29
28
|
UUID,
|
|
30
29
|
AdvertisingData,
|
|
31
30
|
Appearance,
|
|
@@ -47,6 +46,8 @@ from bumble.hci import (
|
|
|
47
46
|
HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR,
|
|
48
47
|
Address,
|
|
49
48
|
Phy,
|
|
49
|
+
Role,
|
|
50
|
+
OwnAddressType,
|
|
50
51
|
)
|
|
51
52
|
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
|
52
53
|
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
|
@@ -114,11 +115,11 @@ SECONDARY_PHY_TO_BUMBLE_PHY_MAP: Dict[SecondaryPhy, Phy] = {
|
|
|
114
115
|
SECONDARY_CODED: Phy.LE_CODED,
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
OWN_ADDRESS_MAP: Dict[host_pb2.OwnAddressType,
|
|
118
|
-
host_pb2.PUBLIC:
|
|
119
|
-
host_pb2.RANDOM:
|
|
120
|
-
host_pb2.RESOLVABLE_OR_PUBLIC:
|
|
121
|
-
host_pb2.RESOLVABLE_OR_RANDOM:
|
|
118
|
+
OWN_ADDRESS_MAP: Dict[host_pb2.OwnAddressType, OwnAddressType] = {
|
|
119
|
+
host_pb2.PUBLIC: OwnAddressType.PUBLIC,
|
|
120
|
+
host_pb2.RANDOM: OwnAddressType.RANDOM,
|
|
121
|
+
host_pb2.RESOLVABLE_OR_PUBLIC: OwnAddressType.RESOLVABLE_OR_PUBLIC,
|
|
122
|
+
host_pb2.RESOLVABLE_OR_RANDOM: OwnAddressType.RESOLVABLE_OR_RANDOM,
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
|
|
@@ -184,7 +185,7 @@ class HostService(HostServicer):
|
|
|
184
185
|
|
|
185
186
|
try:
|
|
186
187
|
connection = await self.device.connect(
|
|
187
|
-
address, transport=
|
|
188
|
+
address, transport=PhysicalTransport.BR_EDR
|
|
188
189
|
)
|
|
189
190
|
except ConnectionError as e:
|
|
190
191
|
if e.error_code == HCI_PAGE_TIMEOUT_ERROR:
|
|
@@ -217,7 +218,7 @@ class HostService(HostServicer):
|
|
|
217
218
|
self.log.debug(f"WaitConnection from {address}...")
|
|
218
219
|
|
|
219
220
|
connection = self.device.find_connection_by_bd_addr(
|
|
220
|
-
address, transport=
|
|
221
|
+
address, transport=PhysicalTransport.BR_EDR
|
|
221
222
|
)
|
|
222
223
|
if connection and id(connection) in self.waited_connections:
|
|
223
224
|
# this connection was already returned: wait for a new one.
|
|
@@ -249,8 +250,8 @@ class HostService(HostServicer):
|
|
|
249
250
|
try:
|
|
250
251
|
connection = await self.device.connect(
|
|
251
252
|
address,
|
|
252
|
-
transport=
|
|
253
|
-
own_address_type=request.own_address_type,
|
|
253
|
+
transport=PhysicalTransport.LE,
|
|
254
|
+
own_address_type=OwnAddressType(request.own_address_type),
|
|
254
255
|
)
|
|
255
256
|
except ConnectionError as e:
|
|
256
257
|
if e.error_code == HCI_PAGE_TIMEOUT_ERROR:
|
|
@@ -377,8 +378,8 @@ class HostService(HostServicer):
|
|
|
377
378
|
|
|
378
379
|
def on_connection(connection: bumble.device.Connection) -> None:
|
|
379
380
|
if (
|
|
380
|
-
connection.transport ==
|
|
381
|
-
and connection.role ==
|
|
381
|
+
connection.transport == PhysicalTransport.LE
|
|
382
|
+
and connection.role == Role.PERIPHERAL
|
|
382
383
|
):
|
|
383
384
|
connections.put_nowait(connection)
|
|
384
385
|
|
|
@@ -495,8 +496,8 @@ class HostService(HostServicer):
|
|
|
495
496
|
|
|
496
497
|
def on_connection(connection: bumble.device.Connection) -> None:
|
|
497
498
|
if (
|
|
498
|
-
connection.transport ==
|
|
499
|
-
and connection.role ==
|
|
499
|
+
connection.transport == PhysicalTransport.LE
|
|
500
|
+
and connection.role == Role.PERIPHERAL
|
|
500
501
|
):
|
|
501
502
|
connections.put_nowait(connection)
|
|
502
503
|
|
|
@@ -509,7 +510,7 @@ class HostService(HostServicer):
|
|
|
509
510
|
await self.device.start_advertising(
|
|
510
511
|
target=target,
|
|
511
512
|
advertising_type=advertising_type,
|
|
512
|
-
own_address_type=request.own_address_type,
|
|
513
|
+
own_address_type=OwnAddressType(request.own_address_type),
|
|
513
514
|
)
|
|
514
515
|
|
|
515
516
|
if not request.connectable:
|
|
@@ -534,7 +535,9 @@ class HostService(HostServicer):
|
|
|
534
535
|
|
|
535
536
|
try:
|
|
536
537
|
self.log.debug('Stop advertising')
|
|
537
|
-
await
|
|
538
|
+
await bumble.utils.cancel_on_event(
|
|
539
|
+
self.device, 'flush', self.device.stop_advertising()
|
|
540
|
+
)
|
|
538
541
|
except:
|
|
539
542
|
pass
|
|
540
543
|
|
|
@@ -558,7 +561,7 @@ class HostService(HostServicer):
|
|
|
558
561
|
await self.device.start_scanning(
|
|
559
562
|
legacy=request.legacy,
|
|
560
563
|
active=not request.passive,
|
|
561
|
-
own_address_type=request.own_address_type,
|
|
564
|
+
own_address_type=OwnAddressType(request.own_address_type),
|
|
562
565
|
scan_interval=(
|
|
563
566
|
int(request.interval)
|
|
564
567
|
if request.interval
|
|
@@ -602,7 +605,9 @@ class HostService(HostServicer):
|
|
|
602
605
|
self.device.remove_listener('advertisement', handler) # type: ignore
|
|
603
606
|
try:
|
|
604
607
|
self.log.debug('Stop scanning')
|
|
605
|
-
await
|
|
608
|
+
await bumble.utils.cancel_on_event(
|
|
609
|
+
self.device, 'flush', self.device.stop_scanning()
|
|
610
|
+
)
|
|
606
611
|
except:
|
|
607
612
|
pass
|
|
608
613
|
|
|
@@ -642,7 +647,9 @@ class HostService(HostServicer):
|
|
|
642
647
|
self.device.remove_listener('inquiry_result', result_handler) # type: ignore
|
|
643
648
|
try:
|
|
644
649
|
self.log.debug('Stop inquiry')
|
|
645
|
-
await
|
|
650
|
+
await bumble.utils.cancel_on_event(
|
|
651
|
+
self.device, 'flush', self.device.stop_discovery()
|
|
652
|
+
)
|
|
646
653
|
except:
|
|
647
654
|
pass
|
|
648
655
|
|
bumble/pandora/l2cap.py
CHANGED
|
@@ -19,8 +19,8 @@ import logging
|
|
|
19
19
|
|
|
20
20
|
from asyncio import Queue as AsyncQueue, Future
|
|
21
21
|
|
|
22
|
-
from . import utils
|
|
23
|
-
from .config import Config
|
|
22
|
+
from bumble.pandora import utils
|
|
23
|
+
from bumble.pandora.config import Config
|
|
24
24
|
from bumble.core import OutOfResourcesError, InvalidArgumentError
|
|
25
25
|
from bumble.device import Device
|
|
26
26
|
from bumble.l2cap import (
|
bumble/pandora/security.py
CHANGED
|
@@ -18,18 +18,16 @@ import contextlib
|
|
|
18
18
|
import grpc
|
|
19
19
|
import logging
|
|
20
20
|
|
|
21
|
-
from . import utils
|
|
22
|
-
from .config import Config
|
|
21
|
+
from bumble.pandora import utils
|
|
22
|
+
from bumble.pandora.config import Config
|
|
23
23
|
from bumble import hci
|
|
24
24
|
from bumble.core import (
|
|
25
|
-
|
|
26
|
-
BT_LE_TRANSPORT,
|
|
27
|
-
BT_PERIPHERAL_ROLE,
|
|
25
|
+
PhysicalTransport,
|
|
28
26
|
ProtocolError,
|
|
29
27
|
)
|
|
28
|
+
import bumble.utils
|
|
30
29
|
from bumble.device import Connection as BumbleConnection, Device
|
|
31
|
-
from bumble.hci import HCI_Error
|
|
32
|
-
from bumble.utils import EventWatcher
|
|
30
|
+
from bumble.hci import HCI_Error, Role
|
|
33
31
|
from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
|
|
34
32
|
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
|
35
33
|
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
|
@@ -95,7 +93,7 @@ class PairingDelegate(BasePairingDelegate):
|
|
|
95
93
|
else:
|
|
96
94
|
# In BR/EDR, connection may not be complete,
|
|
97
95
|
# use address instead
|
|
98
|
-
assert self.connection.transport ==
|
|
96
|
+
assert self.connection.transport == PhysicalTransport.BR_EDR
|
|
99
97
|
ev.address = bytes(reversed(bytes(self.connection.peer_address)))
|
|
100
98
|
|
|
101
99
|
return ev
|
|
@@ -174,7 +172,7 @@ class PairingDelegate(BasePairingDelegate):
|
|
|
174
172
|
|
|
175
173
|
async def display_number(self, number: int, digits: int = 6) -> None:
|
|
176
174
|
if (
|
|
177
|
-
self.connection.transport ==
|
|
175
|
+
self.connection.transport == PhysicalTransport.BR_EDR
|
|
178
176
|
and self.io_capability == BasePairingDelegate.DISPLAY_OUTPUT_ONLY
|
|
179
177
|
):
|
|
180
178
|
return
|
|
@@ -287,7 +285,7 @@ class SecurityService(SecurityServicer):
|
|
|
287
285
|
|
|
288
286
|
oneof = request.WhichOneof('level')
|
|
289
287
|
level = getattr(request, oneof)
|
|
290
|
-
assert {
|
|
288
|
+
assert {PhysicalTransport.BR_EDR: 'classic', PhysicalTransport.LE: 'le'}[
|
|
291
289
|
connection.transport
|
|
292
290
|
] == oneof
|
|
293
291
|
|
|
@@ -302,7 +300,7 @@ class SecurityService(SecurityServicer):
|
|
|
302
300
|
|
|
303
301
|
security_result = asyncio.get_running_loop().create_future()
|
|
304
302
|
|
|
305
|
-
with contextlib.closing(EventWatcher()) as watcher:
|
|
303
|
+
with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
|
|
306
304
|
|
|
307
305
|
@watcher.on(connection, 'pairing')
|
|
308
306
|
def on_pairing(*_: Any) -> None:
|
|
@@ -317,8 +315,8 @@ class SecurityService(SecurityServicer):
|
|
|
317
315
|
security_result.set_result('connection_died')
|
|
318
316
|
|
|
319
317
|
if (
|
|
320
|
-
connection.transport ==
|
|
321
|
-
and connection.role ==
|
|
318
|
+
connection.transport == PhysicalTransport.LE
|
|
319
|
+
and connection.role == Role.PERIPHERAL
|
|
322
320
|
):
|
|
323
321
|
connection.request_pairing()
|
|
324
322
|
else:
|
|
@@ -379,7 +377,7 @@ class SecurityService(SecurityServicer):
|
|
|
379
377
|
|
|
380
378
|
assert request.level
|
|
381
379
|
level = request.level
|
|
382
|
-
assert {
|
|
380
|
+
assert {PhysicalTransport.BR_EDR: 'classic', PhysicalTransport.LE: 'le'}[
|
|
383
381
|
connection.transport
|
|
384
382
|
] == request.level_variant()
|
|
385
383
|
|
|
@@ -427,7 +425,7 @@ class SecurityService(SecurityServicer):
|
|
|
427
425
|
self.log.debug('Wait for security: done')
|
|
428
426
|
wait_for_security.set_result('success')
|
|
429
427
|
elif (
|
|
430
|
-
connection.transport ==
|
|
428
|
+
connection.transport == PhysicalTransport.BR_EDR
|
|
431
429
|
and self.need_authentication(connection, level)
|
|
432
430
|
):
|
|
433
431
|
nonlocal authenticate_task
|
|
@@ -451,7 +449,7 @@ class SecurityService(SecurityServicer):
|
|
|
451
449
|
'security_request': pair,
|
|
452
450
|
}
|
|
453
451
|
|
|
454
|
-
with contextlib.closing(EventWatcher()) as watcher:
|
|
452
|
+
with contextlib.closing(bumble.utils.EventWatcher()) as watcher:
|
|
455
453
|
# register event handlers
|
|
456
454
|
for event, listener in listeners.items():
|
|
457
455
|
watcher.on(connection, event, listener)
|
|
@@ -505,12 +503,12 @@ class SecurityService(SecurityServicer):
|
|
|
505
503
|
return BR_LEVEL_REACHED[level](connection)
|
|
506
504
|
|
|
507
505
|
def need_pairing(self, connection: BumbleConnection, level: int) -> bool:
|
|
508
|
-
if connection.transport ==
|
|
506
|
+
if connection.transport == PhysicalTransport.LE:
|
|
509
507
|
return level >= LE_LEVEL3 and not connection.authenticated
|
|
510
508
|
return False
|
|
511
509
|
|
|
512
510
|
def need_authentication(self, connection: BumbleConnection, level: int) -> bool:
|
|
513
|
-
if connection.transport ==
|
|
511
|
+
if connection.transport == PhysicalTransport.LE:
|
|
514
512
|
return False
|
|
515
513
|
if level == LEVEL2 and connection.encryption != 0:
|
|
516
514
|
return not connection.authenticated
|
|
@@ -518,7 +516,7 @@ class SecurityService(SecurityServicer):
|
|
|
518
516
|
|
|
519
517
|
def need_encryption(self, connection: BumbleConnection, level: int) -> bool:
|
|
520
518
|
# TODO(abel): need to support MITM
|
|
521
|
-
if connection.transport ==
|
|
519
|
+
if connection.transport == PhysicalTransport.LE:
|
|
522
520
|
return level == LE_LEVEL2 and not connection.encryption
|
|
523
521
|
return level >= LEVEL2 and not connection.encryption
|
|
524
522
|
|
bumble/pandora/utils.py
CHANGED
|
@@ -20,11 +20,11 @@ import inspect
|
|
|
20
20
|
import logging
|
|
21
21
|
|
|
22
22
|
from bumble.device import Device
|
|
23
|
-
from bumble.hci import Address
|
|
23
|
+
from bumble.hci import Address, AddressType
|
|
24
24
|
from google.protobuf.message import Message # pytype: disable=pyi-error
|
|
25
25
|
from typing import Any, Dict, Generator, MutableMapping, Optional, Tuple
|
|
26
26
|
|
|
27
|
-
ADDRESS_TYPES: Dict[str,
|
|
27
|
+
ADDRESS_TYPES: Dict[str, AddressType] = {
|
|
28
28
|
"public": Address.PUBLIC_DEVICE_ADDRESS,
|
|
29
29
|
"random": Address.RANDOM_DEVICE_ADDRESS,
|
|
30
30
|
"public_identity": Address.PUBLIC_IDENTITY_ADDRESS,
|
bumble/profiles/aics.py
CHANGED
|
@@ -48,7 +48,7 @@ from bumble.gatt_adapters import (
|
|
|
48
48
|
UTF8CharacteristicProxyAdapter,
|
|
49
49
|
)
|
|
50
50
|
from bumble.gatt_client import ProfileServiceProxy, ServiceProxy
|
|
51
|
-
from bumble
|
|
51
|
+
from bumble import utils
|
|
52
52
|
|
|
53
53
|
# -----------------------------------------------------------------------------
|
|
54
54
|
# Logging
|
|
@@ -64,7 +64,7 @@ GAIN_SETTINGS_MIN_VALUE = 0
|
|
|
64
64
|
GAIN_SETTINGS_MAX_VALUE = 255
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
class ErrorCode(OpenIntEnum):
|
|
67
|
+
class ErrorCode(utils.OpenIntEnum):
|
|
68
68
|
'''
|
|
69
69
|
Cf. 1.6 Application error codes
|
|
70
70
|
'''
|
|
@@ -76,7 +76,7 @@ class ErrorCode(OpenIntEnum):
|
|
|
76
76
|
GAIN_MODE_CHANGE_NOT_ALLOWED = 0x84
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
class Mute(OpenIntEnum):
|
|
79
|
+
class Mute(utils.OpenIntEnum):
|
|
80
80
|
'''
|
|
81
81
|
Cf. 2.2.1.2 Mute Field
|
|
82
82
|
'''
|
|
@@ -86,7 +86,7 @@ class Mute(OpenIntEnum):
|
|
|
86
86
|
DISABLED = 0x02
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
class GainMode(OpenIntEnum):
|
|
89
|
+
class GainMode(utils.OpenIntEnum):
|
|
90
90
|
'''
|
|
91
91
|
Cf. 2.2.1.3 Gain Mode
|
|
92
92
|
'''
|
|
@@ -97,7 +97,7 @@ class GainMode(OpenIntEnum):
|
|
|
97
97
|
AUTOMATIC = 0x03
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
class AudioInputStatus(OpenIntEnum):
|
|
100
|
+
class AudioInputStatus(utils.OpenIntEnum):
|
|
101
101
|
'''
|
|
102
102
|
Cf. 3.4 Audio Input Status
|
|
103
103
|
'''
|
|
@@ -106,7 +106,7 @@ class AudioInputStatus(OpenIntEnum):
|
|
|
106
106
|
ACTIVE = 0x01
|
|
107
107
|
|
|
108
108
|
|
|
109
|
-
class AudioInputControlPointOpCode(OpenIntEnum):
|
|
109
|
+
class AudioInputControlPointOpCode(utils.OpenIntEnum):
|
|
110
110
|
'''
|
|
111
111
|
Cf. 3.5.1 Audio Input Control Point procedure requirements
|
|
112
112
|
'''
|