Merge remote-tracking branch 'aosp/upstream-main' am: 5a897454ff am: cc61586906
Original change: https://android-review.googlesource.com/c/platform/external/python/bumble/+/3270792
Change-Id: Id7e0f8743b71640ac5006f9dbcf736d5414f994a
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/METADATA b/METADATA
index 2f4e87b..e4b9b79 100644
--- a/METADATA
+++ b/METADATA
@@ -11,7 +11,7 @@
type: GIT
value: "https://github.com/google/bumble"
}
- version: "cd9feeb4551f478691e33450219adf6d58c4871f"
- last_upgrade_date { year: 2024 month: 9 day: 12 }
+ version: "737abdc481b226b16d85174d9ae0ebd9346b0fb4"
+ last_upgrade_date { year: 2024 month: 9 day: 17 }
license_type: NOTICE
}
diff --git a/apps/pair.py b/apps/pair.py
index c1ea332..67eec90 100644
--- a/apps/pair.py
+++ b/apps/pair.py
@@ -46,6 +46,12 @@
ATT_INSUFFICIENT_AUTHENTICATION_ERROR,
ATT_INSUFFICIENT_ENCRYPTION_ERROR,
)
+from bumble.utils import AsyncRunner
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+POST_PAIRING_DELAY = 1
# -----------------------------------------------------------------------------
@@ -235,8 +241,10 @@
# Listen for pairing events
connection.on('pairing_start', on_pairing_start)
- connection.on('pairing', lambda keys: on_pairing(connection.peer_address, keys))
- connection.on('pairing_failure', on_pairing_failure)
+ connection.on('pairing', lambda keys: on_pairing(connection, keys))
+ connection.on(
+ 'pairing_failure', lambda reason: on_pairing_failure(connection, reason)
+ )
# Listen for encryption changes
connection.on(
@@ -270,19 +278,24 @@
# -----------------------------------------------------------------------------
-def on_pairing(address, keys):
[email protected]_in_task()
+async def on_pairing(connection, keys):
print(color('***-----------------------------------', 'cyan'))
- print(color(f'*** Paired! (peer identity={address})', 'cyan'))
+ print(color(f'*** Paired! (peer identity={connection.peer_address})', 'cyan'))
keys.print(prefix=color('*** ', 'cyan'))
print(color('***-----------------------------------', 'cyan'))
+ await asyncio.sleep(POST_PAIRING_DELAY)
+ await connection.disconnect()
Waiter.instance.terminate()
# -----------------------------------------------------------------------------
-def on_pairing_failure(reason):
[email protected]_in_task()
+async def on_pairing_failure(connection, reason):
print(color('***-----------------------------------', 'red'))
print(color(f'*** Pairing failed: {smp_error_name(reason)}', 'red'))
print(color('***-----------------------------------', 'red'))
+ await connection.disconnect()
Waiter.instance.terminate()
@@ -293,6 +306,7 @@
mitm,
bond,
ctkd,
+ identity_address,
linger,
io,
oob,
@@ -382,11 +396,18 @@
oob_contexts = None
# Set up a pairing config factory
+ if identity_address == 'public':
+ identity_address_type = PairingConfig.AddressType.PUBLIC
+ elif identity_address == 'random':
+ identity_address_type = PairingConfig.AddressType.RANDOM
+ else:
+ identity_address_type = None
device.pairing_config_factory = lambda connection: PairingConfig(
sc=sc,
mitm=mitm,
bonding=bond,
oob=oob_contexts,
+ identity_address_type=identity_address_type,
delegate=Delegate(mode, connection, io, prompt),
)
@@ -457,6 +478,10 @@
help='Enable CTKD',
show_default=True,
)
[email protected](
+ '--identity-address',
+ type=click.Choice(['random', 'public']),
+)
@click.option('--linger', default=False, is_flag=True, help='Linger after pairing')
@click.option(
'--io',
@@ -493,6 +518,7 @@
mitm,
bond,
ctkd,
+ identity_address,
linger,
io,
oob,
@@ -518,6 +544,7 @@
mitm,
bond,
ctkd,
+ identity_address,
linger,
io,
oob,
diff --git a/bumble/att.py b/bumble/att.py
index 86d7fc6..6eed040 100644
--- a/bumble/att.py
+++ b/bumble/att.py
@@ -23,7 +23,6 @@
# Imports
# -----------------------------------------------------------------------------
from __future__ import annotations
-from bumble.utils import OpenIntEnum
import enum
import functools
@@ -213,15 +212,6 @@
# pylint: disable=invalid-name
-class CommonErrorCode(OpenIntEnum):
- '''See Supplement to the Bluetooth Code Specification 1.2 List of Error Codes.'''
-
- WRITE_REQUEST_REJECTED = 0xFC
- CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_IMPROPERLY_CONFIGURED = 0xFD
- PROCEDURE_ALREADY_IN_PROGRESS = 0xFE
- OUT_OF_RANGE = 0xFF
-
-
# -----------------------------------------------------------------------------
# Exceptions
# -----------------------------------------------------------------------------
diff --git a/bumble/hci.py b/bumble/hci.py
index af39976..1d0cd8e 100644
--- a/bumble/hci.py
+++ b/bumble/hci.py
@@ -267,6 +267,19 @@
HCI_LE_PERIODIC_ADVERTISING_SUBEVENT_DATA_REQUEST_EVENT = 0X27
HCI_LE_PERIODIC_ADVERTISING_RESPONSE_REPORT_EVENT = 0X28
HCI_LE_ENHANCED_CONNECTION_COMPLETE_V2_EVENT = 0X29
+HCI_LE_READ_ALL_REMOTE_FEATURES_COMPLETE_EVENT = 0x2A
+HCI_LE_CIS_ESTABLISHED_V2_EVENT = 0x2B
+HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE_EVENT = 0x2C
+HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE_EVENT = 0x2D
+HCI_LE_CS_SECURITY_ENABLE_COMPLETE_EVENT = 0x2E
+HCI_LE_CS_CONFIG_COMPLETE_EVENT = 0x2F
+HCI_LE_CS_PROCEDURE_ENABLE_EVENT = 0x30
+HCI_LE_CS_SUBEVENT_RESULT_EVENT = 0x31
+HCI_LE_CS_SUBEVENT_RESULT_CONTINUE_EVENT = 0x32
+HCI_LE_CS_TEST_END_COMPLETE_EVENT = 0x33
+HCI_LE_MONITORED_ADVERTISERS_REPORT_EVENT = 0x34
+HCI_LE_FRAME_SPACE_UPDATE_EVENT = 0x35
+
# HCI Command
@@ -573,11 +586,36 @@
HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D)
HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E)
HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x007F)
+HCI_LE_SET_DECISION_DATA_COMMAND = hci_command_op_code(0x08, 0x0080)
+HCI_LE_SET_DECISION_INSTRUCTIONS_COMMAND = hci_command_op_code(0x08, 0x0081)
HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND = hci_command_op_code(0x08, 0x0082)
HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0083)
HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND = hci_command_op_code(0x08, 0x0084)
HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND = hci_command_op_code(0x08, 0x0085)
HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND = hci_command_op_code(0x08, 0x0086)
+HCI_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0087)
+HCI_LE_READ_ALL_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0088)
+HCI_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x0089)
+HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMMAND = hci_command_op_code(0x08, 0x008A)
+HCI_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES = hci_command_op_code(0x08, 0x008B)
+HCI_LE_CS_SECURITY_ENABLE_COMMAND = hci_command_op_code(0x08, 0x008C)
+HCI_LE_CS_SET_DEFAULT_SETTINGS_COMMAND = hci_command_op_code(0x08, 0x008D)
+HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008E)
+HCI_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE_COMMAND = hci_command_op_code(0x08, 0x008F)
+HCI_LE_CS_CREATE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0090)
+HCI_LE_CS_REMOVE_CONFIG_COMMAND = hci_command_op_code(0x08, 0x0091)
+HCI_LE_CS_SET_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0092)
+HCI_LE_CS_SET_PROCEDURE_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0093)
+HCI_LE_CS_PROCEDURE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0094)
+HCI_LE_CS_TEST_COMMAND = hci_command_op_code(0x08, 0x0095)
+HCI_LE_CS_TEST_END_COMMAND = hci_command_op_code(0x08, 0x0096)
+HCI_LE_SET_HOST_FEATURE_V2_COMMAND = hci_command_op_code(0x08, 0x0097)
+HCI_LE_ADD_DEVICE_TO_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0098)
+HCI_LE_REMOVE_DEVICE_FROM_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x0099)
+HCI_LE_CLEAR_MONITORED_ADVERTISERS_LIST_COMMAND = hci_command_op_code(0x08, 0x009A)
+HCI_LE_READ_MONITORED_ADVERTISERS_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x009B)
+HCI_LE_ENABLE_MONITORING_ADVERTISERS_COMMAND = hci_command_op_code(0x08, 0x009C)
+HCI_LE_FRAME_SPACE_UPDATE_COMMAND = hci_command_op_code(0x08, 0x009D)
# HCI Error Codes
@@ -1150,8 +1188,16 @@
CHANNEL_CLASSIFICATION = 39
ADVERTISING_CODING_SELECTION = 40
ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 41
+ DECISION_BASED_ADVERTISING_FILTERING = 42
PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 43
PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 44
+ UNSEGMENTED_FRAMED_MODE = 45
+ CHANNEL_SOUNDING = 46
+ CHANNEL_SOUNDING_HOST_SUPPORT = 47
+ CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 48
+ LL_EXTENDED_FEATURE_SET = 63
+ MONITORING_ADVERTISERS = 64
+ FRAME_SPACE_UPDATE = 65
class LeFeatureMask(enum.IntFlag):
LE_ENCRYPTION = 1 << LeFeature.LE_ENCRYPTION
@@ -1196,8 +1242,16 @@
CHANNEL_CLASSIFICATION = 1 << LeFeature.CHANNEL_CLASSIFICATION
ADVERTISING_CODING_SELECTION = 1 << LeFeature.ADVERTISING_CODING_SELECTION
ADVERTISING_CODING_SELECTION_HOST_SUPPORT = 1 << LeFeature.ADVERTISING_CODING_SELECTION_HOST_SUPPORT
+ DECISION_BASED_ADVERTISING_FILTERING = 1 << LeFeature.DECISION_BASED_ADVERTISING_FILTERING
PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_ADVERTISER
PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER = 1 << LeFeature.PERIODIC_ADVERTISING_WITH_RESPONSES_SCANNER
+ UNSEGMENTED_FRAMED_MODE = 1 << LeFeature.UNSEGMENTED_FRAMED_MODE
+ CHANNEL_SOUNDING = 1 << LeFeature.CHANNEL_SOUNDING
+ CHANNEL_SOUNDING_HOST_SUPPORT = 1 << LeFeature.CHANNEL_SOUNDING_HOST_SUPPORT
+ CHANNEL_SOUNDING_TONE_QUALITY_INDICATION = 1 << LeFeature.CHANNEL_SOUNDING_TONE_QUALITY_INDICATION
+ LL_EXTENDED_FEATURE_SET = 1 << LeFeature.LL_EXTENDED_FEATURE_SET
+ MONITORING_ADVERTISERS = 1 << LeFeature.MONITORING_ADVERTISERS
+ FRAME_SPACE_UPDATE = 1 << LeFeature.FRAME_SPACE_UPDATE
class LmpFeature(enum.IntEnum):
# Page 0 (Legacy LMP features)
@@ -1565,12 +1619,16 @@
# This is an array field, starting with a 1-byte item count.
item_count = data[offset]
offset += 1
+ # Set fields first, because item_count might be 0.
+ for sub_field_name, _ in field:
+ result[sub_field_name] = []
+
for _ in range(item_count):
for sub_field_name, sub_field_type in field:
value, size = HCI_Object.parse_field(
data, offset, sub_field_type
)
- result.setdefault(sub_field_name, []).append(value)
+ result[sub_field_name].append(value)
offset += size
continue
@@ -2986,6 +3044,27 @@
@HCI_Command.command(
return_parameters_fields=[
('status', STATUS_SPEC),
+ ('authentication_enable', 1),
+ ]
+)
+class HCI_Read_Authentication_Enable_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.3.23 Read Authentication Enable Command
+ '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command([('authentication_enable', 1)])
+class HCI_Write_Authentication_Enable_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.3.24 Write Authentication Enable Command
+ '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
('class_of_device', {'size': 3, 'mapper': map_class_of_device}),
]
)
@@ -3022,7 +3101,12 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('synchronous_flow_control_enable', 1),
+ ]
+)
class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command):
'''
See Bluetooth spec @ 7.3.36 Read Synchronous Flow Control Enable Command
@@ -3191,7 +3275,13 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('le_supported_host', 1),
+ ('unused', 1),
+ ]
+)
class HCI_Read_LE_Host_Support_Command(HCI_Command):
'''
See Bluetooth spec @ 7.3.78 Read LE Host Support Command
@@ -3324,7 +3414,13 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ("status", STATUS_SPEC),
+ [("standard_codec_ids", 1)],
+ [("vendor_specific_codec_ids", 4)],
+ ]
+)
class HCI_Read_Local_Supported_Codecs_Command(HCI_Command):
'''
See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command
@@ -3333,6 +3429,26 @@
# -----------------------------------------------------------------------------
@HCI_Command.command(
+ return_parameters_fields=[
+ ("status", STATUS_SPEC),
+ [("standard_codec_ids", 1), ("standard_codec_transports", 1)],
+ [("vendor_specific_codec_ids", 4), ("vendor_specific_codec_transports", 1)],
+ ]
+)
+class HCI_Read_Local_Supported_Codecs_V2_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.4.8 Read Local Supported Codecs Command
+ '''
+
+ class Transport(OpenIntEnum):
+ BR_EDR_ACL = 0x00
+ BR_EDR_SCO = 0x01
+ LE_CIS = 0x02
+ LE_BIS = 0x03
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
fields=[('handle', 2)],
return_parameters_fields=[('status', STATUS_SPEC), ('handle', 2), ('rssi', -1)],
)
@@ -3488,7 +3604,12 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('tx_power_level', 1),
+ ]
+)
class HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command(HCI_Command):
'''
See Bluetooth spec @ 7.8.6 LE Read Advertising Physical Channel Tx Power Command
@@ -3612,7 +3733,12 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('filter_accept_list_size', 1),
+ ]
+)
class HCI_LE_Read_Filter_Accept_List_Size_Command(HCI_Command):
'''
See Bluetooth spec @ 7.8.14 LE Read Filter Accept List Size Command
@@ -3723,7 +3849,12 @@
# -----------------------------------------------------------------------------
-@HCI_Command.command()
+@HCI_Command.command(
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('le_states', 8),
+ ]
+)
class HCI_LE_Read_Supported_States_Command(HCI_Command):
'''
See Bluetooth spec @ 7.8.27 LE Read Supported States Command
@@ -4701,6 +4832,102 @@
# -----------------------------------------------------------------------------
@HCI_Command.command(
fields=[
+ ('big_handle', 1),
+ ('advertising_handle', 1),
+ ('num_bis', 1),
+ ('sdu_interval', 3),
+ ('max_sdu', 2),
+ ('max_transport_latency', 2),
+ ('rtn', 1),
+ ('phy', 1),
+ ('packing', 1),
+ ('framing', 1),
+ ('encryption', 1),
+ ('broadcast_code', 16),
+ ],
+)
+class HCI_LE_Create_BIG_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.8.103 LE Create BIG command
+ '''
+
+ big_handle: int
+ advertising_handle: int
+ num_bis: int
+ sdu_interval: int
+ max_sdu: int
+ max_transport_latency: int
+ rtn: int
+ phy: int
+ packing: int
+ framing: int
+ encryption: int
+ broadcast_code: int
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+ fields=[
+ ('big_handle', 1),
+ ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}),
+ ],
+)
+class HCI_LE_Terminate_BIG_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.8.105 LE Terminate BIG command
+ '''
+
+ big_handle: int
+ reason: int
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+ fields=[
+ ('big_handle', 1),
+ ('sync_handle', 2),
+ ('encryption', 1),
+ ('broadcast_code', 16),
+ ('mse', 1),
+ ('big_sync_timeout', 2),
+ [('bis', 1)],
+ ],
+)
+class HCI_LE_BIG_Create_Sync_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.8.106 LE BIG Create Sync command
+ '''
+
+ big_handle: int
+ sync_handle: int
+ encryption: int
+ broadcast_code: int
+ mse: int
+ big_sync_timeout: int
+ bis: List[int]
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+ fields=[
+ ('big_handle', 1),
+ ],
+ return_parameters_fields=[
+ ('status', STATUS_SPEC),
+ ('big_handle', 2),
+ ],
+)
+class HCI_LE_BIG_Terminate_Sync_Command(HCI_Command):
+ '''
+ See Bluetooth spec @ 7.8.107. LE BIG Terminate Sync command
+ '''
+
+ big_handle: int
+
+
+# -----------------------------------------------------------------------------
+@HCI_Command.command(
+ fields=[
('connection_handle', 2),
('data_path_direction', 1),
('data_path_id', 1),
@@ -5538,6 +5765,27 @@
[
('status', STATUS_SPEC),
('connection_handle', 2),
+ ('service_data', 2),
+ ('sync_handle', 2),
+ ('advertising_sid', 1),
+ ('advertiser_address_type', Address.ADDRESS_TYPE_SPEC),
+ ('advertiser_address', Address.parse_address_preceded_by_type),
+ ('advertiser_phy', 1),
+ ('periodic_advertising_interval', 2),
+ ('advertiser_clock_accuracy', 1),
+ ]
+)
+class HCI_LE_Periodic_Advertising_Sync_Transfer_Received_Event(HCI_LE_Meta_Event):
+ '''
+ See Bluetooth spec @ 7.7.65.24 LE Periodic Advertising Sync Transfer Received Event
+ '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_LE_Meta_Event.event(
+ [
+ ('status', STATUS_SPEC),
+ ('connection_handle', 2),
('cig_sync_delay', 3),
('cis_sync_delay', 3),
('transport_latency_c_to_p', 3),
@@ -6228,6 +6476,23 @@
# -----------------------------------------------------------------------------
@HCI_Event.event(
[
+ ('status', STATUS_SPEC),
+ ('connection_handle', 2),
+ ('max_tx_latency', 2),
+ ('max_rx_latency', 2),
+ ('min_remote_timeout', 2),
+ ('min_local_timeout', 2),
+ ]
+)
+class HCI_Sniff_Subrating_Event(HCI_Event):
+ '''
+ See Bluetooth spec @ 7.7.37 Sniff Subrating Event
+ '''
+
+
+# -----------------------------------------------------------------------------
+@HCI_Event.event(
+ [
('num_responses', 1),
('bd_addr', Address.parse_address),
('page_scan_repetition_mode', 1),
diff --git a/bumble/profiles/aics.py b/bumble/profiles/aics.py
index 8b7468f..3a69627 100644
--- a/bumble/profiles/aics.py
+++ b/bumble/profiles/aics.py
@@ -442,14 +442,15 @@
)
super().__init__(
- [
+ characteristics=[
self.audio_input_state_characteristic, # type: ignore
self.gain_settings_properties_characteristic, # type: ignore
self.audio_input_type_characteristic, # type: ignore
self.audio_input_status_characteristic, # type: ignore
self.audio_input_control_point_characteristic, # type: ignore
self.audio_input_description_characteristic, # type: ignore
- ]
+ ],
+ primary=False,
)
diff --git a/bumble/profiles/hap.py b/bumble/profiles/hap.py
index e61ac4f..1ef055c 100644
--- a/bumble/profiles/hap.py
+++ b/bumble/profiles/hap.py
@@ -19,7 +19,6 @@
import asyncio
import functools
from bumble import att, gatt, gatt_client
-from bumble.att import CommonErrorCode
from bumble.core import InvalidArgumentError, InvalidStateError
from bumble.device import Device, Connection
from bumble.utils import AsyncRunner, OpenIntEnum
@@ -352,16 +351,16 @@
logging.warning(f'HAS require MTU >= 49: {connection}')
if self.read_presets_request_in_progress:
- raise att.ATT_Error(CommonErrorCode.PROCEDURE_ALREADY_IN_PROGRESS)
+ raise att.ATT_Error(att.ErrorCode.PROCEDURE_ALREADY_IN_PROGRESS)
self.read_presets_request_in_progress = True
start_index = value[1]
if start_index == 0x00:
- raise att.ATT_Error(CommonErrorCode.OUT_OF_RANGE)
+ raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
num_presets = value[2]
if num_presets == 0x00:
- raise att.ATT_Error(CommonErrorCode.OUT_OF_RANGE)
+ raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
# Sending `num_presets` presets ordered by increasing index field, starting from start_index
presets = [
@@ -371,7 +370,7 @@
]
del presets[num_presets:]
if len(presets) == 0:
- raise att.ATT_Error(CommonErrorCode.OUT_OF_RANGE)
+ raise att.ATT_Error(att.ErrorCode.OUT_OF_RANGE)
AsyncRunner.spawn(self._read_preset_response(connection, presets))
@@ -468,7 +467,7 @@
assert connection
if self.read_presets_request_in_progress:
- raise att.ATT_Error(CommonErrorCode.PROCEDURE_ALREADY_IN_PROGRESS)
+ raise att.ATT_Error(att.ErrorCode.PROCEDURE_ALREADY_IN_PROGRESS)
index = value[1]
preset = self.preset_records.get(index, None)
diff --git a/bumble/profiles/vcp.py b/bumble/profiles/vcp.py
index 0788219..57452d9 100644
--- a/bumble/profiles/vcp.py
+++ b/bumble/profiles/vcp.py
@@ -24,7 +24,7 @@
from bumble import gatt
from bumble import gatt_client
-from typing import Optional
+from typing import Optional, Sequence
# -----------------------------------------------------------------------------
# Constants
@@ -88,6 +88,7 @@
muted: int = 0,
change_counter: int = 0,
volume_flags: int = 0,
+ included_services: Sequence[gatt.Service] = (),
) -> None:
self.step_size = step_size
self.volume_setting = volume_setting
@@ -117,11 +118,12 @@
)
super().__init__(
- [
+ characteristics=[
self.volume_state,
self.volume_control_point,
self.volume_flags,
- ]
+ ],
+ included_services=list(included_services),
)
@property
diff --git a/tests/aics_test.py b/tests/aics_test.py
index 8b47298..9526558 100644
--- a/tests/aics_test.py
+++ b/tests/aics_test.py
@@ -30,10 +30,9 @@
GainMode,
AudioInputStatus,
AudioInputControlPointOpCode,
- GAIN_SETTINGS_MAX_VALUE,
- GAIN_SETTINGS_MIN_VALUE,
ErrorCode,
)
+from bumble.profiles.vcp import VolumeControlService, VolumeControlServiceProxy
from .test_utils import TwoDevices
@@ -42,23 +41,34 @@
# Tests
# -----------------------------------------------------------------------------
aics_service = AICSService()
+vcp_service = VolumeControlService(
+ volume_setting=32, muted=1, volume_flags=1, included_services=[aics_service]
+)
@pytest_asyncio.fixture
async def aics_client():
devices = TwoDevices()
- devices[0].add_service(aics_service)
+ devices[0].add_service(vcp_service)
await devices.setup_connection()
- assert devices.connections[0] is not None
- assert devices.connections[1] is not None
+ assert devices.connections[0]
+ assert devices.connections[1]
devices.connections[0].encryption = 1
devices.connections[1].encryption = 1
peer = device.Peer(devices.connections[1])
- aics_client = await peer.discover_service_and_create_proxy(AICSServiceProxy)
+
+ vcp_client = await peer.discover_service_and_create_proxy(VolumeControlServiceProxy)
+
+ assert vcp_client
+ included_services = await peer.discover_included_services(vcp_client.service_proxy)
+ assert included_services
+ aics_service_discovered = included_services[0]
+ await peer.discover_characteristics(service=aics_service_discovered)
+ aics_client = AICSServiceProxy(aics_service_discovered)
yield aics_client
diff --git a/tests/hci_test.py b/tests/hci_test.py
index 72f4022..1b69cda 100644
--- a/tests/hci_test.py
+++ b/tests/hci_test.py
@@ -60,6 +60,8 @@
HCI_Number_Of_Completed_Packets_Event,
HCI_Packet,
HCI_PIN_Code_Request_Reply_Command,
+ HCI_Read_Local_Supported_Codecs_Command,
+ HCI_Read_Local_Supported_Codecs_V2_Command,
HCI_Read_Local_Supported_Commands_Command,
HCI_Read_Local_Supported_Features_Command,
HCI_Read_Local_Version_Information_Command,
@@ -477,6 +479,51 @@
# -----------------------------------------------------------------------------
+def test_HCI_Read_Local_Supported_Codecs_Command_Complete():
+ returned_parameters = (
+ HCI_Read_Local_Supported_Codecs_Command.parse_return_parameters(
+ bytes([HCI_SUCCESS, 3, CodecID.A_LOG, CodecID.CVSD, CodecID.LINEAR_PCM, 0])
+ )
+ )
+ assert returned_parameters.standard_codec_ids == [
+ CodecID.A_LOG,
+ CodecID.CVSD,
+ CodecID.LINEAR_PCM,
+ ]
+
+
+# -----------------------------------------------------------------------------
+def test_HCI_Read_Local_Supported_Codecs_V2_Command_Complete():
+ returned_parameters = (
+ HCI_Read_Local_Supported_Codecs_V2_Command.parse_return_parameters(
+ bytes(
+ [
+ HCI_SUCCESS,
+ 3,
+ CodecID.A_LOG,
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_ACL,
+ CodecID.CVSD,
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_SCO,
+ CodecID.LINEAR_PCM,
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.LE_CIS,
+ 0,
+ ]
+ )
+ )
+ )
+ assert returned_parameters.standard_codec_ids == [
+ CodecID.A_LOG,
+ CodecID.CVSD,
+ CodecID.LINEAR_PCM,
+ ]
+ assert returned_parameters.standard_codec_transports == [
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_ACL,
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.BR_EDR_SCO,
+ HCI_Read_Local_Supported_Codecs_V2_Command.Transport.LE_CIS,
+ ]
+
+
+# -----------------------------------------------------------------------------
def test_address():
a = Address('C4:F2:17:1A:1D:BB')
assert not a.is_public
diff --git a/tests/vcp_test.py b/tests/vcp_test.py
index d45a5f5..5853ed9 100644
--- a/tests/vcp_test.py
+++ b/tests/vcp_test.py
@@ -39,6 +39,9 @@
await devices.setup_connection()
+ assert devices.connections[0]
+ assert devices.connections[1]
+
# Mock encryption.
devices.connections[0].encryption = 1
devices.connections[1].encryption = 1