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