Skip to main content

trouble_host/
attribute.rs

1//! Attribute protocol implementation.
2use core::cell::RefCell;
3use core::fmt;
4use core::marker::PhantomData;
5
6use bt_hci::uuid::declarations::{CHARACTERISTIC, INCLUDE, PRIMARY_SERVICE, SECONDARY_SERVICE};
7use bt_hci::uuid::descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION;
8use embassy_sync::blocking_mutex::raw::RawMutex;
9use embassy_sync::blocking_mutex::Mutex;
10use embassy_time::with_timeout;
11use heapless::Vec;
12
13use crate::att::{AttErrorCode, AttUns};
14use crate::attribute_server::AttributeServer;
15use crate::cursor::{ReadCursor, WriteCursor};
16use crate::gatt::ATT_TRANSACTION_TIMEOUT;
17use crate::prelude::{AsGatt, FixedGattValue, FromGatt, GattConnection, SecurityLevel};
18use crate::types::gatt_traits::FromGattError;
19pub use crate::types::uuid::Uuid;
20use crate::{gatt, Error, PacketPool, MAX_INVALID_DATA_LEN};
21
22/// The maximum size in bytes of an attribute using inline small data storage.
23pub const MAX_SMALL_DATA_SIZE: usize = 20;
24
25/// Characteristic properties
26#[derive(Debug, Clone, Copy)]
27#[repr(u8)]
28pub enum CharacteristicProp {
29    /// Broadcast
30    Broadcast = 0x01,
31    /// Read
32    Read = 0x02,
33    /// Write without response
34    WriteWithoutResponse = 0x04,
35    /// Write
36    Write = 0x08,
37    /// Notify
38    Notify = 0x10,
39    /// Indicate
40    Indicate = 0x20,
41    /// Authenticated writes
42    AuthenticatedWrite = 0x40,
43    /// Extended properties
44    Extended = 0x80,
45}
46
47/// Attribute permissions
48#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum PermissionLevel {
51    #[default]
52    /// Operation is allowed with no encryption or authentication
53    Allowed,
54    /// Encryption is required
55    EncryptionRequired,
56    /// Encryption and authentication are required
57    AuthenticationRequired,
58    /// Operation is not allowed
59    NotAllowed,
60}
61
62/// Attribute permissions
63#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65pub struct AttPermissions {
66    /// Security required for read operations
67    pub read: PermissionLevel,
68    /// Security required for write operations
69    pub write: PermissionLevel,
70    /// Minimum encryption key length required (0 = no minimum)
71    #[cfg(feature = "legacy-pairing")]
72    pub min_key_len: u8,
73}
74
75impl AttPermissions {
76    pub(crate) fn read_only() -> Self {
77        Self {
78            read: PermissionLevel::Allowed,
79            write: PermissionLevel::NotAllowed,
80            #[cfg(feature = "legacy-pairing")]
81            min_key_len: 0,
82        }
83    }
84
85    pub(crate) fn can_read(
86        &self,
87        level: SecurityLevel,
88        #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
89    ) -> Result<(), AttErrorCode> {
90        match self.read {
91            PermissionLevel::NotAllowed => Err(AttErrorCode::READ_NOT_PERMITTED),
92            PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
93                if level < SecurityLevel::Encrypted =>
94            {
95                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
96            }
97            PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
98                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
99            }
100            _ => {
101                #[cfg(feature = "legacy-pairing")]
102                if self.min_key_len > 0 && level.encrypted() && encryption_key_len < self.min_key_len {
103                    return Err(AttErrorCode::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
104                }
105                Ok(())
106            }
107        }
108    }
109
110    pub(crate) fn can_write(
111        &self,
112        level: SecurityLevel,
113        #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
114    ) -> Result<(), AttErrorCode> {
115        match self.write {
116            PermissionLevel::NotAllowed => Err(AttErrorCode::WRITE_NOT_PERMITTED),
117            PermissionLevel::EncryptionRequired | PermissionLevel::AuthenticationRequired
118                if level < SecurityLevel::Encrypted =>
119            {
120                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
121            }
122            PermissionLevel::AuthenticationRequired if level < SecurityLevel::EncryptedAuthenticated => {
123                Err(AttErrorCode::INSUFFICIENT_AUTHENTICATION)
124            }
125            _ => {
126                #[cfg(feature = "legacy-pairing")]
127                if self.min_key_len > 0 && level.encrypted() && encryption_key_len < self.min_key_len {
128                    return Err(AttErrorCode::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
129                }
130                Ok(())
131            }
132        }
133    }
134}
135
136/// A GATT characteristic declaration value.
137pub(crate) struct CharacteristicDeclaration {
138    pub props: CharacteristicProps,
139    pub value_handle: u16,
140    pub uuid: Uuid,
141}
142
143impl CharacteristicDeclaration {
144    pub(crate) fn new<U: Into<Uuid>, P: Into<CharacteristicProps>>(props: P, value_handle: u16, uuid: U) -> Self {
145        Self {
146            props: props.into(),
147            value_handle,
148            uuid: uuid.into(),
149        }
150    }
151}
152
153impl TryFrom<&[u8]> for CharacteristicDeclaration {
154    type Error = Error;
155
156    fn try_from(data: &'_ [u8]) -> Result<Self, Self::Error> {
157        let mut r = ReadCursor::new(data);
158        Ok(CharacteristicDeclaration {
159            props: CharacteristicProps(r.read()?),
160            value_handle: r.read()?,
161            uuid: Uuid::try_from(r.remaining())?,
162        })
163    }
164}
165
166/// Attribute metadata.
167pub struct Attribute<'a> {
168    pub(crate) uuid: Uuid,
169    pub(crate) permissions: AttPermissions,
170    pub(crate) data: AttributeData<'a>,
171}
172
173impl<'a> Attribute<'a> {
174    const EMPTY: Option<Attribute<'a>> = None;
175
176    pub(crate) fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
177        self.data.read(offset, data)
178    }
179
180    pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
181        self.data.write(offset, data)
182    }
183
184    pub(crate) fn permissions(&self) -> AttPermissions {
185        self.permissions
186    }
187
188    pub(crate) fn readable(&self) -> bool {
189        self.permissions.read != PermissionLevel::NotAllowed
190    }
191
192    pub(crate) fn writable(&self) -> bool {
193        self.permissions.write != PermissionLevel::NotAllowed
194    }
195}
196
197#[derive(Debug, PartialEq, Eq)]
198pub(crate) enum AttributeData<'d> {
199    ReadOnlyData {
200        value: &'d [u8],
201    },
202    Data {
203        variable_len: bool,
204        len: u16,
205        value: &'d mut [u8],
206    },
207    SmallData {
208        variable_len: bool,
209        capacity: u8,
210        len: u8,
211        value: [u8; MAX_SMALL_DATA_SIZE],
212    },
213    ClientSpecific {
214        variable_len: bool,
215        capacity: u16,
216    },
217}
218
219impl From<Uuid> for AttributeData<'_> {
220    fn from(uuid: Uuid) -> Self {
221        let raw = uuid.as_raw();
222        let len = raw.len() as u8;
223        let mut value = [0u8; MAX_SMALL_DATA_SIZE];
224        value[..raw.len()].copy_from_slice(raw);
225        AttributeData::SmallData {
226            variable_len: false,
227            capacity: len,
228            len,
229            value,
230        }
231    }
232}
233
234impl From<bt_hci::uuid::BluetoothUuid16> for AttributeData<'_> {
235    fn from(uuid: bt_hci::uuid::BluetoothUuid16) -> Self {
236        let raw = uuid.to_le_bytes();
237        let len = raw.len() as u8;
238        let mut value = [0u8; MAX_SMALL_DATA_SIZE];
239        value[..raw.len()].copy_from_slice(&raw);
240        AttributeData::SmallData {
241            variable_len: false,
242            capacity: len,
243            len,
244            value,
245        }
246    }
247}
248
249impl From<bt_hci::uuid::BluetoothUuid128> for AttributeData<'_> {
250    fn from(uuid: bt_hci::uuid::BluetoothUuid128) -> Self {
251        let raw = uuid.to_le_bytes();
252        let len = raw.len() as u8;
253        let mut value = [0u8; MAX_SMALL_DATA_SIZE];
254        value[..raw.len()].copy_from_slice(&raw);
255        AttributeData::SmallData {
256            variable_len: false,
257            capacity: len,
258            len,
259            value,
260        }
261    }
262}
263
264impl From<CharacteristicDeclaration> for AttributeData<'_> {
265    fn from(decl: CharacteristicDeclaration) -> Self {
266        let uuid_raw = decl.uuid.as_raw();
267        let total_len = 1 + 2 + uuid_raw.len(); // props + handle + uuid
268        debug_assert!(total_len <= MAX_SMALL_DATA_SIZE);
269        let mut value = [0u8; MAX_SMALL_DATA_SIZE];
270        value[0] = decl.props.0;
271        value[1..3].copy_from_slice(&decl.value_handle.to_le_bytes());
272        value[3..3 + uuid_raw.len()].copy_from_slice(uuid_raw);
273        let len = total_len as u8;
274        AttributeData::SmallData {
275            variable_len: false,
276            capacity: len,
277            len,
278            value,
279        }
280    }
281}
282
283impl AttributeData<'_> {
284    /// Get the byte value of the attribute data.
285    pub(crate) fn value(&self) -> Option<&[u8]> {
286        match self {
287            AttributeData::ReadOnlyData { value } => Some(value),
288            AttributeData::Data { len, value, .. } => Some(&value[..*len as usize]),
289            AttributeData::SmallData { len, value, .. } => Some(&value[..*len as usize]),
290            AttributeData::ClientSpecific { .. } => None,
291        }
292    }
293
294    pub(crate) fn is_variable_len(&self) -> bool {
295        match self {
296            AttributeData::Data { variable_len, .. }
297            | AttributeData::SmallData { variable_len, .. }
298            | AttributeData::ClientSpecific { variable_len, .. } => *variable_len,
299            _ => false,
300        }
301    }
302
303    pub(crate) fn capacity(&self) -> usize {
304        match self {
305            AttributeData::ReadOnlyData { value, .. } => value.len(),
306            AttributeData::Data { value, .. } => value.len(),
307            AttributeData::SmallData { capacity, .. } => usize::from(*capacity),
308            AttributeData::ClientSpecific { capacity, .. } => usize::from(*capacity),
309        }
310    }
311
312    pub(crate) fn read(&self, mut offset: usize, mut data: &mut [u8]) -> Result<usize, AttErrorCode> {
313        fn append(src: &[u8], offset: &mut usize, dest: &mut &mut [u8]) -> usize {
314            if *offset >= src.len() {
315                *offset -= src.len();
316                0
317            } else {
318                let d = core::mem::take(dest);
319                let n = d.len().min(src.len() - *offset);
320                d[..n].copy_from_slice(&src[*offset..][..n]);
321                *dest = &mut d[n..];
322                *offset = 0;
323                n
324            }
325        }
326
327        let written = match self {
328            Self::ReadOnlyData { value } => append(value, &mut offset, &mut data),
329            Self::Data { len, value, .. } => {
330                let value = &value[..*len as usize];
331                append(value, &mut offset, &mut data)
332            }
333            Self::SmallData { len, value, .. } => {
334                let value = &value[..*len as usize];
335                append(value, &mut offset, &mut data)
336            }
337            Self::ClientSpecific { .. } => return Err(AttErrorCode::UNLIKELY_ERROR),
338        };
339
340        if offset > 0 {
341            Err(AttErrorCode::INVALID_OFFSET)
342        } else {
343            Ok(written)
344        }
345    }
346
347    pub(crate) fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
348        match self {
349            Self::Data {
350                value,
351                variable_len,
352                len,
353            } => {
354                if offset > usize::from(*len) {
355                    Err(AttErrorCode::INVALID_OFFSET)
356                } else if offset + data.len() > value.len() {
357                    Err(AttErrorCode::INVALID_ATTRIBUTE_VALUE_LENGTH)
358                } else {
359                    value[offset..offset + data.len()].copy_from_slice(data);
360                    if *variable_len {
361                        *len = (offset + data.len()) as u16;
362                    }
363                    Ok(())
364                }
365            }
366            Self::SmallData {
367                variable_len,
368                capacity,
369                len,
370                value,
371            } => {
372                if offset > usize::from(*len) {
373                    Err(AttErrorCode::INVALID_OFFSET)
374                } else if offset + data.len() > usize::from(*capacity) {
375                    Err(AttErrorCode::INVALID_ATTRIBUTE_VALUE_LENGTH)
376                } else {
377                    value[offset..offset + data.len()].copy_from_slice(data);
378                    if *variable_len {
379                        *len = (offset + data.len()) as u8;
380                    }
381                    Ok(())
382                }
383            }
384            _ => Err(AttErrorCode::WRITE_NOT_PERMITTED),
385        }
386    }
387}
388
389impl fmt::Debug for Attribute<'_> {
390    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391        f.debug_struct("Attribute")
392            .field("uuid", &self.uuid)
393            .field("readable", &self.readable())
394            .field("writable", &self.writable())
395            .finish()
396    }
397}
398
399#[cfg(feature = "defmt")]
400impl<'a> defmt::Format for Attribute<'a> {
401    fn format(&self, fmt: defmt::Formatter) {
402        defmt::write!(fmt, "{}", defmt::Debug2Format(self))
403    }
404}
405
406impl<'a> Attribute<'a> {
407    pub(crate) fn new<U: Into<Uuid>, T: Into<AttributeData<'a>>>(
408        uuid: U,
409        permissions: AttPermissions,
410        data: T,
411    ) -> Attribute<'a> {
412        Attribute {
413            uuid: uuid.into(),
414            permissions,
415            data: data.into(),
416        }
417    }
418}
419
420/// A table of attributes.
421pub struct AttributeTable<'d, M: RawMutex, const MAX: usize> {
422    inner: Mutex<M, RefCell<InnerTable<'d, MAX>>>,
423}
424
425pub(crate) struct InnerTable<'d, const MAX: usize> {
426    attributes: Vec<Attribute<'d>, MAX>,
427}
428
429impl<'d, const MAX: usize> InnerTable<'d, MAX> {
430    fn push(&mut self, attribute: Attribute<'d>) -> u16 {
431        let handle = self.next_handle();
432        self.attributes.push(attribute).unwrap();
433        handle
434    }
435
436    fn iterate_from(&self, pos: usize) -> AttributeIterator<'_, 'd> {
437        AttributeIterator {
438            attributes: &self.attributes,
439            pos,
440        }
441    }
442
443    fn service_group_end(&self, service_handle: u16) -> u16 {
444        self.iterate_from(usize::from(service_handle)).service_group_end()
445    }
446
447    fn next_handle(&self) -> u16 {
448        self.attributes.len() as u16 + 1
449    }
450}
451
452impl<M: RawMutex, const MAX: usize> Default for AttributeTable<'_, M, MAX> {
453    fn default() -> Self {
454        Self::new()
455    }
456}
457
458impl<'d, M: RawMutex, const MAX: usize> AttributeTable<'d, M, MAX> {
459    /// Create a new GATT table.
460    pub fn new() -> Self {
461        Self {
462            inner: Mutex::new(RefCell::new(InnerTable { attributes: Vec::new() })),
463        }
464    }
465
466    pub(crate) fn with_inner<F: FnOnce(&InnerTable<'d, MAX>) -> R, R>(&self, f: F) -> R {
467        self.inner.lock(|inner| {
468            let table = inner.borrow();
469            f(&table)
470        })
471    }
472
473    pub(crate) fn with_inner_mut<F: FnOnce(&mut InnerTable<'d, MAX>) -> R, R>(&self, f: F) -> R {
474        self.inner.lock(|inner| {
475            let mut table = inner.borrow_mut();
476            f(&mut table)
477        })
478    }
479
480    pub(crate) fn iterate<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, f: F) -> R {
481        self.with_inner(|table| f(table.iterate_from(0)))
482    }
483
484    pub(crate) fn with_attribute<F: FnOnce(&Attribute<'d>) -> R, R>(&self, handle: u16, f: F) -> Option<R> {
485        if handle == 0 {
486            return None;
487        }
488
489        self.with_inner(|table| {
490            let i = usize::from(handle) - 1;
491            table.attributes.get(i).map(f)
492        })
493    }
494
495    pub(crate) fn with_attribute_mut<F: FnOnce(&mut Attribute<'d>) -> R, R>(&self, handle: u16, f: F) -> Option<R> {
496        if handle == 0 {
497            return None;
498        }
499
500        self.with_inner_mut(|table| {
501            let i = usize::from(handle) - 1;
502            table.attributes.get_mut(i).map(f)
503        })
504    }
505
506    pub(crate) fn iterate_from<F: FnOnce(AttributeIterator<'_, 'd>) -> R, R>(&self, start: u16, f: F) -> R {
507        let i = usize::from(start).saturating_sub(1);
508        self.with_inner(|table| f(table.iterate_from(i)))
509    }
510
511    fn push(&mut self, attribute: Attribute<'d>) -> u16 {
512        self.with_inner_mut(|table| table.push(attribute))
513    }
514
515    /// Add a service to the attribute table (group of characteristics)
516    pub fn add_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
517        let handle = self.push(Attribute::new(
518            PRIMARY_SERVICE,
519            AttPermissions::read_only(),
520            service.uuid,
521        ));
522        ServiceBuilder { handle, table: self }
523    }
524
525    /// Add a service to the attribute table (group of characteristics)
526    pub fn add_secondary_service(&mut self, service: Service) -> ServiceBuilder<'_, 'd, M, MAX> {
527        let handle = self.push(Attribute::new(
528            SECONDARY_SERVICE,
529            AttPermissions::read_only(),
530            service.uuid,
531        ));
532        ServiceBuilder { handle, table: self }
533    }
534
535    /// Get the permissions for the attribute
536    ///
537    /// Returns `None` if the attribute handle is invalid.
538    pub fn permissions(&self, attribute: u16) -> Option<AttPermissions> {
539        self.with_attribute(attribute, |att| att.permissions())
540    }
541
542    /// Get the UUID of the attribute type
543    ///
544    /// Returns `None` if the attribute handle is invalid.
545    pub fn uuid(&self, attribute: u16) -> Option<Uuid> {
546        self.with_attribute(attribute, |att| att.uuid)
547    }
548
549    pub(crate) fn set_ro(&self, attribute: u16, new_value: &'d [u8]) -> Result<(), Error> {
550        self.with_attribute_mut(attribute, |att| match &mut att.data {
551            AttributeData::ReadOnlyData { value, .. } => {
552                *value = new_value;
553                Ok(())
554            }
555            _ => Err(Error::NotSupported),
556        })
557        .unwrap_or(Err(Error::NotFound))
558    }
559
560    /// Read the raw value of the attribute
561    ///
562    /// If the attribute value is larger than the data buffer, data will be filled with
563    /// as many bytes as fit. Use additional reads with an offset to read the remaining data.
564    ///
565    /// The value of the attribute is undefined for connection-specific attributes (like CCCD).
566    pub fn read(&self, attribute: u16, offset: usize, data: &mut [u8]) -> Result<usize, Error> {
567        self.with_attribute(attribute, |att| att.read(offset, data).map_err(Into::into))
568            .unwrap_or(Err(Error::NotFound))
569    }
570
571    /// Write the raw value of the attribute
572    ///
573    /// If the attribute is variable length, its length will be set to `offset + data.len()`.
574    /// If the attribute is fixed length, the range `offset..(offset + data.len())` will be
575    /// overwritten.
576    pub fn write(&self, attribute: u16, offset: usize, data: &[u8]) -> Result<(), Error> {
577        self.with_attribute_mut(attribute, |att| att.write(offset, data).map_err(Into::into))
578            .unwrap_or(Err(Error::NotFound))
579    }
580
581    pub(crate) fn set_raw(&self, attribute: u16, input: &[u8]) -> Result<(), Error> {
582        self.with_attribute_mut(attribute, |att| {
583            if !att.data.is_variable_len() && att.data.capacity() != input.len() {
584                Err(Error::UnexpectedDataLength {
585                    expected: att.data.capacity(),
586                    actual: input.len(),
587                })
588            } else {
589                att.write(0, input).map_err(Into::into)
590            }
591        })
592        .unwrap_or(Err(Error::NotFound))
593    }
594
595    /// Get the number of attributes in the table
596    pub fn len(&self) -> usize {
597        self.with_inner(|table| table.attributes.len())
598    }
599
600    /// Returns true if the table is empty
601    pub fn is_empty(&self) -> bool {
602        self.with_inner(|table| table.attributes.is_empty())
603    }
604
605    /// Set the value of a characteristic
606    ///
607    /// For fixed-length values, the provided data must exactly match the storage size.
608    /// For variable-length values, any length up to the configured capacity is accepted.
609    ///
610    /// Returns an error if the characteristic cannot be found, if the input length is
611    /// incompatible with the characteristic storage, or if the data shape does not
612    /// match the characteristic type.
613    pub fn set<T: AttributeHandle>(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> {
614        let gatt_value = input.as_gatt();
615        self.set_raw(attribute_handle.handle(), gatt_value)
616    }
617
618    /// Read the value of the characteristic and pass the value to the provided closure.
619    ///
620    /// The return value of the closure is returned in this function and is assumed to be infallible.
621    ///
622    /// If the characteristic for the handle cannot be found, an error is returned.
623    pub fn get<T: AttributeHandle<Value = V>, V: FromGatt>(&self, attribute_handle: &T) -> Result<T::Value, Error> {
624        self.with_attribute(attribute_handle.handle(), |att| {
625            let value_slice = att.data.value().ok_or(Error::NotSupported)?;
626            T::Value::from_gatt(value_slice).map_err(|_| {
627                let mut invalid_data = [0u8; MAX_INVALID_DATA_LEN];
628                let len_to_copy = value_slice.len().min(MAX_INVALID_DATA_LEN);
629                invalid_data[..len_to_copy].copy_from_slice(&value_slice[..len_to_copy]);
630
631                Error::CannotConstructGattValue(invalid_data)
632            })
633        })
634        .unwrap_or(Err(Error::NotFound))
635    }
636
637    /// Return the characteristic which corresponds to the supplied value handle
638    ///
639    /// If no characteristic corresponding to the given value handle was found, returns an error
640    pub fn find_characteristic_by_value_handle<T: AsGatt>(&self, handle: u16) -> Result<Characteristic<T>, Error> {
641        if handle == 0 {
642            return Err(Error::NotFound);
643        }
644
645        self.iterate_from(handle - 1, |mut it| {
646            if let Some((_, att)) = it.next() {
647                if att.uuid == CHARACTERISTIC.into() {
648                    let decl = CharacteristicDeclaration::try_from(att.data.value().unwrap())?;
649                    let props = decl.props;
650                    let uuid = decl.uuid;
651                    if it.next().is_some() {
652                        let end_handle = it.characteristic_group_end();
653                        let cccd_handle = it.next().and_then(|(handle, att)| {
654                            (att.uuid == CLIENT_CHARACTERISTIC_CONFIGURATION.into()).then_some(handle)
655                        });
656
657                        return Ok(Characteristic {
658                            handle,
659                            cccd_handle,
660                            end_handle,
661                            props,
662                            uuid,
663                            phantom: PhantomData,
664                        });
665                    }
666                }
667            }
668            Err(Error::NotFound)
669        })
670    }
671
672    #[cfg(feature = "security")]
673    /// Calculate the database hash for the attribute table.
674    ///
675    /// See Core Specification Vol 3, Part G, Section 7.3.1
676    pub fn hash(&self) -> u128 {
677        use bt_hci::uuid::*;
678
679        use crate::security_manager::crypto::AesCmac;
680
681        const PRIMARY_SERVICE: Uuid = Uuid::Uuid16(declarations::PRIMARY_SERVICE.to_le_bytes());
682        const SECONDARY_SERVICE: Uuid = Uuid::Uuid16(declarations::SECONDARY_SERVICE.to_le_bytes());
683        const INCLUDED_SERVICE: Uuid = Uuid::Uuid16(declarations::INCLUDE.to_le_bytes());
684        const CHARACTERISTIC: Uuid = Uuid::Uuid16(declarations::CHARACTERISTIC.to_le_bytes());
685        const CHARACTERISTIC_EXTENDED_PROPERTIES: Uuid =
686            Uuid::Uuid16(descriptors::CHARACTERISTIC_EXTENDED_PROPERTIES.to_le_bytes());
687
688        const CHARACTERISTIC_USER_DESCRIPTION: Uuid =
689            Uuid::Uuid16(descriptors::CHARACTERISTIC_USER_DESCRIPTION.to_le_bytes());
690        const CLIENT_CHARACTERISTIC_CONFIGURATION: Uuid =
691            Uuid::Uuid16(descriptors::CLIENT_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
692        const SERVER_CHARACTERISTIC_CONFIGURATION: Uuid =
693            Uuid::Uuid16(descriptors::SERVER_CHARACTERISTIC_CONFIGURATION.to_le_bytes());
694        const CHARACTERISTIC_PRESENTATION_FORMAT: Uuid =
695            Uuid::Uuid16(descriptors::CHARACTERISTIC_PRESENTATION_FORMAT.to_le_bytes());
696        const CHARACTERISTIC_AGGREGATE_FORMAT: Uuid =
697            Uuid::Uuid16(descriptors::CHARACTERISTIC_AGGREGATE_FORMAT.to_le_bytes());
698
699        let mut mac = AesCmac::db_hash();
700
701        self.iterate(|it| {
702            for (handle, att) in it {
703                match att.uuid {
704                    PRIMARY_SERVICE
705                    | SECONDARY_SERVICE
706                    | INCLUDED_SERVICE
707                    | CHARACTERISTIC
708                    | CHARACTERISTIC_EXTENDED_PROPERTIES => {
709                        mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
710                        let value = att.data.value().unwrap_or(&[]);
711                        mac.update(value);
712                    }
713                    CHARACTERISTIC_USER_DESCRIPTION
714                    | CLIENT_CHARACTERISTIC_CONFIGURATION
715                    | SERVER_CHARACTERISTIC_CONFIGURATION
716                    | CHARACTERISTIC_PRESENTATION_FORMAT
717                    | CHARACTERISTIC_AGGREGATE_FORMAT => {
718                        mac.update(handle.to_le_bytes()).update(att.uuid.as_raw());
719                    }
720                    _ => {}
721                }
722            }
723        });
724
725        mac.finalize()
726    }
727}
728
729/// A type which holds a handle to an attribute in the attribute table
730pub trait AttributeHandle {
731    /// The data type which the attribute contains
732    type Value: AsGatt + ?Sized;
733
734    /// Returns the attribute's handle
735    fn handle(&self) -> u16;
736}
737
738impl<T: AsGatt + ?Sized> AttributeHandle for Characteristic<T> {
739    type Value = T;
740
741    fn handle(&self) -> u16 {
742        self.handle
743    }
744}
745
746/// Invalid handle value
747#[derive(Debug, Clone, PartialEq)]
748#[cfg_attr(feature = "defmt", derive(defmt::Format))]
749pub struct InvalidHandle;
750
751impl core::fmt::Display for InvalidHandle {
752    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753        core::fmt::Debug::fmt(self, f)
754    }
755}
756
757impl core::error::Error for InvalidHandle {}
758
759impl From<InvalidHandle> for Error {
760    fn from(value: InvalidHandle) -> Self {
761        Error::InvalidValue
762    }
763}
764
765/// Builder for constructing GATT service definitions.
766pub struct ServiceBuilder<'r, 'd, M: RawMutex, const MAX: usize> {
767    handle: u16,
768    table: &'r mut AttributeTable<'d, M, MAX>,
769}
770
771impl<'d, M: RawMutex, const MAX: usize> ServiceBuilder<'_, 'd, M, MAX> {
772    fn add_characteristic_internal<T: AsGatt + ?Sized>(
773        &mut self,
774        uuid: Uuid,
775        props: CharacteristicProps,
776        permissions: AttPermissions,
777        data: AttributeData<'d>,
778    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
779        let chrc_uuid = uuid;
780        // First the characteristic declaration
781        let (handle, cccd_handle) = self.table.with_inner_mut(|table| {
782            let value_handle = table.next_handle() + 1;
783            let declaration = CharacteristicDeclaration::new(props, value_handle, uuid);
784            table.push(Attribute::new(CHARACTERISTIC, AttPermissions::read_only(), declaration));
785
786            // Then the value declaration
787            let h = table.push(Attribute::new(uuid, permissions, data));
788            debug_assert!(h == value_handle);
789
790            // Add optional CCCD handle
791            let cccd_handle = if props.has_cccd() {
792                let handle = table.push(Attribute::new(
793                    CLIENT_CHARACTERISTIC_CONFIGURATION,
794                    AttPermissions {
795                        read: PermissionLevel::Allowed,
796                        write: PermissionLevel::Allowed,
797                        #[cfg(feature = "legacy-pairing")]
798                        min_key_len: 0,
799                    },
800                    AttributeData::ClientSpecific {
801                        variable_len: false,
802                        capacity: 2,
803                    },
804                ));
805
806                Some(handle)
807            } else {
808                None
809            };
810
811            (value_handle, cccd_handle)
812        });
813
814        CharacteristicBuilder {
815            handle: Characteristic {
816                handle,
817                cccd_handle,
818                // Temporarily set to value handle; updated in build() after descriptors are added
819                end_handle: cccd_handle.unwrap_or(handle),
820                props,
821                uuid: chrc_uuid,
822                phantom: PhantomData,
823            },
824            table: self.table,
825        }
826    }
827
828    /// Add a characteristic to this service with a refererence to a mutable storage buffer.
829    pub fn add_characteristic<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
830        &mut self,
831        uuid: U,
832        props: P,
833        value: T,
834        store: &'d mut [u8],
835    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
836        let props: CharacteristicProps = props.into();
837        let permissions = props.default_permissions();
838        let bytes = value.as_gatt();
839        store[..bytes.len()].copy_from_slice(bytes);
840        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
841        let len = bytes.len() as u16;
842        self.add_characteristic_internal(
843            uuid.into(),
844            props,
845            permissions,
846            AttributeData::Data {
847                value: store,
848                variable_len,
849                len,
850            },
851        )
852    }
853
854    /// Add a characteristic to this service using inline storage. The characteristic value must be [`MAX_SMALL_DATA_SIZE`] bytes or less.
855    pub fn add_characteristic_small<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
856        &mut self,
857        uuid: U,
858        props: P,
859        value: T,
860    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
861        assert!(T::MIN_SIZE <= MAX_SMALL_DATA_SIZE);
862
863        let props: CharacteristicProps = props.into();
864        let permissions = props.default_permissions();
865        let bytes = value.as_gatt();
866        assert!(bytes.len() <= MAX_SMALL_DATA_SIZE);
867        let mut value = [0; MAX_SMALL_DATA_SIZE];
868        value[..bytes.len()].copy_from_slice(bytes);
869        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
870        let capacity = T::MAX_SIZE.min(MAX_SMALL_DATA_SIZE) as u8;
871        let len = bytes.len() as u8;
872        self.add_characteristic_internal(
873            uuid.into(),
874            props,
875            permissions,
876            AttributeData::SmallData {
877                variable_len,
878                capacity,
879                len,
880                value,
881            },
882        )
883    }
884
885    /// Add a characteristic to this service with a refererence to an immutable storage buffer.
886    pub fn add_characteristic_ro<T: AsGatt + ?Sized, U: Into<Uuid>>(
887        &mut self,
888        uuid: U,
889        value: &'d T,
890    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
891        let props: CharacteristicProps = [CharacteristicProp::Read].into();
892        let permissions = props.default_permissions();
893        self.add_characteristic_internal(
894            uuid.into(),
895            props,
896            permissions,
897            AttributeData::ReadOnlyData { value: value.as_gatt() },
898        )
899    }
900
901    /// Add a client-specific characteristic to this service.
902    pub fn add_characteristic_client<T: AsGatt, U: Into<Uuid>, P: Into<CharacteristicProps>>(
903        &mut self,
904        uuid: U,
905        props: P,
906    ) -> CharacteristicBuilder<'_, 'd, T, M, MAX> {
907        assert!(T::MAX_SIZE <= 512);
908        let props: CharacteristicProps = props.into();
909        let permissions = props.default_permissions();
910        let variable_len = T::MAX_SIZE != T::MIN_SIZE;
911        self.add_characteristic_internal(
912            uuid.into(),
913            props,
914            permissions,
915            AttributeData::ClientSpecific {
916                variable_len,
917                capacity: T::MAX_SIZE as u16,
918            },
919        )
920    }
921
922    /// Add an included service to this service
923    pub fn add_included_service(&mut self, handle: u16) -> Result<u16, InvalidHandle> {
924        self.table.with_inner_mut(|table| {
925            if handle > 0 && table.attributes.len() >= usize::from(handle) {
926                let i = usize::from(handle - 1);
927                let att = &table.attributes[i];
928                let service_uuid = att.uuid;
929                if service_uuid == Uuid::from(PRIMARY_SERVICE) || service_uuid == Uuid::from(SECONDARY_SERVICE) {
930                    let last_handle_in_group = table.service_group_end(handle);
931
932                    // Included service values only include 16-bit UUIDs per the Bluetooth spec
933                    let uuid = att.data.value().and_then(|val| (val.len() == 2).then_some(val));
934
935                    // Encode: handle_LE(2) + last_handle_in_group_LE(2) + optional_uuid(0 or 2)
936                    let mut value = [0u8; MAX_SMALL_DATA_SIZE];
937                    let mut w = WriteCursor::new(&mut value);
938                    w.write(handle).unwrap();
939                    w.write(last_handle_in_group).unwrap();
940                    if let Some(uuid) = uuid {
941                        w.append(uuid).unwrap();
942                    }
943                    let len = w.len() as u8;
944
945                    Ok(table.push(Attribute::new(
946                        INCLUDE,
947                        AttPermissions::read_only(),
948                        AttributeData::SmallData {
949                            variable_len: false,
950                            capacity: len,
951                            len,
952                            value,
953                        },
954                    )))
955                } else {
956                    Err(InvalidHandle)
957                }
958            } else {
959                Err(InvalidHandle)
960            }
961        })
962    }
963
964    /// Finish construction of the service and return a handle.
965    pub fn build(self) -> u16 {
966        self.handle
967    }
968}
969
970/// A characteristic in the attribute table.
971#[cfg_attr(feature = "defmt", derive(defmt::Format))]
972#[derive(Clone, Copy, Debug, PartialEq)]
973pub struct Characteristic<T: AsGatt + ?Sized> {
974    /// Handle value assigned to the Client Characteristic Configuration Descriptor (if any)
975    pub cccd_handle: Option<u16>,
976    /// Handle value assigned to this characteristic when it is added to the Gatt Attribute Table
977    pub handle: u16,
978    /// Last attribute handle belonging to this characteristic (value handle + descriptors)
979    pub end_handle: u16,
980    /// Properties of this characteristic
981    pub props: CharacteristicProps,
982    /// UUID of this characteristic
983    pub uuid: Uuid,
984    pub(crate) phantom: PhantomData<T>,
985}
986
987impl<T: AsGatt + ?Sized> Characteristic<T> {
988    /// Check if notifications should be sent to `connection` for this characteristic.
989    pub fn should_notify<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>) -> bool {
990        let Some(cccd_handle) = self.cccd_handle else {
991            return false;
992        };
993        connection.server.should_notify(connection.raw(), cccd_handle)
994    }
995
996    /// Send a notification to `connection` with a new `value` for the characteristic.
997    ///
998    /// If `store` is true, the new value will be written to the table before sending the notification.
999    ///
1000    /// If the provided connection has not subscribed for this characteristic, it will not be notified.
1001    ///
1002    /// If the characteristic does not support notifications, an error is returned.
1003    pub async fn notify<P: PacketPool>(
1004        &self,
1005        connection: &GattConnection<'_, '_, P>,
1006        value: &T,
1007        store: bool,
1008    ) -> Result<(), Error> {
1009        self.notify_raw(connection, value.as_gatt(), store).await
1010    }
1011
1012    /// Send a notification to `connection` with a new `value` for the characteristic.
1013    ///
1014    /// If `store` is true, the new value will be written to the table before sending the notification.
1015    ///
1016    /// If the provided connection has not subscribed for this characteristic, it will not be notified.
1017    ///
1018    /// If the characteristic does not support notifications, an error is returned.
1019    pub async fn notify_raw<P: PacketPool>(
1020        &self,
1021        connection: &GattConnection<'_, '_, P>,
1022        value: &[u8],
1023        store: bool,
1024    ) -> Result<(), Error> {
1025        let server = connection.server;
1026        if store {
1027            server.set(connection.raw(), self.handle, value)?;
1028        }
1029
1030        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
1031        let conn = connection.raw();
1032        if !server.should_notify(conn, cccd_handle) {
1033            // No reason to fail?
1034            return Ok(());
1035        }
1036
1037        self.authorize_unsolicited(connection, cccd_handle).await?;
1038
1039        let uns = AttUns::Notify {
1040            handle: self.handle,
1041            data: value,
1042        };
1043        let pdu = gatt::assemble(conn, crate::att::AttServer::Unsolicited(uns))?;
1044        conn.send(pdu).await;
1045        Ok(())
1046    }
1047
1048    /// Check if indications should be sent to `connection` for this characteristic.
1049    pub fn should_indicate<P: PacketPool>(&self, connection: &GattConnection<'_, '_, P>) -> bool {
1050        let Some(cccd_handle) = self.cccd_handle else {
1051            return false;
1052        };
1053        connection.server.should_notify(connection.raw(), cccd_handle)
1054    }
1055
1056    /// Send an indication to `connection` with a new `value` for the characteristic.
1057    ///
1058    /// If `store` is true, the new value will be written to the table before sending the indication.
1059    ///
1060    /// If the provided connection has not subscribed for this characteristic, it will not be sent an indication.
1061    ///
1062    /// If the characteristic does not support indications, an error is returned.
1063    ///
1064    /// Blocks until the peer's `HandleValueConfirmation` is received, for up to 30 seconds
1065    /// (BT Core Spec Vol 3, Part F, Section 3.3.3 ATT transaction timeout). On timeout the
1066    /// connection is closed and `Error::Timeout` is returned. Concurrent calls on the same
1067    /// connection are serialized; callers do not need to coordinate. Returns
1068    /// `Error::Disconnected` if the connection is dropped while waiting.
1069    pub async fn indicate<P: PacketPool>(
1070        &self,
1071        connection: &GattConnection<'_, '_, P>,
1072        value: &T,
1073        store: bool,
1074    ) -> Result<(), Error> {
1075        self.indicate_raw(connection, value.as_gatt(), store).await
1076    }
1077
1078    /// Send an indication to `connection` with a new `value` for the characteristic.
1079    ///
1080    /// If `store` is true, the new value will be written to the table before sending the indication.
1081    ///
1082    /// If the provided connection has not subscribed for this characteristic, it will not be sent an indication.
1083    ///
1084    /// If the characteristic does not support indications, an error is returned.
1085    ///
1086    /// Blocks until the peer's `HandleValueConfirmation` is received, for up to 30 seconds
1087    /// (BT Core Spec Vol 3, Part F, Section 3.3.3 ATT transaction timeout). On timeout the
1088    /// connection is closed and `Error::Timeout` is returned. Concurrent calls on the same
1089    /// connection are serialized; callers do not need to coordinate. Returns
1090    /// `Error::Disconnected` if the connection is dropped while waiting.
1091    pub async fn indicate_raw<P: PacketPool>(
1092        &self,
1093        connection: &GattConnection<'_, '_, P>,
1094        value: &[u8],
1095        store: bool,
1096    ) -> Result<(), Error> {
1097        let server = connection.server;
1098        if store {
1099            server.set(connection.raw(), self.handle, value)?;
1100        }
1101
1102        let cccd_handle = self.cccd_handle.ok_or(Error::NotFound)?;
1103        let conn = connection.raw();
1104        if !server.should_indicate(conn, cccd_handle) {
1105            // No reason to fail?
1106            return Ok(());
1107        }
1108
1109        self.authorize_unsolicited(connection, cccd_handle).await?;
1110
1111        conn.acquire_indication_slot().await?;
1112        let _drop = crate::host::OnDrop::new(|| conn.release_indication_slot());
1113
1114        let uns = AttUns::Indicate {
1115            handle: self.handle,
1116            data: value,
1117        };
1118        let pdu = gatt::assemble(conn, crate::att::AttServer::Unsolicited(uns))?;
1119        conn.send(pdu).await;
1120
1121        match with_timeout(ATT_TRANSACTION_TIMEOUT, conn.wait_indication_confirmation()).await {
1122            Ok(Ok(())) => Ok(()),
1123            Ok(Err(e)) => Err(e),
1124            Err(_) => {
1125                warn!("[gatt] ATT transaction timeout (30s) waiting for indication confirmation, disconnecting");
1126                conn.disconnect();
1127                Err(Error::Timeout)
1128            }
1129        }
1130    }
1131
1132    /// Set the value of the characteristic in the provided attribute server.
1133    pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1134        &self,
1135        server: &AttributeServer<'_, M, P, AT, CN>,
1136        value: &T,
1137    ) -> Result<(), Error> {
1138        let value = value.as_gatt();
1139        server.table().set_raw(self.handle, value)?;
1140        Ok(())
1141    }
1142
1143    /// Set the value of the characteristic in the provided attribute server.
1144    pub fn set_ro<'a, M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1145        &self,
1146        server: &AttributeServer<'a, M, P, AT, CN>,
1147        value: &'a T,
1148    ) -> Result<(), Error> {
1149        let value = value.as_gatt();
1150        server.table().set_ro(self.handle, value)?;
1151        Ok(())
1152    }
1153
1154    /// Read the value of the characteristic.
1155    ///
1156    /// If the characteristic for the handle cannot be found, an error is returned.
1157    ///
1158    pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1159        &self,
1160        server: &AttributeServer<'_, M, P, AT, CN>,
1161    ) -> Result<T, Error>
1162    where
1163        T: FromGatt,
1164    {
1165        server.table().get(self)
1166    }
1167
1168    /// Access the raw byte value of the characteristic.
1169    pub fn with_raw_value<M: RawMutex, P: PacketPool, R, const AT: usize, const CN: usize>(
1170        &self,
1171        server: &AttributeServer<'_, M, P, AT, CN>,
1172        f: impl FnOnce(&[u8]) -> R,
1173    ) -> Option<R> {
1174        server
1175            .table()
1176            .with_attribute(self.handle, |att| att.data.value().map(f))
1177            .flatten()
1178    }
1179
1180    /// Returns the attribute handle for the characteristic's client characteristic configuration descriptor (if available)
1181    pub fn cccd_handle(&self) -> Option<CharacteristicPropertiesHandle> {
1182        self.cccd_handle.map(CharacteristicPropertiesHandle)
1183    }
1184
1185    /// Convert this characteristic's type to raw bytes
1186    pub fn to_raw(self) -> Characteristic<[u8]> {
1187        Characteristic {
1188            cccd_handle: self.cccd_handle,
1189            handle: self.handle,
1190            end_handle: self.end_handle,
1191            props: self.props,
1192            uuid: self.uuid,
1193            phantom: PhantomData,
1194        }
1195    }
1196
1197    async fn authorize_unsolicited<P: PacketPool>(
1198        &self,
1199        connection: &GattConnection<'_, '_, P>,
1200        cccd_handle: u16,
1201    ) -> Result<(), Error> {
1202        let server = connection.server;
1203        let conn = connection.raw();
1204
1205        // Ensure encryption before sending notifications that require security
1206        // (BT Core Spec Vol 3, Part C, Section 10.3.1.1: server "shall" initiate encryption)
1207        // Write permission on the CCCD defines permission to receive notifications/indications
1208        match server.can_write(conn, cccd_handle) {
1209            Ok(()) => Ok(()),
1210            Err(err) => {
1211                #[cfg(feature = "security")]
1212                if conn.is_bonded_peer() && !conn.security_level().is_ok_and(|l| l.encrypted()) {
1213                    conn.try_enable_encryption().await?;
1214                    server.can_write(conn, cccd_handle)?;
1215                    return Ok(());
1216                }
1217                Err(err.into())
1218            }
1219        }
1220    }
1221}
1222
1223/// Attribute handle for a characteristic's properties
1224pub struct CharacteristicPropertiesHandle(u16);
1225
1226impl AttributeHandle for CharacteristicPropertiesHandle {
1227    type Value = CharacteristicProps;
1228
1229    fn handle(&self) -> u16 {
1230        self.0
1231    }
1232}
1233
1234/// Builder for characteristics.
1235pub struct CharacteristicBuilder<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> {
1236    handle: Characteristic<T>,
1237    table: &'r mut AttributeTable<'d, M, MAX>,
1238}
1239
1240impl<'r, 'd, T: AsGatt + ?Sized, M: RawMutex, const MAX: usize> CharacteristicBuilder<'r, 'd, T, M, MAX> {
1241    fn add_descriptor_internal<DT: AsGatt + ?Sized>(
1242        &mut self,
1243        uuid: Uuid,
1244        permissions: AttPermissions,
1245        data: AttributeData<'d>,
1246    ) -> Descriptor<DT> {
1247        let handle = self.table.push(Attribute::new(uuid, permissions, data));
1248
1249        Descriptor {
1250            handle,
1251            uuid,
1252            phantom: PhantomData,
1253        }
1254    }
1255
1256    /// Add a characteristic descriptor for this characteristic.
1257    pub fn add_descriptor<DT: AsGatt, U: Into<Uuid>>(
1258        &mut self,
1259        uuid: U,
1260        permissions: AttPermissions,
1261        value: DT,
1262        store: &'d mut [u8],
1263    ) -> Descriptor<DT> {
1264        let bytes = value.as_gatt();
1265        store[..bytes.len()].copy_from_slice(bytes);
1266        let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1267        let len = bytes.len() as u16;
1268        self.add_descriptor_internal(
1269            uuid.into(),
1270            permissions,
1271            AttributeData::Data {
1272                value: store,
1273                variable_len,
1274                len,
1275            },
1276        )
1277    }
1278
1279    /// Add a characteristic to this service using inline storage. The descriptor value must be [`MAX_SMALL_DATA_SIZE`] bytes or less.
1280    pub fn add_descriptor_small<DT: AsGatt, U: Into<Uuid>>(
1281        &mut self,
1282        uuid: U,
1283        permissions: AttPermissions,
1284        value: DT,
1285    ) -> Descriptor<DT> {
1286        assert!(DT::MIN_SIZE <= MAX_SMALL_DATA_SIZE);
1287
1288        let bytes = value.as_gatt();
1289        assert!(bytes.len() <= MAX_SMALL_DATA_SIZE);
1290        let mut value = [0; MAX_SMALL_DATA_SIZE];
1291        value[..bytes.len()].copy_from_slice(bytes);
1292        let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1293        let capacity = DT::MAX_SIZE.min(MAX_SMALL_DATA_SIZE) as u8;
1294        let len = bytes.len() as u8;
1295        self.add_descriptor_internal(
1296            uuid.into(),
1297            permissions,
1298            AttributeData::SmallData {
1299                variable_len,
1300                capacity,
1301                len,
1302                value,
1303            },
1304        )
1305    }
1306
1307    /// Add a read only characteristic descriptor for this characteristic.
1308    pub fn add_descriptor_ro<DT: AsGatt + ?Sized, U: Into<Uuid>>(
1309        &mut self,
1310        uuid: U,
1311        read_permission: PermissionLevel,
1312        data: &'d DT,
1313    ) -> Descriptor<DT> {
1314        let permissions = AttPermissions {
1315            write: PermissionLevel::NotAllowed,
1316            read: read_permission,
1317            #[cfg(feature = "legacy-pairing")]
1318            min_key_len: 0,
1319        };
1320        self.add_descriptor_internal(
1321            uuid.into(),
1322            permissions,
1323            AttributeData::ReadOnlyData { value: data.as_gatt() },
1324        )
1325    }
1326
1327    /// Add a client-specific descriptor to this service.
1328    pub fn add_descriptor_client<DT: AsGatt, U: Into<Uuid>>(
1329        &mut self,
1330        uuid: U,
1331        permissions: AttPermissions,
1332    ) -> Descriptor<DT> {
1333        assert!(DT::MAX_SIZE <= 512);
1334        let variable_len = DT::MAX_SIZE != DT::MIN_SIZE;
1335        self.add_descriptor_internal(
1336            uuid.into(),
1337            permissions,
1338            AttributeData::ClientSpecific {
1339                variable_len,
1340                capacity: DT::MAX_SIZE as u16,
1341            },
1342        )
1343    }
1344
1345    /// Set the read permission for this characteristic
1346    pub fn read_permission(self, read: PermissionLevel) -> Self {
1347        self.table
1348            .with_attribute_mut(self.handle.handle, |att| att.permissions.read = read);
1349        self
1350    }
1351
1352    /// Set the write permission for this characteristic
1353    pub fn write_permission(self, write: PermissionLevel) -> Self {
1354        self.table
1355            .with_attribute_mut(self.handle.handle, |att| att.permissions.write = write);
1356        self
1357    }
1358
1359    /// Set the write permission for the Client Characteristic Configuration Descriptor for this characteristic
1360    ///
1361    /// Panics if this characteristic does not have a Client Characteristic Configuration Descriptor.
1362    pub fn cccd_permission(self, write: PermissionLevel) -> Self {
1363        let Some(handle) = self.handle.cccd_handle else {
1364            panic!("Can't set CCCD permission on characteristics without notify or indicate properties.");
1365        };
1366
1367        self.table
1368            .with_attribute_mut(handle, |att| att.permissions.write = write);
1369        self
1370    }
1371
1372    /// Set the minimum encryption key length required for this characteristic
1373    #[cfg(feature = "legacy-pairing")]
1374    pub fn min_key_len(self, len: u8) -> Self {
1375        self.table
1376            .with_attribute_mut(self.handle.handle, |att| att.permissions.min_key_len = len);
1377        self
1378    }
1379
1380    /// Convert this characteristic's type to raw bytes
1381    pub fn to_raw(self) -> CharacteristicBuilder<'r, 'd, [u8], M, MAX> {
1382        CharacteristicBuilder {
1383            handle: self.handle.to_raw(),
1384            table: self.table,
1385        }
1386    }
1387    /// Return the built characteristic.
1388    pub fn build(mut self) -> Characteristic<T> {
1389        self.handle.end_handle = self.table.with_inner(|t| t.next_handle() - 1);
1390        self.handle
1391    }
1392}
1393
1394/// Characteristic descriptor handle.
1395#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1396#[derive(Clone, Copy, Debug)]
1397pub struct Descriptor<T: AsGatt + ?Sized> {
1398    pub(crate) handle: u16,
1399    pub(crate) uuid: Uuid,
1400    pub(crate) phantom: PhantomData<T>,
1401}
1402
1403impl<T: AsGatt + ?Sized> AttributeHandle for Descriptor<T> {
1404    type Value = T;
1405
1406    fn handle(&self) -> u16 {
1407        self.handle
1408    }
1409}
1410
1411impl<T: AsGatt + ?Sized> Descriptor<T> {
1412    /// Get the handle of this descriptor.
1413    pub fn handle(&self) -> u16 {
1414        self.handle
1415    }
1416
1417    /// Get the UUID of this descriptor.
1418    pub fn uuid(&self) -> &Uuid {
1419        &self.uuid
1420    }
1421
1422    /// Set the value of the descriptor in the provided attribute server.
1423    pub fn set<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1424        &self,
1425        server: &AttributeServer<'_, M, P, AT, CN>,
1426        value: &T,
1427    ) -> Result<(), Error> {
1428        let value = value.as_gatt();
1429        server.table().set_raw(self.handle, value)?;
1430        Ok(())
1431    }
1432
1433    /// Read the value of the descriptor.
1434    ///
1435    /// If the descriptor for the handle cannot be found, an error is returned.
1436    ///
1437    pub fn get<M: RawMutex, P: PacketPool, const AT: usize, const CN: usize>(
1438        &self,
1439        server: &AttributeServer<'_, M, P, AT, CN>,
1440    ) -> Result<T, Error>
1441    where
1442        T: FromGatt,
1443    {
1444        server.table().get(self)
1445    }
1446}
1447
1448/// Iterator over attributes.
1449pub struct AttributeIterator<'a, 'd> {
1450    attributes: &'a [Attribute<'d>],
1451    pos: usize,
1452}
1453
1454impl<'a, 'd> Iterator for AttributeIterator<'a, 'd> {
1455    type Item = (u16, &'a Attribute<'d>);
1456
1457    fn next(&mut self) -> Option<Self::Item> {
1458        if self.pos < self.attributes.len() {
1459            let att = &self.attributes[self.pos];
1460            self.pos += 1;
1461            let handle = self.pos as u16;
1462            Some((handle, att))
1463        } else {
1464            None
1465        }
1466    }
1467}
1468
1469impl<'a, 'd> AttributeIterator<'a, 'd> {
1470    /// Find the handle of the last attribute in the current GATT service.
1471    ///
1472    /// Returns `u16::MAX` for the last service in the table.
1473    pub fn service_group_end(&self) -> u16 {
1474        // We return the 0-based index of the next service definition. When interpretted as a 1-based handle,
1475        // this represents the handle of the last attribute before the next service definition.
1476        self.attributes
1477            .iter()
1478            .enumerate()
1479            .skip(self.pos)
1480            .find(|(_, attr)| attr.uuid == PRIMARY_SERVICE.into() || attr.uuid == SECONDARY_SERVICE.into())
1481            .map(|(i, _)| i as u16)
1482            .unwrap_or(u16::MAX)
1483    }
1484
1485    /// Find the handle of the last attribute in the current GATT characteristic.
1486    pub fn characteristic_group_end(&self) -> u16 {
1487        // We return the 0-based index of the next characteristic or service definition. When interpretted as a 1-based handle,
1488        // this represents the handle of the last attribute before the next characteristic or service definition.
1489        self.attributes
1490            .iter()
1491            .enumerate()
1492            .skip(self.pos)
1493            .find(|(_, attr)| {
1494                attr.uuid == PRIMARY_SERVICE.into()
1495                    || attr.uuid == SECONDARY_SERVICE.into()
1496                    || attr.uuid == CHARACTERISTIC.into()
1497            })
1498            .map(|(i, _)| i as u16)
1499            .unwrap_or(self.attributes.len() as u16)
1500    }
1501}
1502
1503/// A GATT service.
1504pub struct Service {
1505    /// UUID of the service.
1506    pub uuid: Uuid,
1507}
1508
1509impl Service {
1510    /// Create a new service with a uuid.
1511    pub fn new<U: Into<Uuid>>(uuid: U) -> Self {
1512        Self { uuid: uuid.into() }
1513    }
1514}
1515
1516/// Properties of a characteristic.
1517#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1518#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1519pub struct CharacteristicProps(u8);
1520
1521impl<'a> From<&'a [CharacteristicProp]> for CharacteristicProps {
1522    fn from(props: &'a [CharacteristicProp]) -> Self {
1523        let mut val: u8 = 0;
1524        for prop in props {
1525            val |= *prop as u8;
1526        }
1527        CharacteristicProps(val)
1528    }
1529}
1530
1531impl<'a, const N: usize> From<&'a [CharacteristicProp; N]> for CharacteristicProps {
1532    fn from(props: &'a [CharacteristicProp; N]) -> Self {
1533        let mut val: u8 = 0;
1534        for prop in props {
1535            val |= *prop as u8;
1536        }
1537        CharacteristicProps(val)
1538    }
1539}
1540
1541impl<const N: usize> From<[CharacteristicProp; N]> for CharacteristicProps {
1542    fn from(props: [CharacteristicProp; N]) -> Self {
1543        let mut val: u8 = 0;
1544        for prop in props {
1545            val |= prop as u8;
1546        }
1547        CharacteristicProps(val)
1548    }
1549}
1550
1551impl From<u8> for CharacteristicProps {
1552    fn from(value: u8) -> Self {
1553        Self(value)
1554    }
1555}
1556
1557impl CharacteristicProps {
1558    /// Check if any of the properties are set.
1559    pub fn any(&self, props: &[CharacteristicProp]) -> bool {
1560        for p in props {
1561            if (*p as u8) & self.0 != 0 {
1562                return true;
1563            }
1564        }
1565        false
1566    }
1567
1568    pub(crate) fn default_permissions(&self) -> AttPermissions {
1569        let read = if (self.0 & CharacteristicProp::Read as u8) != 0 {
1570            PermissionLevel::Allowed
1571        } else {
1572            PermissionLevel::NotAllowed
1573        };
1574
1575        let write = if (self.0
1576            & (CharacteristicProp::Write as u8
1577                | CharacteristicProp::WriteWithoutResponse as u8
1578                | CharacteristicProp::AuthenticatedWrite as u8))
1579            != 0
1580        {
1581            PermissionLevel::Allowed
1582        } else {
1583            PermissionLevel::NotAllowed
1584        };
1585
1586        AttPermissions {
1587            read,
1588            write,
1589            #[cfg(feature = "legacy-pairing")]
1590            min_key_len: 0,
1591        }
1592    }
1593
1594    /// Check if the characteristic will have a Client Characteristic Configuration Descriptor
1595    pub fn has_cccd(&self) -> bool {
1596        (self.0 & (CharacteristicProp::Indicate as u8 | CharacteristicProp::Notify as u8)) != 0
1597    }
1598
1599    /// Get the raw value of the characteristic props
1600    pub fn to_raw(self) -> u8 {
1601        self.0
1602    }
1603}
1604
1605impl FixedGattValue for CharacteristicProps {
1606    const SIZE: usize = 1;
1607}
1608
1609impl FromGatt for CharacteristicProps {
1610    fn from_gatt(data: &[u8]) -> Result<Self, FromGattError> {
1611        if data.len() != Self::SIZE {
1612            return Err(FromGattError::InvalidLength);
1613        }
1614
1615        Ok(CharacteristicProps(data[0]))
1616    }
1617}
1618
1619impl AsGatt for CharacteristicProps {
1620    const MIN_SIZE: usize = Self::SIZE;
1621    const MAX_SIZE: usize = Self::SIZE;
1622
1623    fn as_gatt(&self) -> &[u8] {
1624        AsGatt::as_gatt(&self.0)
1625    }
1626}
1627
1628/// A value of an attribute.
1629pub struct AttributeValue<'d, M: RawMutex> {
1630    value: Mutex<M, &'d mut [u8]>,
1631}
1632
1633impl<M: RawMutex> AttributeValue<'_, M> {}
1634
1635/// CCCD flag values.
1636#[derive(Clone, Copy)]
1637pub enum CCCDFlag {
1638    /// Notifications enabled.
1639    Notify = 0x1,
1640    /// Indications enabled.
1641    Indicate = 0x2,
1642}
1643
1644/// CCCD flag.
1645#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1646#[derive(Clone, Copy, Default, Debug, PartialEq)]
1647pub struct CCCD(pub(crate) u16);
1648
1649impl<const T: usize> From<[CCCDFlag; T]> for CCCD {
1650    fn from(props: [CCCDFlag; T]) -> Self {
1651        let mut val: u16 = 0;
1652        for prop in props {
1653            val |= prop as u16;
1654        }
1655        CCCD(val)
1656    }
1657}
1658
1659impl From<u16> for CCCD {
1660    fn from(value: u16) -> Self {
1661        CCCD(value)
1662    }
1663}
1664
1665impl CCCD {
1666    /// Get raw value
1667    pub fn raw(&self) -> u16 {
1668        self.0
1669    }
1670
1671    /// Clear all properties
1672    pub fn disable(&mut self) {
1673        self.0 = 0;
1674    }
1675
1676    /// Check if any of the properties are set.
1677    pub fn any(&self, props: &[CCCDFlag]) -> bool {
1678        for p in props {
1679            if (*p as u16) & self.0 != 0 {
1680                return true;
1681            }
1682        }
1683        false
1684    }
1685
1686    /// Enable or disable notifications
1687    pub fn set_notify(&mut self, is_enabled: bool) {
1688        let mask: u16 = CCCDFlag::Notify as u16;
1689        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1690    }
1691
1692    /// Check if notifications are enabled
1693    pub fn should_notify(&self) -> bool {
1694        (self.0 & (CCCDFlag::Notify as u16)) != 0
1695    }
1696
1697    /// Enable or disable indication
1698    pub fn set_indicate(&mut self, is_enabled: bool) {
1699        let mask: u16 = CCCDFlag::Indicate as u16;
1700        self.0 = if is_enabled { self.0 | mask } else { self.0 & !mask };
1701    }
1702
1703    /// Check if indications are enabled
1704    pub fn should_indicate(&self) -> bool {
1705        (self.0 & (CCCDFlag::Indicate as u16)) != 0
1706    }
1707}
1708
1709#[cfg(test)]
1710mod tests {
1711    extern crate std;
1712
1713    #[cfg(feature = "security")]
1714    #[test]
1715    fn database_hash() {
1716        use bt_hci::uuid::characteristic::{
1717            APPEARANCE, CLIENT_SUPPORTED_FEATURES, DATABASE_HASH, DEVICE_NAME, SERVICE_CHANGED,
1718        };
1719        use bt_hci::uuid::declarations::{CHARACTERISTIC, PRIMARY_SERVICE};
1720        use bt_hci::uuid::descriptors::{
1721            CHARACTERISTIC_PRESENTATION_FORMAT, CHARACTERISTIC_USER_DESCRIPTION, CLIENT_CHARACTERISTIC_CONFIGURATION,
1722        };
1723        use bt_hci::uuid::service::{GAP, GATT};
1724        use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1725
1726        use super::*;
1727
1728        // The raw message data that should be hashed for this attribute table is:
1729        //
1730        // 0100 0028 0018
1731        // 0200 0328 020300002a
1732        // 0400 0328 020500012a
1733        //
1734        // 0600 0028 0118
1735        // 0700 0328 200800052a
1736        // 0900 0229
1737        // 0a00 0328 0a0b00292b
1738        // 0c00 0328 020d002a2b
1739        //
1740        // 0e00 0028 f0debc9a785634127856341278563412
1741        // 0f00 0328 121000f1debc9a785634127856341278563412
1742        // 1100 0229
1743        // 1200 0129
1744        // 1300 0429
1745        //
1746        // The message hash can be calculated on the command line with:
1747        // > xxd -plain -revert message.txt message.bin
1748        // > openssl mac -cipher AES-128-CBC -macopt hexkey:00000000000000000000000000000000 -in message.bin CMAC
1749
1750        let mut table: AttributeTable<'static, NoopRawMutex, 20> = AttributeTable::new();
1751
1752        let ro = AttPermissions::read_only();
1753        let cccd_perms = AttPermissions {
1754            read: PermissionLevel::Allowed,
1755            write: PermissionLevel::Allowed,
1756            #[cfg(feature = "legacy-pairing")]
1757            min_key_len: 0,
1758        };
1759
1760        // GAP service (handles 0x001 - 0x005)
1761        table.push(Attribute::new(PRIMARY_SERVICE, ro, GAP));
1762
1763        let expected = 0xd4cdec10804db3f147b4d7d10baa0120;
1764        let actual = table.hash();
1765        assert_eq!(
1766            actual, expected,
1767            "\nexpected: {:#032x}\nactual: {:#032x}",
1768            expected, actual
1769        );
1770
1771        // Device name characteristic
1772        table.push(Attribute::new(
1773            CHARACTERISTIC,
1774            ro,
1775            CharacteristicDeclaration::new([CharacteristicProp::Read], 0x0003, DEVICE_NAME),
1776        ));
1777
1778        table.push(Attribute::new(
1779            DEVICE_NAME,
1780            ro,
1781            AttributeData::ReadOnlyData { value: b"" },
1782        ));
1783
1784        // Appearance characteristic
1785        table.push(Attribute::new(
1786            CHARACTERISTIC,
1787            ro,
1788            CharacteristicDeclaration::new([CharacteristicProp::Read], 0x0005, APPEARANCE),
1789        ));
1790
1791        table.push(Attribute::new(
1792            APPEARANCE,
1793            ro,
1794            AttributeData::ReadOnlyData { value: b"" },
1795        ));
1796
1797        let expected = 0x6c329e3f1d52c03f174980f6b4704875;
1798        let actual = table.hash();
1799        assert_eq!(
1800            actual, expected,
1801            "\nexpected: {:#032x}\n  actual: {:#032x}",
1802            expected, actual
1803        );
1804
1805        // GATT service (handles 0x006 - 0x000d)
1806        table.push(Attribute::new(PRIMARY_SERVICE, ro, GATT));
1807
1808        // Service changed characteristic
1809        table.push(Attribute::new(
1810            CHARACTERISTIC,
1811            ro,
1812            CharacteristicDeclaration::new([CharacteristicProp::Indicate], 0x0008, SERVICE_CHANGED),
1813        ));
1814
1815        table.push(Attribute::new(
1816            SERVICE_CHANGED,
1817            ro,
1818            AttributeData::ReadOnlyData { value: b"" },
1819        ));
1820
1821        table.push(Attribute::new(
1822            CLIENT_CHARACTERISTIC_CONFIGURATION,
1823            cccd_perms,
1824            AttributeData::ClientSpecific {
1825                variable_len: false,
1826                capacity: 2,
1827            },
1828        ));
1829
1830        // Client supported features characteristic
1831        table.push(Attribute::new(
1832            CHARACTERISTIC,
1833            ro,
1834            CharacteristicDeclaration::new(
1835                [CharacteristicProp::Read, CharacteristicProp::Write],
1836                0x000b,
1837                CLIENT_SUPPORTED_FEATURES,
1838            ),
1839        ));
1840
1841        table.push(Attribute::new(
1842            CLIENT_SUPPORTED_FEATURES,
1843            ro,
1844            AttributeData::ReadOnlyData { value: b"" },
1845        ));
1846
1847        // Database hash characteristic
1848        table.push(Attribute::new(
1849            CHARACTERISTIC,
1850            ro,
1851            CharacteristicDeclaration::new([CharacteristicProp::Read], 0x000d, DATABASE_HASH),
1852        ));
1853
1854        table.push(Attribute::new(
1855            DATABASE_HASH,
1856            ro,
1857            AttributeData::ReadOnlyData { value: b"" },
1858        ));
1859
1860        let expected = 0x16ce756326c5062bf74022f845c2b21f;
1861        let actual = table.hash();
1862        assert_eq!(
1863            actual, expected,
1864            "\nexpected: {:#032x}\n  actual: {:#032x}",
1865            expected, actual
1866        );
1867
1868        const CUSTOM_SERVICE: u128 = 0x12345678_12345678_12345678_9abcdef0;
1869        const CUSTOM_CHARACTERISTIC: u128 = 0x12345678_12345678_12345678_9abcdef1;
1870
1871        // Custom service (handles 0x00e - 0x0013)
1872        table.push(Attribute::new(PRIMARY_SERVICE, ro, Uuid::from(CUSTOM_SERVICE)));
1873
1874        // Custom characteristic
1875        table.push(Attribute::new(
1876            CHARACTERISTIC,
1877            ro,
1878            CharacteristicDeclaration::new(
1879                [CharacteristicProp::Notify, CharacteristicProp::Read],
1880                0x0010,
1881                CUSTOM_CHARACTERISTIC,
1882            ),
1883        ));
1884
1885        table.push(Attribute::new(
1886            CUSTOM_CHARACTERISTIC,
1887            ro,
1888            AttributeData::ReadOnlyData { value: b"" },
1889        ));
1890
1891        table.push(Attribute::new(
1892            CLIENT_CHARACTERISTIC_CONFIGURATION,
1893            cccd_perms,
1894            AttributeData::ClientSpecific {
1895                variable_len: false,
1896                capacity: 2,
1897            },
1898        ));
1899
1900        table.push(Attribute::new(
1901            CHARACTERISTIC_USER_DESCRIPTION,
1902            ro,
1903            AttributeData::ReadOnlyData {
1904                value: b"Custom Characteristic",
1905            },
1906        ));
1907
1908        table.push(Attribute::new(
1909            CHARACTERISTIC_PRESENTATION_FORMAT,
1910            ro,
1911            AttributeData::ReadOnlyData {
1912                value: &[4, 0, 0, 0x27, 1, 0, 0],
1913            },
1914        ));
1915
1916        let expected = 0xc7352cced28d6608d4b057d247d8be76;
1917        let actual = table.hash();
1918        assert_eq!(
1919            actual, expected,
1920            "\nexpected: {:#032x}\n  actual: {:#032x}",
1921            expected, actual
1922        );
1923    }
1924
1925    #[test]
1926    fn set_updates_variable_length_when_value_fills_backing_storage() {
1927        use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1928        use heapless::Vec;
1929
1930        use super::*;
1931
1932        let mut storage = [0u8; 4];
1933        let mut table: AttributeTable<'_, NoopRawMutex, 4> = AttributeTable::new();
1934        let initial = Vec::<u8, 4>::from_slice(b"ab").unwrap();
1935        let characteristic = table
1936            .add_service(Service {
1937                uuid: Uuid::new_long([0x10; 16]),
1938            })
1939            .add_characteristic(
1940                Uuid::new_long([0x11; 16]),
1941                [CharacteristicProp::Read, CharacteristicProp::Write],
1942                initial,
1943                &mut storage,
1944            )
1945            .build();
1946
1947        let replacement = Vec::<u8, 4>::from_slice(b"wxyz").unwrap();
1948        table.set(&characteristic, &replacement).unwrap();
1949
1950        let stored: Vec<u8, 4> = table.get(&characteristic).unwrap();
1951        assert_eq!(stored.as_slice(), b"wxyz");
1952    }
1953
1954    #[test]
1955    fn set_updates_small_variable_length_when_value_reaches_capacity() {
1956        use embassy_sync::blocking_mutex::raw::NoopRawMutex;
1957        use heapless::String;
1958
1959        use super::*;
1960
1961        let mut table: AttributeTable<'_, NoopRawMutex, 4> = AttributeTable::new();
1962        let initial = String::<8>::try_from("hi").unwrap();
1963        let characteristic = table
1964            .add_service(Service {
1965                uuid: Uuid::new_long([0x12; 16]),
1966            })
1967            .add_characteristic_small(
1968                Uuid::new_long([0x13; 16]),
1969                [CharacteristicProp::Read, CharacteristicProp::Write],
1970                initial,
1971            )
1972            .build();
1973
1974        let replacement = String::<8>::try_from("12345678").unwrap();
1975        table.set(&characteristic, &replacement).unwrap();
1976
1977        let stored: String<8> = table.get(&characteristic).unwrap();
1978        assert_eq!(stored.as_str(), "12345678");
1979    }
1980}