• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This module converts a GattDatastore to an AttDatabase,
2 //! by converting a registry of services into a list of attributes, and proxying
3 //! ATT read/write requests into characteristic reads/writes
4 
5 use std::{cell::RefCell, collections::BTreeMap, ops::RangeInclusive, rc::Rc};
6 
7 use anyhow::{bail, Result};
8 use async_trait::async_trait;
9 use log::{error, warn};
10 
11 use crate::{
12     core::{
13         shared_box::{SharedBox, WeakBox, WeakBoxRef},
14         uuid::Uuid,
15     },
16     gatt::{
17         callbacks::{GattWriteRequestType, RawGattDatastore},
18         ffi::AttributeBackingType,
19         ids::{AttHandle, TransportIndex},
20     },
21     packets::{
22         AttAttributeDataChild, AttAttributeDataView, AttErrorCode,
23         GattCharacteristicDeclarationValueBuilder, GattCharacteristicPropertiesBuilder,
24         GattServiceDeclarationValueBuilder, UuidBuilder,
25     },
26 };
27 
28 use super::{
29     att_database::{AttAttribute, AttDatabase},
30     att_server_bearer::AttServerBearer,
31 };
32 
33 pub use super::att_database::AttPermissions;
34 
35 /// Primary Service Declaration from Bluetooth Assigned Numbers 3.5 Declarations
36 pub const PRIMARY_SERVICE_DECLARATION_UUID: Uuid = Uuid::new(0x2800);
37 /// Secondary Service Declaration from Bluetooth Assigned Numbers 3.5 Declarations
38 pub const SECONDARY_SERVICE_DECLARATION_UUID: Uuid = Uuid::new(0x2801);
39 /// Characteristic Declaration from Bluetooth Assigned Numbers 3.5 Declarations
40 pub const CHARACTERISTIC_UUID: Uuid = Uuid::new(0x2803);
41 
42 /// A GattService (currently, only primary services are supported) has an
43 /// identifying UUID and a list of contained characteristics, as well as a
44 /// handle (indicating the attribute where the service declaration will live)
45 #[derive(Debug, Clone)]
46 pub struct GattServiceWithHandle {
47     /// The handle of the service declaration
48     pub handle: AttHandle,
49     /// The type of the service
50     pub type_: Uuid,
51     /// A list of contained characteristics (that must have handles between the
52     /// service declaration handle, and that of the next service)
53     pub characteristics: Vec<GattCharacteristicWithHandle>,
54 }
55 
56 /// A GattCharacteristic consists of a handle (where the value attribute lives),
57 /// a UUID identifying its type, and permissions indicating what operations can
58 /// be performed
59 #[derive(Debug, Clone)]
60 pub struct GattCharacteristicWithHandle {
61     /// The handle of the characteristic value attribute. The characteristic
62     /// declaration is one before this handle.
63     pub handle: AttHandle,
64     /// The UUID representing the type of the characteristic value.
65     pub type_: Uuid,
66     /// The permissions (read/write) indicate what operations can be performed.
67     pub permissions: AttPermissions,
68     /// The descriptors associated with this characteristic
69     pub descriptors: Vec<GattDescriptorWithHandle>,
70 }
71 
72 /// A GattDescriptor consists of a handle, type_, and permissions (similar to a
73 /// GattCharacteristic) It is guaranteed that the handle of the GattDescriptor
74 /// is after the handle of the characteristic value attribute, and before the
75 /// next characteristic/service declaration
76 #[derive(Debug, Clone)]
77 pub struct GattDescriptorWithHandle {
78     /// The handle of the descriptor.
79     pub handle: AttHandle,
80     /// The UUID representing the type of the descriptor.
81     pub type_: Uuid,
82     /// The permissions (read/write) indicate what operations can be performed.
83     pub permissions: AttPermissions,
84 }
85 
86 /// The GattDatabase implements AttDatabase, and converts attribute reads/writes
87 /// into GATT operations to be sent to the upper layers
88 #[derive(Default)]
89 pub struct GattDatabase {
90     schema: RefCell<GattDatabaseSchema>,
91     listeners: RefCell<Vec<Rc<dyn GattDatabaseCallbacks>>>,
92 }
93 
94 #[derive(Default)]
95 struct GattDatabaseSchema {
96     attributes: BTreeMap<AttHandle, AttAttributeWithBackingValue>,
97 }
98 
99 #[derive(Clone)]
100 enum AttAttributeBackingValue {
101     Static(AttAttributeDataChild),
102     DynamicCharacteristic(Rc<dyn RawGattDatastore>),
103     DynamicDescriptor(Rc<dyn RawGattDatastore>),
104 }
105 
106 #[derive(Clone)]
107 struct AttAttributeWithBackingValue {
108     attribute: AttAttribute,
109     value: AttAttributeBackingValue,
110 }
111 
112 /// Callbacks that can be registered on the GattDatabase to watch for
113 /// events of interest.
114 ///
115 /// Note: if the GattDatabase is dropped (e.g. due to unregistration), these
116 /// callbacks will not be invoked, even if the relevant event occurs later.
117 /// e.g. if we open the db, connect, close the db, then disconnect, then on_le_disconnect()
118 /// will NEVER be invoked.
119 pub trait GattDatabaseCallbacks {
120     /// A peer device on the given bearer has connected to this database (and can see its attributes)
on_le_connect( &self, tcb_idx: TransportIndex, bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>, )121     fn on_le_connect(
122         &self,
123         tcb_idx: TransportIndex,
124         bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>,
125     );
126     /// A peer device has disconnected from this database
on_le_disconnect(&self, tcb_idx: TransportIndex)127     fn on_le_disconnect(&self, tcb_idx: TransportIndex);
128     /// The attributes in the specified range have changed
on_service_change(&self, range: RangeInclusive<AttHandle>)129     fn on_service_change(&self, range: RangeInclusive<AttHandle>);
130 }
131 
132 impl GattDatabase {
133     /// Constructor, wrapping a GattDatastore
new() -> Self134     pub fn new() -> Self {
135         Default::default()
136     }
137 
138     /// Register an event listener
register_listener(&self, callbacks: Rc<dyn GattDatabaseCallbacks>)139     pub fn register_listener(&self, callbacks: Rc<dyn GattDatabaseCallbacks>) {
140         self.listeners.borrow_mut().push(callbacks);
141     }
142 
143     /// When a connection has been made with access to this database.
144     /// The supplied bearer is guaranteed to be ready for use.
on_bearer_ready( &self, tcb_idx: TransportIndex, bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>, )145     pub fn on_bearer_ready(
146         &self,
147         tcb_idx: TransportIndex,
148         bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>,
149     ) {
150         for listener in self.listeners.borrow().iter() {
151             listener.on_le_connect(tcb_idx, bearer.clone());
152         }
153     }
154 
155     /// When the connection has dropped.
on_bearer_dropped(&self, tcb_idx: TransportIndex)156     pub fn on_bearer_dropped(&self, tcb_idx: TransportIndex) {
157         for listener in self.listeners.borrow().iter() {
158             listener.on_le_disconnect(tcb_idx);
159         }
160     }
161 
162     /// Add a service with pre-allocated handles (for co-existence with C++) backed by the supplied datastore
163     /// Assumes that the characteristic DECLARATION handles are one less than
164     /// the characteristic handles.
165     /// Returns failure if handles overlap with ones already allocated
add_service_with_handles( &self, service: GattServiceWithHandle, datastore: Rc<dyn RawGattDatastore>, ) -> Result<()>166     pub fn add_service_with_handles(
167         &self,
168         service: GattServiceWithHandle,
169         datastore: Rc<dyn RawGattDatastore>,
170     ) -> Result<()> {
171         let mut attributes = BTreeMap::new();
172         let mut attribute_cnt = 0;
173 
174         let mut add_attribute = |attribute: AttAttribute, value: AttAttributeBackingValue| {
175             attribute_cnt += 1;
176             attributes.insert(attribute.handle, AttAttributeWithBackingValue { attribute, value })
177         };
178 
179         let mut characteristics = vec![];
180 
181         // service definition
182         add_attribute(
183             AttAttribute {
184                 handle: service.handle,
185                 type_: PRIMARY_SERVICE_DECLARATION_UUID,
186                 permissions: AttPermissions::READABLE,
187             },
188             AttAttributeBackingValue::Static(
189                 GattServiceDeclarationValueBuilder { uuid: UuidBuilder::from(service.type_) }
190                     .into(),
191             ),
192         );
193 
194         // characteristics
195         for characteristic in service.characteristics {
196             characteristics.push(characteristic.clone());
197 
198             // declaration
199             // Recall that we assume the declaration handle is one less than the value
200             // handle
201             let declaration_handle = AttHandle(characteristic.handle.0 - 1);
202 
203             add_attribute(
204                 AttAttribute {
205                     handle: declaration_handle,
206                     type_: CHARACTERISTIC_UUID,
207                     permissions: AttPermissions::READABLE,
208                 },
209                 AttAttributeBackingValue::Static(
210                     GattCharacteristicDeclarationValueBuilder {
211                         properties: GattCharacteristicPropertiesBuilder {
212                             broadcast: 0,
213                             read: characteristic.permissions.readable().into(),
214                             write_without_response: characteristic
215                                 .permissions
216                                 .writable_without_response()
217                                 .into(),
218                             write: characteristic.permissions.writable_with_response().into(),
219                             notify: 0,
220                             indicate: characteristic.permissions.indicate().into(),
221                             authenticated_signed_writes: 0,
222                             extended_properties: 0,
223                         },
224                         handle: characteristic.handle.into(),
225                         uuid: characteristic.type_.into(),
226                     }
227                     .into(),
228                 ),
229             );
230 
231             // value
232             add_attribute(
233                 AttAttribute {
234                     handle: characteristic.handle,
235                     type_: characteristic.type_,
236                     permissions: characteristic.permissions,
237                 },
238                 AttAttributeBackingValue::DynamicCharacteristic(datastore.clone()),
239             );
240 
241             // descriptors
242             for descriptor in characteristic.descriptors {
243                 add_attribute(
244                     AttAttribute {
245                         handle: descriptor.handle,
246                         type_: descriptor.type_,
247                         permissions: descriptor.permissions,
248                     },
249                     AttAttributeBackingValue::DynamicDescriptor(datastore.clone()),
250                 );
251             }
252         }
253 
254         // validate attributes for overlap
255         let mut static_data = self.schema.borrow_mut();
256 
257         for handle in attributes.keys() {
258             if static_data.attributes.contains_key(handle) {
259                 bail!("duplicate handle detected");
260             }
261         }
262         if attributes.len() != attribute_cnt {
263             bail!("duplicate handle detected");
264         }
265 
266         // if we made it here, we successfully loaded the new service
267         static_data.attributes.extend(attributes.clone().into_iter());
268 
269         // re-entrancy via the listeners is possible, so we prevent it by dropping here
270         drop(static_data);
271 
272         // notify listeners if any attribute changed
273         let added_handles = attributes.into_iter().map(|attr| attr.0).collect::<Vec<_>>();
274         if !added_handles.is_empty() {
275             for listener in self.listeners.borrow().iter() {
276                 listener.on_service_change(
277                     *added_handles.iter().min().unwrap()..=*added_handles.iter().max().unwrap(),
278                 );
279             }
280         }
281 
282         Ok(())
283     }
284 
285     /// Remove a previously-added service by service handle
remove_service_at_handle(&self, service_handle: AttHandle) -> Result<()>286     pub fn remove_service_at_handle(&self, service_handle: AttHandle) -> Result<()> {
287         let mut static_data = self.schema.borrow_mut();
288 
289         // find next service
290         let next_service_handle = static_data
291             .attributes
292             .values()
293             .find(|attribute| {
294                 attribute.attribute.handle > service_handle
295                     && attribute.attribute.type_ == PRIMARY_SERVICE_DECLARATION_UUID
296             })
297             .map(|service| service.attribute.handle);
298 
299         // predicate matching all handles in our service
300         let in_service_pred = |handle: AttHandle| {
301             service_handle <= handle && next_service_handle.map(|x| handle < x).unwrap_or(true)
302         };
303 
304         // record largest attribute matching predicate
305         let largest_service_handle =
306             static_data.attributes.keys().filter(|handle| in_service_pred(**handle)).max().cloned();
307 
308         // clear out attributes
309         static_data.attributes.retain(|curr_handle, _| !in_service_pred(*curr_handle));
310 
311         // re-entrancy via the listeners is possible, so we prevent it by dropping here
312         drop(static_data);
313 
314         // notify listeners if any attribute changed
315         if let Some(largest_service_handle) = largest_service_handle {
316             for listener in self.listeners.borrow().iter() {
317                 listener.on_service_change(service_handle..=largest_service_handle);
318             }
319         }
320 
321         Ok(())
322     }
323 }
324 
325 impl SharedBox<GattDatabase> {
326     /// Generate an impl AttDatabase from a backing GattDatabase, associated
327     /// with a given connection.
328     ///
329     /// Note: After the AttDatabaseImpl is constructed, we MUST call on_bearer_ready() with
330     /// the resultant bearer, so that the listeners get the correct sequence of callbacks.
get_att_database(&self, tcb_idx: TransportIndex) -> AttDatabaseImpl331     pub fn get_att_database(&self, tcb_idx: TransportIndex) -> AttDatabaseImpl {
332         AttDatabaseImpl { gatt_db: self.downgrade(), tcb_idx }
333     }
334 }
335 
336 /// An implementation of AttDatabase wrapping an underlying GattDatabase
337 pub struct AttDatabaseImpl {
338     gatt_db: WeakBox<GattDatabase>,
339     tcb_idx: TransportIndex,
340 }
341 
342 #[async_trait(?Send)]
343 impl AttDatabase for AttDatabaseImpl {
read_attribute( &self, handle: AttHandle, ) -> Result<AttAttributeDataChild, AttErrorCode>344     async fn read_attribute(
345         &self,
346         handle: AttHandle,
347     ) -> Result<AttAttributeDataChild, AttErrorCode> {
348         let value = self.gatt_db.with(|gatt_db| {
349             let Some(gatt_db) = gatt_db else {
350                 // db must have been closed
351                 return Err(AttErrorCode::INVALID_HANDLE);
352             };
353             let services = gatt_db.schema.borrow();
354             let Some(attr) = services.attributes.get(&handle) else {
355                 return Err(AttErrorCode::INVALID_HANDLE);
356             };
357             if !attr.attribute.permissions.readable() {
358                 return Err(AttErrorCode::READ_NOT_PERMITTED);
359             }
360             Ok(attr.value.clone())
361         })?;
362 
363         match value {
364             AttAttributeBackingValue::Static(val) => return Ok(val),
365             AttAttributeBackingValue::DynamicCharacteristic(datastore) => {
366                 datastore
367                     .read(
368                         self.tcb_idx,
369                         handle,
370                         /* offset */ 0,
371                         AttributeBackingType::Characteristic,
372                     )
373                     .await
374             }
375             AttAttributeBackingValue::DynamicDescriptor(datastore) => {
376                 datastore
377                     .read(
378                         self.tcb_idx,
379                         handle,
380                         /* offset */ 0,
381                         AttributeBackingType::Descriptor,
382                     )
383                     .await
384             }
385         }
386     }
387 
write_attribute( &self, handle: AttHandle, data: AttAttributeDataView<'_>, ) -> Result<(), AttErrorCode>388     async fn write_attribute(
389         &self,
390         handle: AttHandle,
391         data: AttAttributeDataView<'_>,
392     ) -> Result<(), AttErrorCode> {
393         let value = self.gatt_db.with(|gatt_db| {
394             let Some(gatt_db) = gatt_db else {
395                 // db must have been closed
396                 return Err(AttErrorCode::INVALID_HANDLE);
397             };
398             let services = gatt_db.schema.borrow();
399             let Some(attr) = services.attributes.get(&handle) else {
400                 return Err(AttErrorCode::INVALID_HANDLE);
401             };
402             if !attr.attribute.permissions.writable_with_response() {
403                 return Err(AttErrorCode::WRITE_NOT_PERMITTED);
404             }
405             Ok(attr.value.clone())
406         })?;
407 
408         match value {
409             AttAttributeBackingValue::Static(val) => {
410                 error!("A static attribute {val:?} is marked as writable - ignoring it and rejecting the write...");
411                 return Err(AttErrorCode::WRITE_NOT_PERMITTED);
412             }
413             AttAttributeBackingValue::DynamicCharacteristic(datastore) => {
414                 datastore
415                     .write(
416                         self.tcb_idx,
417                         handle,
418                         AttributeBackingType::Characteristic,
419                         GattWriteRequestType::Request,
420                         data,
421                     )
422                     .await
423             }
424             AttAttributeBackingValue::DynamicDescriptor(datastore) => {
425                 datastore
426                     .write(
427                         self.tcb_idx,
428                         handle,
429                         AttributeBackingType::Descriptor,
430                         GattWriteRequestType::Request,
431                         data,
432                     )
433                     .await
434             }
435         }
436     }
437 
write_no_response_attribute(&self, handle: AttHandle, data: AttAttributeDataView<'_>)438     fn write_no_response_attribute(&self, handle: AttHandle, data: AttAttributeDataView<'_>) {
439         let value = self.gatt_db.with(|gatt_db| {
440             let Some(gatt_db) = gatt_db else {
441                 // db must have been closed
442                 return None;
443             };
444             let services = gatt_db.schema.borrow();
445             let Some(attr) = services.attributes.get(&handle) else {
446                 warn!("cannot find handle {handle:?}");
447                 return None;
448             };
449             if !attr.attribute.permissions.writable_without_response() {
450                 warn!("trying to write without response to {handle:?}, which doesn't support it");
451                 return None;
452             }
453             Some(attr.value.clone())
454         });
455 
456         let Some(value) = value else {
457             return;
458         };
459 
460         match value {
461             AttAttributeBackingValue::Static(val) => {
462                 error!("A static attribute {val:?} is marked as writable - ignoring it and rejecting the write...");
463             }
464             AttAttributeBackingValue::DynamicCharacteristic(datastore) => {
465                 datastore.write_no_response(
466                     self.tcb_idx,
467                     handle,
468                     AttributeBackingType::Characteristic,
469                     data,
470                 );
471             }
472             AttAttributeBackingValue::DynamicDescriptor(datastore) => {
473                 datastore.write_no_response(
474                     self.tcb_idx,
475                     handle,
476                     AttributeBackingType::Descriptor,
477                     data,
478                 );
479             }
480         };
481     }
482 
list_attributes(&self) -> Vec<AttAttribute>483     fn list_attributes(&self) -> Vec<AttAttribute> {
484         self.gatt_db.with(|db| {
485             db.map(|db| db.schema.borrow().attributes.values().map(|attr| attr.attribute).collect())
486                 .unwrap_or_default()
487         })
488     }
489 }
490 
491 impl Clone for AttDatabaseImpl {
clone(&self) -> Self492     fn clone(&self) -> Self {
493         Self { gatt_db: self.gatt_db.clone(), tcb_idx: self.tcb_idx }
494     }
495 }
496 
497 impl AttDatabaseImpl {
498     /// When the bearer owning this AttDatabase is invalidated,
499     /// we must notify the listeners tied to our GattDatabase.
500     ///
501     /// Note: AttDatabases referring to the backing GattDatabase
502     /// may still exist after bearer invalidation, but the bearer will
503     /// no longer exist (so packets can no longer be sent/received).
on_bearer_dropped(&self)504     pub fn on_bearer_dropped(&self) {
505         self.gatt_db.with(|db| {
506             db.map(|db| {
507                 for listener in db.listeners.borrow().iter() {
508                     listener.on_le_disconnect(self.tcb_idx)
509                 }
510             })
511         });
512     }
513 }
514 
515 #[cfg(test)]
516 mod test {
517     use tokio::{join, sync::mpsc::error::TryRecvError, task::spawn_local};
518 
519     use crate::{
520         gatt::mocks::{
521             mock_database_callbacks::{MockCallbackEvents, MockCallbacks},
522             mock_datastore::{MockDatastore, MockDatastoreEvents},
523             mock_raw_datastore::{MockRawDatastore, MockRawDatastoreEvents},
524         },
525         packets::Packet,
526         utils::{
527             packet::{build_att_data, build_view_or_crash},
528             task::block_on_locally,
529         },
530     };
531 
532     use super::*;
533 
534     const SERVICE_HANDLE: AttHandle = AttHandle(1);
535     const SERVICE_TYPE: Uuid = Uuid::new(0x1234);
536 
537     const CHARACTERISTIC_DECLARATION_HANDLE: AttHandle = AttHandle(2);
538     const CHARACTERISTIC_VALUE_HANDLE: AttHandle = AttHandle(3);
539     const CHARACTERISTIC_TYPE: Uuid = Uuid::new(0x5678);
540 
541     const DESCRIPTOR_HANDLE: AttHandle = AttHandle(4);
542     const DESCRIPTOR_TYPE: Uuid = Uuid::new(0x9ABC);
543 
544     const TCB_IDX: TransportIndex = TransportIndex(1);
545 
546     #[test]
test_read_empty_db()547     fn test_read_empty_db() {
548         let gatt_db = SharedBox::new(GattDatabase::new());
549         let att_db = gatt_db.get_att_database(TCB_IDX);
550 
551         let resp = tokio_test::block_on(att_db.read_attribute(AttHandle(1)));
552 
553         assert_eq!(resp, Err(AttErrorCode::INVALID_HANDLE))
554     }
555 
556     #[test]
test_single_service()557     fn test_single_service() {
558         let (gatt_datastore, _) = MockDatastore::new();
559         let gatt_db = SharedBox::new(GattDatabase::new());
560         gatt_db
561             .add_service_with_handles(
562                 GattServiceWithHandle {
563                     handle: SERVICE_HANDLE,
564                     type_: SERVICE_TYPE,
565                     characteristics: vec![],
566                 },
567                 Rc::new(gatt_datastore),
568             )
569             .unwrap();
570         let att_db = gatt_db.get_att_database(TCB_IDX);
571 
572         let attrs = att_db.list_attributes();
573         let service_value = tokio_test::block_on(att_db.read_attribute(SERVICE_HANDLE));
574 
575         assert_eq!(
576             attrs,
577             vec![AttAttribute {
578                 handle: SERVICE_HANDLE,
579                 type_: PRIMARY_SERVICE_DECLARATION_UUID,
580                 permissions: AttPermissions::READABLE
581             }]
582         );
583         assert_eq!(
584             service_value,
585             Ok(AttAttributeDataChild::GattServiceDeclarationValue(
586                 GattServiceDeclarationValueBuilder { uuid: SERVICE_TYPE.into() }
587             ))
588         );
589     }
590 
591     #[test]
test_service_removal()592     fn test_service_removal() {
593         // arrange three services, each with a single characteristic
594         let (gatt_datastore, _) = MockDatastore::new();
595         let gatt_datastore = Rc::new(gatt_datastore);
596         let gatt_db = SharedBox::new(GattDatabase::new());
597 
598         gatt_db
599             .add_service_with_handles(
600                 GattServiceWithHandle {
601                     handle: AttHandle(1),
602                     type_: SERVICE_TYPE,
603                     characteristics: vec![GattCharacteristicWithHandle {
604                         handle: AttHandle(3),
605                         type_: CHARACTERISTIC_TYPE,
606                         permissions: AttPermissions::READABLE,
607                         descriptors: vec![],
608                     }],
609                 },
610                 gatt_datastore.clone(),
611             )
612             .unwrap();
613         gatt_db
614             .add_service_with_handles(
615                 GattServiceWithHandle {
616                     handle: AttHandle(4),
617                     type_: SERVICE_TYPE,
618                     characteristics: vec![GattCharacteristicWithHandle {
619                         handle: AttHandle(6),
620                         type_: CHARACTERISTIC_TYPE,
621                         permissions: AttPermissions::READABLE,
622                         descriptors: vec![],
623                     }],
624                 },
625                 gatt_datastore.clone(),
626             )
627             .unwrap();
628         gatt_db
629             .add_service_with_handles(
630                 GattServiceWithHandle {
631                     handle: AttHandle(7),
632                     type_: SERVICE_TYPE,
633                     characteristics: vec![GattCharacteristicWithHandle {
634                         handle: AttHandle(9),
635                         type_: CHARACTERISTIC_TYPE,
636                         permissions: AttPermissions::READABLE,
637                         descriptors: vec![],
638                     }],
639                 },
640                 gatt_datastore,
641             )
642             .unwrap();
643         let att_db = gatt_db.get_att_database(TCB_IDX);
644         assert_eq!(att_db.list_attributes().len(), 9);
645 
646         // act: remove the middle service
647         gatt_db.remove_service_at_handle(AttHandle(4)).unwrap();
648         let attrs = att_db.list_attributes();
649 
650         // assert that the middle service is gone
651         assert_eq!(attrs.len(), 6, "{attrs:?}");
652 
653         // assert the other two old services are still there
654         assert_eq!(
655             attrs[0],
656             AttAttribute {
657                 handle: AttHandle(1),
658                 type_: PRIMARY_SERVICE_DECLARATION_UUID,
659                 permissions: AttPermissions::READABLE
660             }
661         );
662         assert_eq!(
663             attrs[3],
664             AttAttribute {
665                 handle: AttHandle(7),
666                 type_: PRIMARY_SERVICE_DECLARATION_UUID,
667                 permissions: AttPermissions::READABLE
668             }
669         );
670     }
671 
672     #[test]
test_single_characteristic_declaration()673     fn test_single_characteristic_declaration() {
674         let (gatt_datastore, _) = MockDatastore::new();
675         let gatt_db = SharedBox::new(GattDatabase::new());
676         gatt_db
677             .add_service_with_handles(
678                 GattServiceWithHandle {
679                     handle: SERVICE_HANDLE,
680                     type_: SERVICE_TYPE,
681                     characteristics: vec![GattCharacteristicWithHandle {
682                         handle: CHARACTERISTIC_VALUE_HANDLE,
683                         type_: CHARACTERISTIC_TYPE,
684                         permissions: AttPermissions::READABLE
685                             | AttPermissions::WRITABLE_WITH_RESPONSE
686                             | AttPermissions::INDICATE,
687                         descriptors: vec![],
688                     }],
689                 },
690                 Rc::new(gatt_datastore),
691             )
692             .unwrap();
693         let att_db = gatt_db.get_att_database(TCB_IDX);
694 
695         let attrs = att_db.list_attributes();
696         let characteristic_decl =
697             tokio_test::block_on(att_db.read_attribute(CHARACTERISTIC_DECLARATION_HANDLE));
698 
699         assert_eq!(attrs.len(), 3, "{attrs:?}");
700         assert_eq!(attrs[0].type_, PRIMARY_SERVICE_DECLARATION_UUID);
701         assert_eq!(
702             attrs[1],
703             AttAttribute {
704                 handle: CHARACTERISTIC_DECLARATION_HANDLE,
705                 type_: CHARACTERISTIC_UUID,
706                 permissions: AttPermissions::READABLE
707             }
708         );
709         assert_eq!(
710             attrs[2],
711             AttAttribute {
712                 handle: CHARACTERISTIC_VALUE_HANDLE,
713                 type_: CHARACTERISTIC_TYPE,
714                 permissions: AttPermissions::READABLE
715                     | AttPermissions::WRITABLE_WITH_RESPONSE
716                     | AttPermissions::INDICATE
717             }
718         );
719 
720         assert_eq!(
721             characteristic_decl,
722             Ok(AttAttributeDataChild::GattCharacteristicDeclarationValue(
723                 GattCharacteristicDeclarationValueBuilder {
724                     properties: GattCharacteristicPropertiesBuilder {
725                         read: 1,
726                         broadcast: 0,
727                         write_without_response: 0,
728                         write: 1,
729                         notify: 0,
730                         indicate: 1,
731                         authenticated_signed_writes: 0,
732                         extended_properties: 0,
733                     },
734                     handle: CHARACTERISTIC_VALUE_HANDLE.into(),
735                     uuid: CHARACTERISTIC_TYPE.into()
736                 }
737             ))
738         );
739     }
740 
741     #[test]
test_all_characteristic_permissions()742     fn test_all_characteristic_permissions() {
743         // arrange
744         let (gatt_datastore, _) = MockDatastore::new();
745         let gatt_db = SharedBox::new(GattDatabase::new());
746         let att_db = gatt_db.get_att_database(TCB_IDX);
747 
748         // act: add a characteristic with all permission bits set
749         gatt_db
750             .add_service_with_handles(
751                 GattServiceWithHandle {
752                     handle: SERVICE_HANDLE,
753                     type_: SERVICE_TYPE,
754                     characteristics: vec![GattCharacteristicWithHandle {
755                         handle: CHARACTERISTIC_VALUE_HANDLE,
756                         type_: CHARACTERISTIC_TYPE,
757                         permissions: AttPermissions::all(),
758                         descriptors: vec![],
759                     }],
760                 },
761                 Rc::new(gatt_datastore),
762             )
763             .unwrap();
764 
765         // assert: the characteristic declaration has all the bits we support set
766         let characteristic_decl =
767             tokio_test::block_on(att_db.read_attribute(CHARACTERISTIC_DECLARATION_HANDLE));
768         assert_eq!(
769             characteristic_decl,
770             Ok(AttAttributeDataChild::GattCharacteristicDeclarationValue(
771                 GattCharacteristicDeclarationValueBuilder {
772                     properties: GattCharacteristicPropertiesBuilder {
773                         read: 1,
774                         broadcast: 0,
775                         write_without_response: 1,
776                         write: 1,
777                         notify: 0,
778                         indicate: 1,
779                         authenticated_signed_writes: 0,
780                         extended_properties: 0,
781                     },
782                     handle: CHARACTERISTIC_VALUE_HANDLE.into(),
783                     uuid: CHARACTERISTIC_TYPE.into()
784                 }
785             ))
786         );
787     }
788 
789     #[test]
test_single_characteristic_value()790     fn test_single_characteristic_value() {
791         // arrange: create a database with a single characteristic
792         let (gatt_datastore, mut data_evts) = MockDatastore::new();
793         let gatt_db = SharedBox::new(GattDatabase::new());
794         gatt_db
795             .add_service_with_handles(
796                 GattServiceWithHandle {
797                     handle: SERVICE_HANDLE,
798                     type_: SERVICE_TYPE,
799                     characteristics: vec![GattCharacteristicWithHandle {
800                         handle: CHARACTERISTIC_VALUE_HANDLE,
801                         type_: CHARACTERISTIC_TYPE,
802                         permissions: AttPermissions::READABLE,
803                         descriptors: vec![],
804                     }],
805                 },
806                 Rc::new(gatt_datastore),
807             )
808             .unwrap();
809         let att_db = gatt_db.get_att_database(TCB_IDX);
810         let data = AttAttributeDataChild::RawData(Box::new([1, 2]));
811 
812         // act: read from the database, and supply a value from the backing datastore
813         let characteristic_value = tokio_test::block_on(async {
814             join!(
815                 async {
816                     let MockDatastoreEvents::Read(
817                     TCB_IDX,
818                     CHARACTERISTIC_VALUE_HANDLE,
819                     AttributeBackingType::Characteristic,
820                     reply,
821                 ) = data_evts.recv().await.unwrap() else {
822                     unreachable!()
823                 };
824                     reply.send(Ok(data.clone())).unwrap();
825                 },
826                 att_db.read_attribute(CHARACTERISTIC_VALUE_HANDLE)
827             )
828             .1
829         });
830 
831         // assert: the supplied value matches what the att datastore returned
832         assert_eq!(characteristic_value, Ok(data));
833     }
834 
835     #[test]
test_unreadable_characteristic()836     fn test_unreadable_characteristic() {
837         let (gatt_datastore, _) = MockDatastore::new();
838         let gatt_db = SharedBox::new(GattDatabase::new());
839         gatt_db
840             .add_service_with_handles(
841                 GattServiceWithHandle {
842                     handle: SERVICE_HANDLE,
843                     type_: SERVICE_TYPE,
844                     characteristics: vec![GattCharacteristicWithHandle {
845                         handle: CHARACTERISTIC_VALUE_HANDLE,
846                         type_: CHARACTERISTIC_TYPE,
847                         permissions: AttPermissions::empty(),
848                         descriptors: vec![],
849                     }],
850                 },
851                 Rc::new(gatt_datastore),
852             )
853             .unwrap();
854 
855         let characteristic_value = tokio_test::block_on(
856             gatt_db.get_att_database(TCB_IDX).read_attribute(CHARACTERISTIC_VALUE_HANDLE),
857         );
858 
859         assert_eq!(characteristic_value, Err(AttErrorCode::READ_NOT_PERMITTED));
860     }
861 
862     #[test]
test_handle_clash()863     fn test_handle_clash() {
864         let (gatt_datastore, _) = MockDatastore::new();
865         let gatt_db = SharedBox::new(GattDatabase::new());
866 
867         let result = gatt_db.add_service_with_handles(
868             GattServiceWithHandle {
869                 handle: SERVICE_HANDLE,
870                 type_: SERVICE_TYPE,
871                 characteristics: vec![GattCharacteristicWithHandle {
872                     handle: SERVICE_HANDLE,
873                     type_: CHARACTERISTIC_TYPE,
874                     permissions: AttPermissions::WRITABLE_WITH_RESPONSE,
875                     descriptors: vec![],
876                 }],
877             },
878             Rc::new(gatt_datastore),
879         );
880 
881         assert!(result.is_err());
882     }
883 
884     #[test]
test_handle_clash_with_existing()885     fn test_handle_clash_with_existing() {
886         let (gatt_datastore, _) = MockDatastore::new();
887         let gatt_datastore = Rc::new(gatt_datastore);
888         let gatt_db = Rc::new(GattDatabase::new());
889 
890         gatt_db
891             .add_service_with_handles(
892                 GattServiceWithHandle {
893                     handle: SERVICE_HANDLE,
894                     type_: SERVICE_TYPE,
895                     characteristics: vec![],
896                 },
897                 gatt_datastore.clone(),
898             )
899             .unwrap();
900 
901         let result = gatt_db.add_service_with_handles(
902             GattServiceWithHandle {
903                 handle: SERVICE_HANDLE,
904                 type_: SERVICE_TYPE,
905                 characteristics: vec![],
906             },
907             gatt_datastore,
908         );
909 
910         assert!(result.is_err());
911     }
912 
913     #[test]
test_write_single_characteristic_callback_invoked()914     fn test_write_single_characteristic_callback_invoked() {
915         // arrange: create a database with a single characteristic
916         let (gatt_datastore, mut data_evts) = MockDatastore::new();
917         let gatt_db = SharedBox::new(GattDatabase::new());
918         gatt_db
919             .add_service_with_handles(
920                 GattServiceWithHandle {
921                     handle: SERVICE_HANDLE,
922                     type_: SERVICE_TYPE,
923                     characteristics: vec![GattCharacteristicWithHandle {
924                         handle: CHARACTERISTIC_VALUE_HANDLE,
925                         type_: CHARACTERISTIC_TYPE,
926                         permissions: AttPermissions::WRITABLE_WITH_RESPONSE,
927                         descriptors: vec![],
928                     }],
929                 },
930                 Rc::new(gatt_datastore),
931             )
932             .unwrap();
933         let att_db = gatt_db.get_att_database(TCB_IDX);
934         let data =
935             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
936 
937         // act: write to the database
938         let recv_data = block_on_locally(async {
939             // start write task
940             let cloned_data = data.view().to_owned_packet();
941             spawn_local(async move {
942                 att_db
943                     .write_attribute(CHARACTERISTIC_VALUE_HANDLE, cloned_data.view())
944                     .await
945                     .unwrap();
946             });
947 
948             let MockDatastoreEvents::Write(
949                 TCB_IDX,
950                 CHARACTERISTIC_VALUE_HANDLE,
951                 AttributeBackingType::Characteristic,
952                 recv_data,
953                 _,
954             ) = data_evts.recv().await.unwrap() else {
955                 unreachable!();
956             };
957             recv_data
958         });
959 
960         // assert: the received value matches what we supplied
961         assert_eq!(
962             recv_data.view().get_raw_payload().collect::<Vec<_>>(),
963             data.view().get_raw_payload().collect::<Vec<_>>()
964         );
965     }
966 
967     #[test]
test_write_single_characteristic_recv_response()968     fn test_write_single_characteristic_recv_response() {
969         // arrange: create a database with a single characteristic
970         let (gatt_datastore, mut data_evts) = MockDatastore::new();
971         let gatt_db = SharedBox::new(GattDatabase::new());
972         gatt_db
973             .add_service_with_handles(
974                 GattServiceWithHandle {
975                     handle: SERVICE_HANDLE,
976                     type_: SERVICE_TYPE,
977                     characteristics: vec![GattCharacteristicWithHandle {
978                         handle: CHARACTERISTIC_VALUE_HANDLE,
979                         type_: CHARACTERISTIC_TYPE,
980                         permissions: AttPermissions::WRITABLE_WITH_RESPONSE,
981                         descriptors: vec![],
982                     }],
983                 },
984                 Rc::new(gatt_datastore),
985             )
986             .unwrap();
987         let att_db = gatt_db.get_att_database(TCB_IDX);
988         let data =
989             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
990 
991         // act: write to the database
992         let res = tokio_test::block_on(async {
993             join!(
994                 async {
995                     let MockDatastoreEvents::Write(_,_,_,_,reply) = data_evts.recv().await.unwrap() else {
996                         unreachable!();
997                     };
998                     reply.send(Err(AttErrorCode::UNLIKELY_ERROR)).unwrap();
999                 },
1000                 att_db.write_attribute(CHARACTERISTIC_VALUE_HANDLE, data.view())
1001             )
1002             .1
1003         });
1004 
1005         // assert: the supplied value matches what the att datastore returned
1006         assert_eq!(res, Err(AttErrorCode::UNLIKELY_ERROR));
1007     }
1008 
1009     #[test]
test_unwriteable_characteristic()1010     fn test_unwriteable_characteristic() {
1011         let (gatt_datastore, _) = MockDatastore::new();
1012         let gatt_db = SharedBox::new(GattDatabase::new());
1013         gatt_db
1014             .add_service_with_handles(
1015                 GattServiceWithHandle {
1016                     handle: SERVICE_HANDLE,
1017                     type_: SERVICE_TYPE,
1018                     characteristics: vec![GattCharacteristicWithHandle {
1019                         handle: CHARACTERISTIC_VALUE_HANDLE,
1020                         type_: CHARACTERISTIC_TYPE,
1021                         permissions: AttPermissions::READABLE,
1022                         descriptors: vec![],
1023                     }],
1024                 },
1025                 Rc::new(gatt_datastore),
1026             )
1027             .unwrap();
1028         let data =
1029             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
1030 
1031         let characteristic_value = tokio_test::block_on(
1032             gatt_db
1033                 .get_att_database(TCB_IDX)
1034                 .write_attribute(CHARACTERISTIC_VALUE_HANDLE, data.view()),
1035         );
1036 
1037         assert_eq!(characteristic_value, Err(AttErrorCode::WRITE_NOT_PERMITTED));
1038     }
1039 
1040     #[test]
test_single_descriptor_declaration()1041     fn test_single_descriptor_declaration() {
1042         let (gatt_datastore, mut data_evts) = MockDatastore::new();
1043         let gatt_db = SharedBox::new(GattDatabase::new());
1044         gatt_db
1045             .add_service_with_handles(
1046                 GattServiceWithHandle {
1047                     handle: SERVICE_HANDLE,
1048                     type_: SERVICE_TYPE,
1049                     characteristics: vec![GattCharacteristicWithHandle {
1050                         handle: CHARACTERISTIC_VALUE_HANDLE,
1051                         type_: CHARACTERISTIC_TYPE,
1052                         permissions: AttPermissions::READABLE,
1053                         descriptors: vec![GattDescriptorWithHandle {
1054                             handle: DESCRIPTOR_HANDLE,
1055                             type_: DESCRIPTOR_TYPE,
1056                             permissions: AttPermissions::READABLE,
1057                         }],
1058                     }],
1059                 },
1060                 Rc::new(gatt_datastore),
1061             )
1062             .unwrap();
1063         let att_db = gatt_db.get_att_database(TCB_IDX);
1064         let data = AttAttributeDataChild::RawData(Box::new([1, 2]));
1065 
1066         let descriptor_value = block_on_locally(async {
1067             // start write task
1068             let pending_read =
1069                 spawn_local(async move { att_db.read_attribute(DESCRIPTOR_HANDLE).await.unwrap() });
1070 
1071             let MockDatastoreEvents::Read(
1072                 TCB_IDX,
1073                 DESCRIPTOR_HANDLE,
1074                 AttributeBackingType::Descriptor,
1075                 reply,
1076             ) = data_evts.recv().await.unwrap() else {
1077                 unreachable!();
1078             };
1079 
1080             reply.send(Ok(data.clone())).unwrap();
1081 
1082             pending_read.await.unwrap()
1083         });
1084 
1085         assert_eq!(descriptor_value, data);
1086     }
1087 
1088     #[test]
test_write_descriptor()1089     fn test_write_descriptor() {
1090         // arrange: db with a writable descriptor
1091         let (gatt_datastore, mut data_evts) = MockDatastore::new();
1092         let gatt_db = SharedBox::new(GattDatabase::new());
1093         gatt_db
1094             .add_service_with_handles(
1095                 GattServiceWithHandle {
1096                     handle: SERVICE_HANDLE,
1097                     type_: SERVICE_TYPE,
1098                     characteristics: vec![GattCharacteristicWithHandle {
1099                         handle: CHARACTERISTIC_VALUE_HANDLE,
1100                         type_: CHARACTERISTIC_TYPE,
1101                         permissions: AttPermissions::READABLE,
1102                         descriptors: vec![GattDescriptorWithHandle {
1103                             handle: DESCRIPTOR_HANDLE,
1104                             type_: DESCRIPTOR_TYPE,
1105                             permissions: AttPermissions::WRITABLE_WITH_RESPONSE,
1106                         }],
1107                     }],
1108                 },
1109                 Rc::new(gatt_datastore),
1110             )
1111             .unwrap();
1112         let att_db = gatt_db.get_att_database(TCB_IDX);
1113         let data =
1114             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
1115 
1116         // act: write, and wait for the callback to be invoked
1117         block_on_locally(async {
1118             // start write task
1119             spawn_local(async move {
1120                 att_db.write_attribute(DESCRIPTOR_HANDLE, data.view()).await.unwrap()
1121             });
1122 
1123             let MockDatastoreEvents::Write(
1124                 TCB_IDX,
1125                 DESCRIPTOR_HANDLE,
1126                 AttributeBackingType::Descriptor,
1127                 _,
1128                 _,
1129             ) = data_evts.recv().await.unwrap() else {
1130                 unreachable!();
1131             };
1132         });
1133 
1134         // assert: nothing, if we reach this far we are OK
1135     }
1136 
1137     #[test]
test_multiple_descriptors()1138     fn test_multiple_descriptors() {
1139         // arrange: a database with some characteristics and descriptors
1140         let (gatt_datastore, _) = MockDatastore::new();
1141         let gatt_db = SharedBox::new(GattDatabase::new());
1142         gatt_db
1143             .add_service_with_handles(
1144                 GattServiceWithHandle {
1145                     handle: AttHandle(1),
1146                     type_: SERVICE_TYPE,
1147                     characteristics: vec![
1148                         GattCharacteristicWithHandle {
1149                             handle: AttHandle(3),
1150                             type_: CHARACTERISTIC_TYPE,
1151                             permissions: AttPermissions::READABLE,
1152                             descriptors: vec![GattDescriptorWithHandle {
1153                                 handle: AttHandle(4),
1154                                 type_: DESCRIPTOR_TYPE,
1155                                 permissions: AttPermissions::READABLE,
1156                             }],
1157                         },
1158                         GattCharacteristicWithHandle {
1159                             handle: AttHandle(6),
1160                             type_: CHARACTERISTIC_TYPE,
1161                             permissions: AttPermissions::READABLE,
1162                             descriptors: vec![
1163                                 GattDescriptorWithHandle {
1164                                     handle: AttHandle(7),
1165                                     type_: DESCRIPTOR_TYPE,
1166                                     permissions: AttPermissions::WRITABLE_WITH_RESPONSE,
1167                                 },
1168                                 GattDescriptorWithHandle {
1169                                     handle: AttHandle(8),
1170                                     type_: DESCRIPTOR_TYPE,
1171                                     permissions: AttPermissions::READABLE
1172                                         | AttPermissions::WRITABLE_WITH_RESPONSE,
1173                                 },
1174                             ],
1175                         },
1176                     ],
1177                 },
1178                 Rc::new(gatt_datastore),
1179             )
1180             .unwrap();
1181 
1182         // act: get the attributes
1183         let attributes = gatt_db.get_att_database(TCB_IDX).list_attributes();
1184 
1185         // assert: check the attributes are in the correct order
1186         assert_eq!(attributes.len(), 8);
1187         assert_eq!(attributes[0].type_, PRIMARY_SERVICE_DECLARATION_UUID);
1188         assert_eq!(attributes[1].type_, CHARACTERISTIC_UUID);
1189         assert_eq!(attributes[2].type_, CHARACTERISTIC_TYPE);
1190         assert_eq!(attributes[3].type_, DESCRIPTOR_TYPE);
1191         assert_eq!(attributes[4].type_, CHARACTERISTIC_UUID);
1192         assert_eq!(attributes[5].type_, CHARACTERISTIC_TYPE);
1193         assert_eq!(attributes[6].type_, DESCRIPTOR_TYPE);
1194         assert_eq!(attributes[7].type_, DESCRIPTOR_TYPE);
1195         // assert: check the handles of the descriptors are correct
1196         assert_eq!(attributes[3].handle, AttHandle(4));
1197         assert_eq!(attributes[6].handle, AttHandle(7));
1198         assert_eq!(attributes[7].handle, AttHandle(8));
1199         // assert: check the permissions of the descriptors are correct
1200         assert_eq!(attributes[3].permissions, AttPermissions::READABLE);
1201         assert_eq!(attributes[6].permissions, AttPermissions::WRITABLE_WITH_RESPONSE);
1202         assert_eq!(
1203             attributes[7].permissions,
1204             AttPermissions::READABLE | AttPermissions::WRITABLE_WITH_RESPONSE
1205         );
1206     }
1207 
1208     #[test]
test_multiple_datastores()1209     fn test_multiple_datastores() {
1210         // arrange: create a database with two services backed by different datastores
1211         let gatt_db = SharedBox::new(GattDatabase::new());
1212 
1213         let (gatt_datastore_1, mut data_evts_1) = MockDatastore::new();
1214         gatt_db
1215             .add_service_with_handles(
1216                 GattServiceWithHandle {
1217                     handle: AttHandle(1),
1218                     type_: SERVICE_TYPE,
1219                     characteristics: vec![GattCharacteristicWithHandle {
1220                         handle: AttHandle(3),
1221                         type_: CHARACTERISTIC_TYPE,
1222                         permissions: AttPermissions::READABLE,
1223                         descriptors: vec![],
1224                     }],
1225                 },
1226                 Rc::new(gatt_datastore_1),
1227             )
1228             .unwrap();
1229 
1230         let (gatt_datastore_2, mut data_evts_2) = MockDatastore::new();
1231         gatt_db
1232             .add_service_with_handles(
1233                 GattServiceWithHandle {
1234                     handle: AttHandle(4),
1235                     type_: SERVICE_TYPE,
1236                     characteristics: vec![GattCharacteristicWithHandle {
1237                         handle: AttHandle(6),
1238                         type_: CHARACTERISTIC_TYPE,
1239                         permissions: AttPermissions::READABLE,
1240                         descriptors: vec![],
1241                     }],
1242                 },
1243                 Rc::new(gatt_datastore_2),
1244             )
1245             .unwrap();
1246 
1247         let att_db = gatt_db.get_att_database(TCB_IDX);
1248         let data = AttAttributeDataChild::RawData(Box::new([1, 2]));
1249 
1250         // act: read from the second characteristic and supply a response from the second datastore
1251         let characteristic_value = tokio_test::block_on(async {
1252             join!(
1253                 async {
1254                     let MockDatastoreEvents::Read(
1255                     TCB_IDX,
1256                     AttHandle(6),
1257                     AttributeBackingType::Characteristic,
1258                     reply,
1259                 ) = data_evts_2.recv().await.unwrap() else {
1260                     unreachable!()
1261                 };
1262                     reply.send(Ok(data.clone())).unwrap();
1263                 },
1264                 att_db.read_attribute(AttHandle(6))
1265             )
1266             .1
1267         });
1268 
1269         // assert: the supplied value matches what the att datastore returned
1270         assert_eq!(characteristic_value, Ok(data));
1271         // the first datastore received no events
1272         assert_eq!(data_evts_1.try_recv().unwrap_err(), TryRecvError::Empty);
1273         // the second datastore has no remaining events
1274         assert_eq!(data_evts_2.try_recv().unwrap_err(), TryRecvError::Empty);
1275     }
1276 
make_bearer( gatt_db: &SharedBox<GattDatabase>, ) -> SharedBox<AttServerBearer<AttDatabaseImpl>>1277     fn make_bearer(
1278         gatt_db: &SharedBox<GattDatabase>,
1279     ) -> SharedBox<AttServerBearer<AttDatabaseImpl>> {
1280         SharedBox::new(AttServerBearer::new(gatt_db.get_att_database(TCB_IDX), |_| {
1281             unreachable!();
1282         }))
1283     }
1284 
1285     #[test]
test_connection_listener()1286     fn test_connection_listener() {
1287         // arrange: db with a listener
1288         let gatt_db = SharedBox::new(GattDatabase::new());
1289         let (callbacks, mut rx) = MockCallbacks::new();
1290         gatt_db.register_listener(Rc::new(callbacks));
1291         let bearer = make_bearer(&gatt_db);
1292 
1293         // act: open a connection
1294         gatt_db.on_bearer_ready(TCB_IDX, bearer.as_ref());
1295 
1296         // assert: we got the callback
1297         let event = rx.blocking_recv().unwrap();
1298         assert!(matches!(event, MockCallbackEvents::OnLeConnect(TCB_IDX, _)));
1299     }
1300 
1301     #[test]
test_disconnection_listener()1302     fn test_disconnection_listener() {
1303         // arrange: db with a listener
1304         let gatt_db = SharedBox::new(GattDatabase::new());
1305         let (callbacks, mut rx) = MockCallbacks::new();
1306         gatt_db.register_listener(Rc::new(callbacks));
1307 
1308         // act: disconnect
1309         gatt_db.on_bearer_dropped(TCB_IDX);
1310 
1311         // assert: we got the callback
1312         let event = rx.blocking_recv().unwrap();
1313         assert!(matches!(event, MockCallbackEvents::OnLeDisconnect(TCB_IDX)));
1314     }
1315 
1316     #[test]
test_multiple_listeners()1317     fn test_multiple_listeners() {
1318         // arrange: db with two listeners
1319         let gatt_db = SharedBox::new(GattDatabase::new());
1320         let (callbacks1, mut rx1) = MockCallbacks::new();
1321         gatt_db.register_listener(Rc::new(callbacks1));
1322         let (callbacks2, mut rx2) = MockCallbacks::new();
1323         gatt_db.register_listener(Rc::new(callbacks2));
1324 
1325         // act: disconnect
1326         gatt_db.on_bearer_dropped(TCB_IDX);
1327 
1328         // assert: we got the callback on both listeners
1329         let event = rx1.blocking_recv().unwrap();
1330         assert!(matches!(event, MockCallbackEvents::OnLeDisconnect(TCB_IDX)));
1331         let event = rx2.blocking_recv().unwrap();
1332         assert!(matches!(event, MockCallbackEvents::OnLeDisconnect(TCB_IDX)));
1333     }
1334 
1335     #[test]
test_add_service_changed_listener()1336     fn test_add_service_changed_listener() {
1337         // arrange: db with a listener
1338         let gatt_db = SharedBox::new(GattDatabase::new());
1339         let (callbacks, mut rx) = MockCallbacks::new();
1340         let (datastore, _) = MockDatastore::new();
1341 
1342         // act: start listening and add a new service
1343         gatt_db.register_listener(Rc::new(callbacks));
1344         gatt_db
1345             .add_service_with_handles(
1346                 GattServiceWithHandle {
1347                     handle: AttHandle(4),
1348                     type_: SERVICE_TYPE,
1349                     characteristics: vec![GattCharacteristicWithHandle {
1350                         handle: AttHandle(6),
1351                         type_: CHARACTERISTIC_TYPE,
1352                         permissions: AttPermissions::empty(),
1353                         descriptors: vec![],
1354                     }],
1355                 },
1356                 Rc::new(datastore),
1357             )
1358             .unwrap();
1359 
1360         // assert: we got the callback
1361         let event = rx.blocking_recv().unwrap();
1362         let MockCallbackEvents::OnServiceChange(range) = event else {
1363             unreachable!();
1364         };
1365         assert_eq!(*range.start(), AttHandle(4));
1366         assert_eq!(*range.end(), AttHandle(6));
1367     }
1368 
1369     #[test]
test_partial_remove_service_changed_listener()1370     fn test_partial_remove_service_changed_listener() {
1371         // arrange: db with two services and a listener
1372         let gatt_db = SharedBox::new(GattDatabase::new());
1373         let (callbacks, mut rx) = MockCallbacks::new();
1374         let (datastore, _) = MockDatastore::new();
1375         let datastore = Rc::new(datastore);
1376         gatt_db
1377             .add_service_with_handles(
1378                 GattServiceWithHandle {
1379                     handle: AttHandle(4),
1380                     type_: SERVICE_TYPE,
1381                     characteristics: vec![GattCharacteristicWithHandle {
1382                         handle: AttHandle(6),
1383                         type_: CHARACTERISTIC_TYPE,
1384                         permissions: AttPermissions::empty(),
1385                         descriptors: vec![],
1386                     }],
1387                 },
1388                 datastore.clone(),
1389             )
1390             .unwrap();
1391         gatt_db
1392             .add_service_with_handles(
1393                 GattServiceWithHandle {
1394                     handle: AttHandle(8),
1395                     type_: SERVICE_TYPE,
1396                     characteristics: vec![GattCharacteristicWithHandle {
1397                         handle: AttHandle(10),
1398                         type_: CHARACTERISTIC_TYPE,
1399                         permissions: AttPermissions::empty(),
1400                         descriptors: vec![],
1401                     }],
1402                 },
1403                 datastore,
1404             )
1405             .unwrap();
1406 
1407         // act: start listening and remove the first service
1408         gatt_db.register_listener(Rc::new(callbacks));
1409         gatt_db.remove_service_at_handle(AttHandle(4)).unwrap();
1410 
1411         // assert: we got the callback
1412         let event = rx.blocking_recv().unwrap();
1413         let MockCallbackEvents::OnServiceChange(range) = event else {
1414             unreachable!();
1415         };
1416         assert_eq!(*range.start(), AttHandle(4));
1417         assert_eq!(*range.end(), AttHandle(6));
1418     }
1419 
1420     #[test]
test_full_remove_service_changed_listener()1421     fn test_full_remove_service_changed_listener() {
1422         // arrange: db with a listener and a service
1423         let gatt_db = SharedBox::new(GattDatabase::new());
1424         let (callbacks, mut rx) = MockCallbacks::new();
1425         let (datastore, _) = MockDatastore::new();
1426         gatt_db
1427             .add_service_with_handles(
1428                 GattServiceWithHandle {
1429                     handle: AttHandle(4),
1430                     type_: SERVICE_TYPE,
1431                     characteristics: vec![GattCharacteristicWithHandle {
1432                         handle: AttHandle(6),
1433                         type_: CHARACTERISTIC_TYPE,
1434                         permissions: AttPermissions::empty(),
1435                         descriptors: vec![],
1436                     }],
1437                 },
1438                 Rc::new(datastore),
1439             )
1440             .unwrap();
1441 
1442         // act: start listening and remove the service
1443         gatt_db.register_listener(Rc::new(callbacks));
1444         gatt_db.remove_service_at_handle(AttHandle(4)).unwrap();
1445 
1446         // assert: we got the callback
1447         let event = rx.blocking_recv().unwrap();
1448         let MockCallbackEvents::OnServiceChange(range) = event else {
1449             unreachable!();
1450         };
1451         assert_eq!(*range.start(), AttHandle(4));
1452         assert_eq!(*range.end(), AttHandle(6));
1453     }
1454 
1455     #[test]
test_trivial_remove_service_changed_listener()1456     fn test_trivial_remove_service_changed_listener() {
1457         // arrange: db with a listener and a trivial service
1458         let gatt_db = SharedBox::new(GattDatabase::new());
1459         let (callbacks, mut rx) = MockCallbacks::new();
1460         let (datastore, _) = MockDatastore::new();
1461         gatt_db
1462             .add_service_with_handles(
1463                 GattServiceWithHandle {
1464                     handle: AttHandle(4),
1465                     type_: SERVICE_TYPE,
1466                     characteristics: vec![],
1467                 },
1468                 Rc::new(datastore),
1469             )
1470             .unwrap();
1471 
1472         // act: start listening and remove the service
1473         gatt_db.register_listener(Rc::new(callbacks));
1474         gatt_db.remove_service_at_handle(AttHandle(4)).unwrap();
1475 
1476         // assert: we got the callback
1477         let event = rx.blocking_recv().unwrap();
1478         let MockCallbackEvents::OnServiceChange(range) = event else {
1479             unreachable!();
1480         };
1481         assert_eq!(*range.start(), AttHandle(4));
1482         assert_eq!(*range.end(), AttHandle(4));
1483     }
1484 
1485     #[test]
test_write_no_response_single_characteristic()1486     fn test_write_no_response_single_characteristic() {
1487         // arrange: create a database with a single characteristic
1488         let (gatt_datastore, mut data_evts) = MockRawDatastore::new();
1489         let gatt_db = SharedBox::new(GattDatabase::new());
1490         gatt_db
1491             .add_service_with_handles(
1492                 GattServiceWithHandle {
1493                     handle: SERVICE_HANDLE,
1494                     type_: SERVICE_TYPE,
1495                     characteristics: vec![GattCharacteristicWithHandle {
1496                         handle: CHARACTERISTIC_VALUE_HANDLE,
1497                         type_: CHARACTERISTIC_TYPE,
1498                         permissions: AttPermissions::WRITABLE_WITHOUT_RESPONSE,
1499                         descriptors: vec![],
1500                     }],
1501                 },
1502                 Rc::new(gatt_datastore),
1503             )
1504             .unwrap();
1505         let att_db = gatt_db.get_att_database(TCB_IDX);
1506         let data =
1507             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
1508 
1509         // act: write without response to the database
1510         att_db.write_no_response_attribute(CHARACTERISTIC_VALUE_HANDLE, data.view());
1511 
1512         // assert: we got a callback
1513         let event = data_evts.blocking_recv().unwrap();
1514         let MockRawDatastoreEvents::WriteNoResponse(TCB_IDX, CHARACTERISTIC_VALUE_HANDLE, AttributeBackingType::Characteristic, recv_data) = event else {
1515             unreachable!("{event:?}");
1516         };
1517         assert_eq!(
1518             recv_data.view().get_raw_payload().collect::<Vec<_>>(),
1519             data.view().get_raw_payload().collect::<Vec<_>>()
1520         );
1521     }
1522 
1523     #[test]
test_unwriteable_without_response_characteristic()1524     fn test_unwriteable_without_response_characteristic() {
1525         // arrange: db with a characteristic that is writable, but not writable-without-response
1526         let (gatt_datastore, mut data_events) = MockRawDatastore::new();
1527         let gatt_db = SharedBox::new(GattDatabase::new());
1528         gatt_db
1529             .add_service_with_handles(
1530                 GattServiceWithHandle {
1531                     handle: SERVICE_HANDLE,
1532                     type_: SERVICE_TYPE,
1533                     characteristics: vec![GattCharacteristicWithHandle {
1534                         handle: CHARACTERISTIC_VALUE_HANDLE,
1535                         type_: CHARACTERISTIC_TYPE,
1536                         permissions: AttPermissions::READABLE
1537                             | AttPermissions::WRITABLE_WITH_RESPONSE,
1538                         descriptors: vec![],
1539                     }],
1540                 },
1541                 Rc::new(gatt_datastore),
1542             )
1543             .unwrap();
1544         let att_db = gatt_db.get_att_database(TCB_IDX);
1545         let data =
1546             build_view_or_crash(build_att_data(AttAttributeDataChild::RawData(Box::new([1, 2]))));
1547 
1548         // act: try writing without response to this characteristic
1549         att_db.write_no_response_attribute(CHARACTERISTIC_VALUE_HANDLE, data.view());
1550 
1551         // assert: no callback was sent
1552         assert_eq!(data_events.try_recv().unwrap_err(), TryRecvError::Empty);
1553     }
1554 }
1555