rust/bt-gatt: Refactor CharacteristicProperties to be zero-allocation Use a static slice of CharacteristicProperty instead of Vec to avoid allocation. Update all callsites to use this. While here, define convenience types for common sets of properties. Test: ./presubmit.sh Change-Id: Ic6eb4d3e6413d7d9b3d03cf7a461c93e7a275cf7 Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/3760
diff --git a/rust/bt-ascs/src/client.rs b/rust/bt-ascs/src/client.rs index ce1e1e8..89cfefc 100644 --- a/rust/bt-ascs/src/client.rs +++ b/rust/bt-ascs/src/client.rs
@@ -662,7 +662,7 @@ use bt_common::core::CodecId; use bt_common::generic_audio::metadata_ltv::Metadata; use bt_gatt::test_utils::{FakePeerService, FakeTypes}; - use bt_gatt::types::{AttributePermissions, CharacteristicProperty}; + use bt_gatt::types::{AttributePermissions, CharacteristicProperties, CharacteristicProperty}; use bt_gatt::Characteristic; const CONTROL_POINT_HANDLE: Handle = Handle(1); @@ -676,9 +676,7 @@ Characteristic { handle: CONTROL_POINT_HANDLE, uuid: ASE_CONTROL_POINT_UUID, - properties: CharacteristicProperty::Write - | CharacteristicProperty::WriteWithoutResponse - | CharacteristicProperty::Notify, + properties: CharacteristicProperties::WRITE_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -689,7 +687,7 @@ Characteristic { handle: SINK_ASE_HANDLE, uuid: SINK_ASE_UUID, - properties: CharacteristicProperty::Read | CharacteristicProperty::Notify, + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -700,7 +698,7 @@ Characteristic { handle: SOURCE_ASE_HANDLE, uuid: SOURCE_ASE_UUID, - properties: CharacteristicProperty::Read | CharacteristicProperty::Notify, + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -750,7 +748,7 @@ Characteristic { handle: SINK_ASE_HANDLE, uuid: SINK_ASE_UUID, - properties: CharacteristicProperty::Read | CharacteristicProperty::Notify, + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -773,7 +771,8 @@ Characteristic { handle: CONTROL_POINT_HANDLE, uuid: ASE_CONTROL_POINT_UUID, - properties: CharacteristicProperty::Write | CharacteristicProperty::Notify, + properties: (&[CharacteristicProperty::Write, CharacteristicProperty::Notify]) + .into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -796,7 +795,8 @@ Characteristic { handle: CONTROL_POINT_HANDLE, uuid: ASE_CONTROL_POINT_UUID, - properties: CharacteristicProperty::Write | CharacteristicProperty::Notify, + properties: (&[CharacteristicProperty::Write, CharacteristicProperty::Notify]) + .into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -807,7 +807,8 @@ Characteristic { handle: Handle(4), uuid: ASE_CONTROL_POINT_UUID, - properties: CharacteristicProperty::Write | CharacteristicProperty::Notify, + properties: (&[CharacteristicProperty::Write, CharacteristicProperty::Notify]) + .into(), permissions: AttributePermissions::default(), descriptors: vec![], },
diff --git a/rust/bt-ascs/src/server.rs b/rust/bt-ascs/src/server.rs index 3ee9823..6c4ae42 100644 --- a/rust/bt-ascs/src/server.rs +++ b/rust/bt-ascs/src/server.rs
@@ -8,7 +8,7 @@ use bt_gatt::server::{LocalService, Server, ServiceDefinition, ServiceId}; use bt_gatt::server::{ReadResponder, WriteResponder}; use bt_gatt::types::{ - AttributePermissions, CharacteristicProperty, GattError, Handle, SecurityLevels, + AttributePermissions, CharacteristicProperties, GattError, Handle, SecurityLevels, }; use bt_gatt::Characteristic; @@ -281,9 +281,7 @@ } fn build_control_point() -> Characteristic { - let properties = CharacteristicProperty::Write - | CharacteristicProperty::WriteWithoutResponse - | CharacteristicProperty::Notify; + let properties = CharacteristicProperties::WRITE_NOTIFY; let permissions = AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required()); Characteristic { @@ -398,7 +396,7 @@ impl From<&AudioStreamEndpoint> for Characteristic { fn from(value: &AudioStreamEndpoint) -> Self { - let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify; + let properties = CharacteristicProperties::READ_NOTIFY; let permissions = AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required()); Characteristic {
diff --git a/rust/bt-bass/src/client.rs b/rust/bt-bass/src/client.rs index 1a1122f..aa29368 100644 --- a/rust/bt-bass/src/client.rs +++ b/rust/bt-bass/src/client.rs
@@ -428,10 +428,7 @@ Characteristic { handle: RECEIVE_STATE_1_HANDLE, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -441,10 +438,7 @@ Characteristic { handle: RECEIVE_STATE_2_HANDLE, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -454,10 +448,7 @@ Characteristic { handle: RECEIVE_STATE_3_HANDLE, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -467,7 +458,7 @@ Characteristic { handle: RANDOME_CHAR_HANDLE, uuid: Uuid::from_u16(0x1234), - properties: CharacteristicProperties(vec![CharacteristicProperty::Notify]), + properties: CharacteristicProperty::Notify.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -477,7 +468,7 @@ Characteristic { handle: AUDIO_SCAN_CONTROL_POINT_HANDLE, uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -517,10 +508,7 @@ Characteristic { handle: Handle(1), uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -543,7 +531,7 @@ Characteristic { handle: Handle(1), uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -568,10 +556,7 @@ Characteristic { handle: Handle(1), uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -581,7 +566,7 @@ Characteristic { handle: Handle(2), uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -591,7 +576,7 @@ Characteristic { handle: Handle(3), uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -620,10 +605,7 @@ Characteristic { handle: RECEIVE_STATE_2_HANDLE, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -662,10 +644,7 @@ Characteristic { handle: RECEIVE_STATE_3_HANDLE, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], },
diff --git a/rust/bt-bass/src/server.rs b/rust/bt-bass/src/server.rs index 29a20b8..cbffdad 100644 --- a/rust/bt-bass/src/server.rs +++ b/rust/bt-bass/src/server.rs
@@ -12,7 +12,8 @@ WriteResponder, }; use bt_gatt::types::{ - AttributePermissions, CharacteristicProperty, GattError, Handle, SecurityLevels, ServiceKind, + AttributePermissions, CharacteristicProperties, CharacteristicProperty, GattError, Handle, + SecurityLevels, ServiceKind, }; use bt_gatt::Characteristic; use futures::stream::Stream; @@ -176,12 +177,12 @@ /// Constructs the Broadcast Audio Scan Control Point characteristic. /// Defined in BASS v1.0 Section 3.1. fn build_control_point() -> Characteristic { - let cp_properties = - CharacteristicProperty::Write | CharacteristicProperty::WriteWithoutResponse; + let cp_properties: CharacteristicProperties = + (&[CharacteristicProperty::Write, CharacteristicProperty::WriteWithoutResponse]).into(); Characteristic { handle: CONTROL_POINT_HANDLE, uuid: BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: cp_properties.clone(), + properties: cp_properties, permissions: AttributePermissions::with_levels( &cp_properties, &SecurityLevels::encryption_required(), @@ -193,11 +194,11 @@ /// Constructs the Broadcast Receive State characteristic. /// Defined in BASS v1.0 Section 3.2. fn build_receive_state(handle: Handle) -> Characteristic { - let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify; + let properties = CharacteristicProperties::READ_NOTIFY; Characteristic { handle, uuid: BROADCAST_RECEIVE_STATE_UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(),
diff --git a/rust/bt-battery/src/monitor/client.rs b/rust/bt-battery/src/monitor/client.rs index 0ac3ae3..29e20e4 100644 --- a/rust/bt-battery/src/monitor/client.rs +++ b/rust/bt-battery/src/monitor/client.rs
@@ -203,7 +203,8 @@ use bt_common::Uuid; use bt_gatt::test_utils::{FakeClient, FakePeerService, FakeTypes}; use bt_gatt::types::{ - AttributePermissions, Characteristic, CharacteristicProperties, GattError, + AttributePermissions, Characteristic, CharacteristicProperties, CharacteristicProperty, + GattError, }; use futures::{pin_mut, FutureExt}; @@ -214,10 +215,7 @@ Characteristic { handle: BATTERY_LEVEL_HANDLE, uuid: BATTERY_LEVEL_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Read, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -257,9 +255,7 @@ Characteristic { handle: BATTERY_LEVEL_HANDLE, uuid: BATTERY_LEVEL_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Read, // Only read is mandatory - ]), + properties: CharacteristicProperty::Read.into(), // Only read is mandatory permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -301,10 +297,7 @@ Characteristic { handle: BATTERY_LEVEL_HANDLE, uuid: Uuid::from_u16(0x1234), // Random UUID, not Battery Level - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Read, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -329,10 +322,7 @@ Characteristic { handle: BATTERY_LEVEL_HANDLE, uuid: BATTERY_LEVEL_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Read, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], },
diff --git a/rust/bt-broadcast-assistant/src/assistant/peer.rs b/rust/bt-broadcast-assistant/src/assistant/peer.rs index c4a4d10..8587e38 100644 --- a/rust/bt-broadcast-assistant/src/assistant/peer.rs +++ b/rust/bt-broadcast-assistant/src/assistant/peer.rs
@@ -227,10 +227,7 @@ Characteristic { handle: RECEIVE_STATE_HANDLE, uuid: bt_bass::types::BROADCAST_RECEIVE_STATE_UUID, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -240,7 +237,7 @@ Characteristic { handle: AUDIO_SCAN_CONTROL_POINT_HANDLE, uuid: bt_bass::types::BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], },
diff --git a/rust/bt-csis/src/client.rs b/rust/bt-csis/src/client.rs index f897c3f..38c1e86 100644 --- a/rust/bt-csis/src/client.rs +++ b/rust/bt-csis/src/client.rs
@@ -195,9 +195,7 @@ use super::*; use assert_matches::assert_matches; use bt_gatt::test_utils::*; - use bt_gatt::types::{ - AttributePermissions, CharacteristicProperties, CharacteristicProperty, Handle, - }; + use bt_gatt::types::{AttributePermissions, CharacteristicProperty, Handle}; use bt_gatt::Characteristic; use core::num::NonZeroU8; use futures::{ @@ -216,7 +214,7 @@ Characteristic { handle, uuid, - properties: CharacteristicProperties(vec![CharacteristicProperty::Read]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], },
diff --git a/rust/bt-gatt/src/tests.rs b/rust/bt-gatt/src/tests.rs index 8b95ec1..78412ed 100644 --- a/rust/bt-gatt/src/tests.rs +++ b/rust/bt-gatt/src/tests.rs
@@ -26,10 +26,7 @@ Characteristic { handle: Handle(1), uuid: TEST_UUID_1, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -39,10 +36,7 @@ Characteristic { handle: Handle(2), uuid: TEST_UUID_1, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -52,10 +46,7 @@ Characteristic { handle: Handle(3), uuid: TEST_UUID_1, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -65,7 +56,7 @@ Characteristic { handle: Handle(4), uuid: TEST_UUID_2, - properties: CharacteristicProperties(vec![CharacteristicProperty::Notify]), + properties: CharacteristicProperty::Notify.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -75,7 +66,7 @@ Characteristic { handle: Handle(5), uuid: TEST_UUID_3, - properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]), + properties: CharacteristicProperty::Broadcast.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -131,10 +122,7 @@ Characteristic { handle: Handle(1), uuid: TEST_UUID_1, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -244,10 +232,7 @@ Characteristic { handle: Handle(1), uuid: TEST_UUID_1, - properties: CharacteristicProperties(vec![ - CharacteristicProperty::Broadcast, - CharacteristicProperty::Notify, - ]), + properties: CharacteristicProperties::BROADCAST_NOTIFY, permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -368,9 +353,12 @@ def.add_characteristic(Characteristic { handle: Handle(3), uuid: TEST_UUID_3, - properties: CharacteristicProperty::Read - | CharacteristicProperty::Write - | CharacteristicProperty::Notify, + properties: (&[ + CharacteristicProperty::Read, + CharacteristicProperty::Write, + CharacteristicProperty::Notify, + ]) + .into(), permissions: AttributePermissions { read: Some(SecurityLevels::default()), write: Some(SecurityLevels::default()), @@ -383,7 +371,7 @@ def.add_characteristic(Characteristic { handle: Handle(4), uuid: TEST_UUID_3, - properties: CharacteristicProperty::Read | CharacteristicProperty::Indicate, + properties: (&[CharacteristicProperty::Read, CharacteristicProperty::Indicate]).into(), permissions: AttributePermissions { read: Some(SecurityLevels::default()), write: None,
diff --git a/rust/bt-gatt/src/types.rs b/rust/bt-gatt/src/types.rs index e8beec7..a4a9514 100644 --- a/rust/bt-gatt/src/types.rs +++ b/rust/bt-gatt/src/types.rs
@@ -230,65 +230,94 @@ WritableAuxiliaries = 0x200, } -impl CharacteristicProperty { +/// Set of [`CharacteristicProperty`] flags associated with a GATT +/// Characteristic. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CharacteristicProperties(&'static [CharacteristicProperty]); + +impl CharacteristicProperties { /// Characteristic is readable and can send notifications. - pub const READ_NOTIFY: &'static [Self] = &[Self::Read, Self::Notify]; + pub const READ_NOTIFY: Self = + Self(&[CharacteristicProperty::Read, CharacteristicProperty::Notify]); + + /// Characteristic can be broadcast and send notifications. + pub const BROADCAST_NOTIFY: Self = + Self(&[CharacteristicProperty::Broadcast, CharacteristicProperty::Notify]); /// Control Point property set: writable (with and without response) and can /// send notifications. - pub const WRITE_NOTIFY: &'static [Self] = - &[Self::Write, Self::WriteWithoutResponse, Self::Notify]; + pub const WRITE_NOTIFY: Self = Self(&[ + CharacteristicProperty::Write, + CharacteristicProperty::WriteWithoutResponse, + CharacteristicProperty::Notify, + ]); /// Readable, writable (with and without response), and can send /// notifications. - pub const READ_WRITE_NOTIFY: &'static [Self] = - &[Self::Read, Self::Write, Self::WriteWithoutResponse, Self::Notify]; -} + pub const READ_WRITE_NOTIFY: Self = Self(&[ + CharacteristicProperty::Read, + CharacteristicProperty::Write, + CharacteristicProperty::WriteWithoutResponse, + CharacteristicProperty::Notify, + ]); -impl std::ops::BitOr for CharacteristicProperty { - type Output = CharacteristicProperties; + /// Characteristic can send notifications or indications. + pub const NOTIFY_INDICATE: Self = + Self(&[CharacteristicProperty::Notify, CharacteristicProperty::Indicate]); - fn bitor(self, rhs: Self) -> Self::Output { - CharacteristicProperties(vec![self, rhs]) + pub fn contains(&self, property: CharacteristicProperty) -> bool { + self.0.contains(&property) } -} -#[derive(Default, Debug, Clone)] -pub struct CharacteristicProperties(pub Vec<CharacteristicProperty>); - -impl From<CharacteristicProperty> for CharacteristicProperties { - fn from(value: CharacteristicProperty) -> Self { - Self(vec![value]) - } -} - -impl FromIterator<CharacteristicProperty> for CharacteristicProperties { - fn from_iter<T: IntoIterator<Item = CharacteristicProperty>>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } -} - -impl std::ops::BitOr<CharacteristicProperty> for CharacteristicProperties { - type Output = Self; - - fn bitor(mut self, rhs: CharacteristicProperty) -> Self::Output { - self.0.push(rhs); - self - } -} - -impl CharacteristicProperties { pub fn is_disjoint(&self, other: &Self) -> bool { for property in self.0.iter() { - if other.0.contains(&property) { + if other.0.contains(property) { return false; } } true } +} - pub fn contains(&self, property: CharacteristicProperty) -> bool { - self.0.contains(&property) +impl std::ops::Deref for CharacteristicProperties { + type Target = [CharacteristicProperty]; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl From<CharacteristicProperty> for CharacteristicProperties { + fn from(value: CharacteristicProperty) -> Self { + match value { + CharacteristicProperty::Broadcast => Self(&[CharacteristicProperty::Broadcast]), + CharacteristicProperty::Read => Self(&[CharacteristicProperty::Read]), + CharacteristicProperty::WriteWithoutResponse => { + Self(&[CharacteristicProperty::WriteWithoutResponse]) + } + CharacteristicProperty::Write => Self(&[CharacteristicProperty::Write]), + CharacteristicProperty::Notify => Self(&[CharacteristicProperty::Notify]), + CharacteristicProperty::Indicate => Self(&[CharacteristicProperty::Indicate]), + CharacteristicProperty::AuthenticatedSignedWrites => { + Self(&[CharacteristicProperty::AuthenticatedSignedWrites]) + } + CharacteristicProperty::ReliableWrite => Self(&[CharacteristicProperty::ReliableWrite]), + CharacteristicProperty::WritableAuxiliaries => { + Self(&[CharacteristicProperty::WritableAuxiliaries]) + } + } + } +} + +impl From<&'static [CharacteristicProperty]> for CharacteristicProperties { + fn from(value: &'static [CharacteristicProperty]) -> Self { + Self(value) + } +} + +impl<const N: usize> From<&'static [CharacteristicProperty; N]> for CharacteristicProperties { + fn from(value: &'static [CharacteristicProperty; N]) -> Self { + Self(value) } } @@ -329,11 +358,11 @@ impl AttributePermissions { pub fn with_levels(properties: &CharacteristicProperties, levels: &SecurityLevels) -> Self { - let notify_properties = CharacteristicProperty::Notify | CharacteristicProperty::Indicate; Self { read: properties.contains(CharacteristicProperty::Read).then_some(levels.clone()), write: properties.contains(CharacteristicProperty::Write).then_some(levels.clone()), - update: (!properties.is_disjoint(¬ify_properties)).then_some(levels.clone()), + update: (!properties.is_disjoint(&CharacteristicProperties::NOTIFY_INDICATE)) + .then_some(levels.clone()), } } } @@ -405,7 +434,7 @@ } pub fn supports_property(&self, property: &CharacteristicProperty) -> bool { - self.properties.0.contains(property) + self.properties.contains(*property) } pub fn descriptors(&self) -> impl Iterator<Item = &Descriptor> {
diff --git a/rust/bt-mcs/src/server.rs b/rust/bt-mcs/src/server.rs index f4b43d0..5abb3fa 100644 --- a/rust/bt-mcs/src/server.rs +++ b/rust/bt-mcs/src/server.rs
@@ -46,91 +46,91 @@ /// Handle assigned to the Content Control ID (CCID) characteristic. const CONTENT_CONTROL_ID_HANDLE: Handle = Handle(13); -/// Specification of a mandatory GATT characteristic for MCS. -struct CharacteristicSpec { - handle: Handle, - uuid: Uuid, - properties: &'static [CharacteristicProperty], +/// All 13 mandatory characteristics defined in MCS v1.0.1 Section 3. +fn mandatory_characteristics() -> [Characteristic; 13] { + [ + build_mandatory_characteristic( + MEDIA_PLAYER_NAME_HANDLE, + MEDIA_PLAYER_NAME_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + TRACK_CHANGED_HANDLE, + TRACK_CHANGED_UUID, + CharacteristicProperty::Notify, + ), + build_mandatory_characteristic( + TRACK_TITLE_HANDLE, + TRACK_TITLE_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + TRACK_DURATION_HANDLE, + TRACK_DURATION_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + TRACK_POSITION_HANDLE, + TRACK_POSITION_UUID, + CharacteristicProperties::READ_WRITE_NOTIFY, + ), + build_mandatory_characteristic( + PLAYBACK_SPEED_HANDLE, + PLAYBACK_SPEED_UUID, + CharacteristicProperties::READ_WRITE_NOTIFY, + ), + build_mandatory_characteristic( + SEEKING_SPEED_HANDLE, + SEEKING_SPEED_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + PLAYING_ORDER_HANDLE, + PLAYING_ORDER_UUID, + CharacteristicProperties::READ_WRITE_NOTIFY, + ), + build_mandatory_characteristic( + PLAYING_ORDERS_SUPPORTED_HANDLE, + PLAYING_ORDERS_SUPPORTED_UUID, + CharacteristicProperty::Read, + ), + build_mandatory_characteristic( + MEDIA_STATE_HANDLE, + MEDIA_STATE_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + MEDIA_CONTROL_POINT_HANDLE, + MEDIA_CONTROL_POINT_UUID, + CharacteristicProperties::WRITE_NOTIFY, + ), + build_mandatory_characteristic( + MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_HANDLE, + MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UUID, + CharacteristicProperties::READ_NOTIFY, + ), + build_mandatory_characteristic( + CONTENT_CONTROL_ID_HANDLE, + CONTENT_CONTROL_ID_UUID, + CharacteristicProperty::Read, + ), + ] } -/// All the mandatory characteristics defined in MCS v1.0.1 Section 3. -const MANDATORY_CHARACTERISTICS: [CharacteristicSpec; 13] = [ - CharacteristicSpec { - handle: MEDIA_PLAYER_NAME_HANDLE, - uuid: MEDIA_PLAYER_NAME_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: TRACK_CHANGED_HANDLE, - uuid: TRACK_CHANGED_UUID, - properties: &[CharacteristicProperty::Notify], - }, - CharacteristicSpec { - handle: TRACK_TITLE_HANDLE, - uuid: TRACK_TITLE_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: TRACK_DURATION_HANDLE, - uuid: TRACK_DURATION_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: TRACK_POSITION_HANDLE, - uuid: TRACK_POSITION_UUID, - properties: CharacteristicProperty::READ_WRITE_NOTIFY, - }, - CharacteristicSpec { - handle: PLAYBACK_SPEED_HANDLE, - uuid: PLAYBACK_SPEED_UUID, - properties: CharacteristicProperty::READ_WRITE_NOTIFY, - }, - CharacteristicSpec { - handle: SEEKING_SPEED_HANDLE, - uuid: SEEKING_SPEED_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: PLAYING_ORDER_HANDLE, - uuid: PLAYING_ORDER_UUID, - properties: CharacteristicProperty::READ_WRITE_NOTIFY, - }, - CharacteristicSpec { - handle: PLAYING_ORDERS_SUPPORTED_HANDLE, - uuid: PLAYING_ORDERS_SUPPORTED_UUID, - properties: &[CharacteristicProperty::Read], - }, - CharacteristicSpec { - handle: MEDIA_STATE_HANDLE, - uuid: MEDIA_STATE_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: MEDIA_CONTROL_POINT_HANDLE, - uuid: MEDIA_CONTROL_POINT_UUID, - properties: CharacteristicProperty::WRITE_NOTIFY, - }, - CharacteristicSpec { - handle: MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_HANDLE, - uuid: MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_UUID, - properties: CharacteristicProperty::READ_NOTIFY, - }, - CharacteristicSpec { - handle: CONTENT_CONTROL_ID_HANDLE, - uuid: CONTENT_CONTROL_ID_UUID, - properties: &[CharacteristicProperty::Read], - }, -]; - +/// Specification of a mandatory GATT characteristic for MCS. /// Constructs a characteristic definition with the specified handle, UUID, /// properties, and encryption-required permissions conforming to MCS v1.0.1 /// Section 3. -fn build_mandatory_characteristic(spec: &CharacteristicSpec) -> Characteristic { - let properties: CharacteristicProperties = spec.properties.iter().copied().collect(); +fn build_mandatory_characteristic( + handle: Handle, + uuid: Uuid, + properties: impl Into<CharacteristicProperties>, +) -> Characteristic { + let properties = properties.into(); Characteristic { - handle: spec.handle, - uuid: spec.uuid, - properties: properties.clone(), + handle, + uuid, + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -179,8 +179,8 @@ ServiceKind::Primary, ); - for spec in &MANDATORY_CHARACTERISTICS { - service_def.add_characteristic(build_mandatory_characteristic(spec))?; + for chrc in mandatory_characteristics() { + service_def.add_characteristic(chrc)?; } Ok(service_def) @@ -222,31 +222,26 @@ builder.build_service_definition().expect("service definition builds successfully"); let characteristics: Vec<&Characteristic> = service_def.characteristics().collect(); - assert_eq!(characteristics.len(), MANDATORY_CHARACTERISTICS.len()); + let expected_mandatory = mandatory_characteristics(); + assert_eq!(characteristics.len(), expected_mandatory.len()); - for (i, spec) in MANDATORY_CHARACTERISTICS.iter().enumerate() { + for (i, expected) in expected_mandatory.iter().enumerate() { let chrc = characteristics[i]; - assert_eq!(chrc.handle, spec.handle); - assert_eq!(chrc.uuid, spec.uuid); - assert_eq!(chrc.properties.0.as_slice(), spec.properties); + assert_eq!(chrc.handle, expected.handle); + assert_eq!(chrc.uuid, expected.uuid); + assert_eq!(chrc.properties, expected.properties); - let expected_properties: CharacteristicProperties = - spec.properties.iter().copied().collect(); - let expected_permissions = AttributePermissions::with_levels( - &expected_properties, - &SecurityLevels::encryption_required(), - ); assert_eq!( chrc.permissions.read.map(|s| s.encryption), - expected_permissions.read.map(|s| s.encryption) + expected.permissions.read.map(|s| s.encryption) ); assert_eq!( chrc.permissions.write.map(|s| s.encryption), - expected_permissions.write.map(|s| s.encryption) + expected.permissions.write.map(|s| s.encryption) ); assert_eq!( chrc.permissions.update.map(|s| s.encryption), - expected_permissions.update.map(|s| s.encryption) + expected.permissions.update.map(|s| s.encryption) ); } }
diff --git a/rust/bt-pacs/src/lib.rs b/rust/bt-pacs/src/lib.rs index 7d65629..d27ad26 100644 --- a/rust/bt-pacs/src/lib.rs +++ b/rust/bt-pacs/src/lib.rs
@@ -482,7 +482,7 @@ Uuid, }; use bt_gatt::{ - types::{AttributePermissions, Handle}, + types::{AttributePermissions, CharacteristicProperty, Handle}, Characteristic, }; @@ -537,9 +537,7 @@ Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x2BC9), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -571,9 +569,7 @@ Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x2BC9), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -605,9 +601,7 @@ Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x2BC9), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -658,9 +652,7 @@ Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x28CD), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], }, @@ -680,9 +672,7 @@ let chr = Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x28CD), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], }; @@ -705,9 +695,7 @@ let chr = Characteristic { handle: Handle(1), uuid: Uuid::from_u16(0x28CE), - properties: bt_gatt::types::CharacteristicProperties(vec![ - bt_gatt::types::CharacteristicProperty::Read, - ]), + properties: CharacteristicProperty::Read.into(), permissions: AttributePermissions::default(), descriptors: vec![], };
diff --git a/rust/bt-pacs/src/server/types.rs b/rust/bt-pacs/src/server/types.rs index 70096af..1bc9afb 100644 --- a/rust/bt-pacs/src/server/types.rs +++ b/rust/bt-pacs/src/server/types.rs
@@ -21,12 +21,12 @@ impl From<&SupportedAudioContexts> for Characteristic { fn from(_value: &SupportedAudioContexts) -> Self { // TODO(b/309015071): implement optional properties. - let properties: CharacteristicProperties = CharacteristicProperty::Read.into(); + let properties = CharacteristicProperty::Read.into(); Characteristic { handle: SUPPORTED_AUDIO_CONTEXTS_HANDLE, uuid: <SupportedAudioContexts as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -38,12 +38,12 @@ impl From<&AvailableAudioContexts> for Characteristic { fn from(_value: &AvailableAudioContexts) -> Self { - let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify; + let properties = CharacteristicProperties::READ_NOTIFY; Characteristic { handle: AVAILABLE_AUDIO_CONTEXTS_HANDLE, uuid: <AvailableAudioContexts as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -56,12 +56,12 @@ impl From<&SourcePac> for Characteristic { fn from(value: &SourcePac) -> Self { // TODO(b/309015071): implement optional properties. - let properties: CharacteristicProperties = CharacteristicProperty::Read.into(); + let properties = CharacteristicProperty::Read.into(); Characteristic { handle: value.handle, uuid: <SourcePac as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -74,12 +74,12 @@ impl From<&SinkPac> for Characteristic { fn from(value: &SinkPac) -> Self { // TODO(b/309015071): implement optional properties. - let properties: CharacteristicProperties = CharacteristicProperty::Read.into(); + let properties = CharacteristicProperty::Read.into(); Characteristic { handle: value.handle, uuid: <SinkPac as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -92,12 +92,12 @@ impl From<&SourceAudioLocations> for Characteristic { fn from(value: &SourceAudioLocations) -> Self { // TODO(b/309015071): implement optional properties. - let properties: CharacteristicProperties = CharacteristicProperty::Read.into(); + let properties = CharacteristicProperty::Read.into(); Characteristic { handle: value.handle, uuid: <SourceAudioLocations as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(), @@ -110,12 +110,12 @@ impl From<&SinkAudioLocations> for Characteristic { fn from(value: &SinkAudioLocations) -> Self { // TODO(b/309015071): implement optional properties. - let properties: CharacteristicProperties = CharacteristicProperty::Read.into(); + let properties = CharacteristicProperty::Read.into(); Characteristic { handle: value.handle, uuid: <SinkAudioLocations as FromCharacteristic>::UUID, - properties: properties.clone(), + properties, permissions: AttributePermissions::with_levels( &properties, &SecurityLevels::encryption_required(),
diff --git a/rust/bt-vcs/src/lib.rs b/rust/bt-vcs/src/lib.rs index 12532e2..7300b3c 100644 --- a/rust/bt-vcs/src/lib.rs +++ b/rust/bt-vcs/src/lib.rs
@@ -438,7 +438,7 @@ Characteristic { handle: STATE_HANDLE, uuid: VolumeState::UUID, - properties: CharacteristicProperty::Read | CharacteristicProperty::Notify, + properties: CharacteristicProperties::READ_NOTIFY, permissions: AttributePermissions { read: Some(SecurityLevels::default()), update: Some(SecurityLevels::default()),