1 //! The GATT service as defined in Core Spec 5.3 Vol 3G Section 7
2 
3 use std::{cell::RefCell, collections::HashMap, ops::RangeInclusive, rc::Rc};
4 
5 use anyhow::Result;
6 use async_trait::async_trait;
7 use log::{error, warn};
8 use tokio::task::spawn_local;
9 
10 use crate::{
11     core::{
12         shared_box::{WeakBox, WeakBoxRef},
13         uuid::Uuid,
14     },
15     gatt::{
16         callbacks::GattDatastore,
17         ffi::AttributeBackingType,
18         ids::{AttHandle, TransportIndex},
19         server::{
20             att_server_bearer::AttServerBearer,
21             gatt_database::{
22                 AttDatabaseImpl, AttPermissions, GattCharacteristicWithHandle, GattDatabase,
23                 GattDatabaseCallbacks, GattDescriptorWithHandle, GattServiceWithHandle,
24             },
25         },
26     },
27     packets::{
28         AttErrorCode, GattClientCharacteristicConfigurationBuilder,
29         GattClientCharacteristicConfigurationView, GattServiceChangedBuilder, Packet, Serializable,
30     },
31 };
32 
33 #[derive(Default)]
34 struct GattService {
35     clients: RefCell<HashMap<TransportIndex, ClientState>>,
36 }
37 
38 #[derive(Clone)]
39 struct ClientState {
40     bearer: WeakBox<AttServerBearer<AttDatabaseImpl>>,
41     registered_for_service_change: bool,
42 }
43 
44 // Must lie in the range specified by GATT_GATT_START_HANDLE from legacy stack
45 const GATT_SERVICE_HANDLE: AttHandle = AttHandle(1);
46 const SERVICE_CHANGE_HANDLE: AttHandle = AttHandle(3);
47 const SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE: AttHandle = AttHandle(4);
48 
49 /// The UUID used for the GATT service (Assigned Numbers 3.4.1 Services by Name)
50 pub const GATT_SERVICE_UUID: Uuid = Uuid::new(0x1801);
51 /// The UUID used for the Service Changed characteristic (Assigned Numbers 3.8.1 Characteristics by Name)
52 pub const SERVICE_CHANGE_UUID: Uuid = Uuid::new(0x2A05);
53 /// The UUID used for the Client Characteristic Configuration descriptor (Assigned Numbers 3.7 Descriptors)
54 pub const CLIENT_CHARACTERISTIC_CONFIGURATION_UUID: Uuid = Uuid::new(0x2902);
55 
56 #[async_trait(?Send)]
57 impl GattDatastore for GattService {
read( &self, tcb_idx: TransportIndex, handle: AttHandle, _: AttributeBackingType, ) -> Result<Vec<u8>, AttErrorCode>58     async fn read(
59         &self,
60         tcb_idx: TransportIndex,
61         handle: AttHandle,
62         _: AttributeBackingType,
63     ) -> Result<Vec<u8>, AttErrorCode> {
64         if handle == SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE {
65             GattClientCharacteristicConfigurationBuilder {
66                 notification: 0,
67                 indication: self
68                     .clients
69                     .borrow()
70                     .get(&tcb_idx)
71                     .map(|state| state.registered_for_service_change)
72                     .unwrap_or(false)
73                     .into(),
74             }
75             .to_vec()
76             .map_err(|_| AttErrorCode::UNLIKELY_ERROR)
77         } else {
78             unreachable!()
79         }
80     }
81 
write( &self, tcb_idx: TransportIndex, handle: AttHandle, _: AttributeBackingType, data: &[u8], ) -> Result<(), AttErrorCode>82     async fn write(
83         &self,
84         tcb_idx: TransportIndex,
85         handle: AttHandle,
86         _: AttributeBackingType,
87         data: &[u8],
88     ) -> Result<(), AttErrorCode> {
89         if handle == SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE {
90             let ccc = GattClientCharacteristicConfigurationView::try_parse_from_buffer(data)
91                 .map_err(|err| {
92                     warn!("failed to parse CCC descriptor, got: {err:?}");
93                     AttErrorCode::APPLICATION_ERROR
94                 })?;
95             let mut clients = self.clients.borrow_mut();
96             let state = clients.get_mut(&tcb_idx);
97             let Some(state) = state else {
98                 error!("Received write request from disconnected client...");
99                 return Err(AttErrorCode::UNLIKELY_ERROR);
100             };
101             state.registered_for_service_change = ccc.get_indication() != 0;
102             Ok(())
103         } else {
104             unreachable!()
105         }
106     }
107 }
108 
109 impl GattDatabaseCallbacks for GattService {
on_le_connect( &self, tcb_idx: TransportIndex, bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>, )110     fn on_le_connect(
111         &self,
112         tcb_idx: TransportIndex,
113         bearer: WeakBoxRef<AttServerBearer<AttDatabaseImpl>>,
114     ) {
115         // TODO(aryarahul): registered_for_service_change may not be false for bonded devices
116         self.clients.borrow_mut().insert(
117             tcb_idx,
118             ClientState { bearer: bearer.downgrade(), registered_for_service_change: false },
119         );
120     }
121 
on_le_disconnect(&self, tcb_idx: TransportIndex)122     fn on_le_disconnect(&self, tcb_idx: TransportIndex) {
123         self.clients.borrow_mut().remove(&tcb_idx);
124     }
125 
on_service_change(&self, range: RangeInclusive<AttHandle>)126     fn on_service_change(&self, range: RangeInclusive<AttHandle>) {
127         for (conn_id, client) in self.clients.borrow().clone() {
128             if client.registered_for_service_change {
129                 client.bearer.with(|bearer| match bearer {
130                     Some(bearer) => {
131                         spawn_local(
132                             bearer.send_indication(
133                                 SERVICE_CHANGE_HANDLE,
134                                 GattServiceChangedBuilder {
135                                     start_handle: (*range.start()).into(),
136                                     end_handle: (*range.end()).into(),
137                                 }
138                                 .into(),
139                             ),
140                         );
141                     }
142                     None => {
143                         error!("Registered client's bearer has been destructed ({conn_id:?})")
144                     }
145                 });
146             }
147         }
148     }
149 }
150 
151 /// Register the GATT service in the provided GATT database.
register_gatt_service(database: &mut GattDatabase) -> Result<()>152 pub fn register_gatt_service(database: &mut GattDatabase) -> Result<()> {
153     let this = Rc::new(GattService::default());
154     database.add_service_with_handles(
155         // GATT Service
156         GattServiceWithHandle {
157             handle: GATT_SERVICE_HANDLE,
158             type_: GATT_SERVICE_UUID,
159             // Service Changed Characteristic
160             characteristics: vec![GattCharacteristicWithHandle {
161                 handle: SERVICE_CHANGE_HANDLE,
162                 type_: SERVICE_CHANGE_UUID,
163                 permissions: AttPermissions::INDICATE,
164                 descriptors: vec![GattDescriptorWithHandle {
165                     handle: SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE,
166                     type_: CLIENT_CHARACTERISTIC_CONFIGURATION_UUID,
167                     permissions: AttPermissions::READABLE | AttPermissions::WRITABLE_WITH_RESPONSE,
168                 }],
169             }],
170         },
171         this.clone(),
172     )?;
173     database.register_listener(this);
174     Ok(())
175 }
176 #[cfg(test)]
177 mod test {
178     use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
179 
180     use super::*;
181 
182     use crate::{
183         core::shared_box::SharedBox,
184         gatt::{
185             mocks::mock_datastore::MockDatastore,
186             server::{
187                 att_database::AttDatabase,
188                 gatt_database::{
189                     GattDatabase, CHARACTERISTIC_UUID, PRIMARY_SERVICE_DECLARATION_UUID,
190                 },
191             },
192         },
193         packets::{AttAttributeDataChild, AttBuilder, AttChild},
194         utils::task::{block_on_locally, try_await},
195     };
196 
197     const TCB_IDX: TransportIndex = TransportIndex(1);
198     const ANOTHER_TCB_IDX: TransportIndex = TransportIndex(2);
199     const SERVICE_TYPE: Uuid = Uuid::new(0x1234);
200     const CHARACTERISTIC_TYPE: Uuid = Uuid::new(0x5678);
201 
init_gatt_db() -> SharedBox<GattDatabase>202     fn init_gatt_db() -> SharedBox<GattDatabase> {
203         let mut gatt_database = GattDatabase::new();
204         register_gatt_service(&mut gatt_database).unwrap();
205         SharedBox::new(gatt_database)
206     }
207 
add_connection( gatt_database: &SharedBox<GattDatabase>, tcb_idx: TransportIndex, ) -> (AttDatabaseImpl, SharedBox<AttServerBearer<AttDatabaseImpl>>, UnboundedReceiver<AttBuilder>)208     fn add_connection(
209         gatt_database: &SharedBox<GattDatabase>,
210         tcb_idx: TransportIndex,
211     ) -> (AttDatabaseImpl, SharedBox<AttServerBearer<AttDatabaseImpl>>, UnboundedReceiver<AttBuilder>)
212     {
213         let att_database = gatt_database.get_att_database(tcb_idx);
214         let (tx, rx) = unbounded_channel();
215         let bearer = SharedBox::new(AttServerBearer::new(att_database.clone(), move |packet| {
216             tx.send(packet).unwrap();
217             Ok(())
218         }));
219         gatt_database.on_bearer_ready(tcb_idx, bearer.as_ref());
220         (att_database, bearer, rx)
221     }
222 
223     #[test]
test_gatt_service_discovery()224     fn test_gatt_service_discovery() {
225         // arrange
226         let gatt_db = init_gatt_db();
227         let (att_db, _, _) = add_connection(&gatt_db, TCB_IDX);
228 
229         // act: discover all services
230         let attrs = att_db.list_attributes();
231 
232         // assert: 1 service + 1 char decl + 1 char value + 1 char descriptor = 4 attrs
233         assert_eq!(attrs.len(), 4);
234         // assert: value handles are correct
235         assert_eq!(attrs[0].handle, GATT_SERVICE_HANDLE);
236         assert_eq!(attrs[2].handle, SERVICE_CHANGE_HANDLE);
237         // assert: types are correct
238         assert_eq!(attrs[0].type_, PRIMARY_SERVICE_DECLARATION_UUID);
239         assert_eq!(attrs[1].type_, CHARACTERISTIC_UUID);
240         assert_eq!(attrs[2].type_, SERVICE_CHANGE_UUID);
241         assert_eq!(attrs[3].type_, CLIENT_CHARACTERISTIC_CONFIGURATION_UUID);
242         // assert: permissions of value attrs are correct
243         assert_eq!(attrs[2].permissions, AttPermissions::INDICATE);
244         assert_eq!(
245             attrs[3].permissions,
246             AttPermissions::READABLE | AttPermissions::WRITABLE_WITH_RESPONSE
247         );
248     }
249 
250     #[test]
test_default_indication_subscription()251     fn test_default_indication_subscription() {
252         // arrange
253         let gatt_db = init_gatt_db();
254         let (att_db, _, _) = add_connection(&gatt_db, TCB_IDX);
255 
256         // act: try to read the CCC descriptor
257         let resp =
258             block_on_locally(att_db.read_attribute(SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE)).unwrap();
259 
260         assert_eq!(
261             Ok(resp),
262             GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 0 }
263                 .to_vec()
264         );
265     }
266 
register_for_indication( att_db: &impl AttDatabase, handle: AttHandle, ) -> Result<(), AttErrorCode>267     async fn register_for_indication(
268         att_db: &impl AttDatabase,
269         handle: AttHandle,
270     ) -> Result<(), AttErrorCode> {
271         att_db
272             .write_attribute(
273                 handle,
274                 &GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 1 }
275                     .to_vec()
276                     .unwrap(),
277             )
278             .await
279     }
280 
281     #[test]
test_subscribe_to_indication()282     fn test_subscribe_to_indication() {
283         // arrange
284         let gatt_db = init_gatt_db();
285         let (att_db, _, _) = add_connection(&gatt_db, TCB_IDX);
286 
287         // act: register for service change indication
288         block_on_locally(register_for_indication(&att_db, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE))
289             .unwrap();
290         // read our registration status
291         let resp =
292             block_on_locally(att_db.read_attribute(SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE)).unwrap();
293 
294         // assert: we are registered for indications
295         assert_eq!(
296             Ok(resp),
297             GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 1 }
298                 .to_vec()
299         );
300     }
301 
302     #[test]
test_unsubscribe_to_indication()303     fn test_unsubscribe_to_indication() {
304         // arrange
305         let gatt_db = init_gatt_db();
306         let (att_db, _, _) = add_connection(&gatt_db, TCB_IDX);
307 
308         // act: register for service change indication
309         block_on_locally(
310             att_db.write_attribute(
311                 SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE,
312                 &GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 1 }
313                     .to_vec()
314                     .unwrap(),
315             ),
316         )
317         .unwrap();
318         // act: next, unregister from this indication
319         block_on_locally(
320             att_db.write_attribute(
321                 SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE,
322                 &GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 0 }
323                     .to_vec()
324                     .unwrap(),
325             ),
326         )
327         .unwrap();
328         // read our registration status
329         let resp =
330             block_on_locally(att_db.read_attribute(SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE)).unwrap();
331 
332         // assert: we are not registered for indications
333         assert_eq!(
334             Ok(resp),
335             GattClientCharacteristicConfigurationBuilder { notification: 0, indication: 0 }
336                 .to_vec()
337         );
338     }
339 
340     #[test]
test_single_registered_service_change_indication()341     fn test_single_registered_service_change_indication() {
342         block_on_locally(async {
343             // arrange
344             let gatt_db = init_gatt_db();
345             let (att_db, _bearer, mut rx) = add_connection(&gatt_db, TCB_IDX);
346             let (gatt_datastore, _) = MockDatastore::new();
347             let gatt_datastore = Rc::new(gatt_datastore);
348             register_for_indication(&att_db, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
349 
350             // act: register some new service
351             gatt_db
352                 .add_service_with_handles(
353                     GattServiceWithHandle {
354                         handle: AttHandle(15),
355                         type_: SERVICE_TYPE,
356                         characteristics: vec![GattCharacteristicWithHandle {
357                             handle: AttHandle(17),
358                             type_: CHARACTERISTIC_TYPE,
359                             permissions: AttPermissions::empty(),
360                             descriptors: vec![],
361                         }],
362                     },
363                     gatt_datastore,
364                 )
365                 .unwrap();
366 
367             // assert: we received the service change indication
368             let resp = rx.recv().await.unwrap();
369             let AttChild::AttHandleValueIndication(resp) = resp._child_ else {
370                 unreachable!();
371             };
372             let AttAttributeDataChild::GattServiceChanged(resp) = resp.value._child_ else {
373                 unreachable!();
374             };
375             assert_eq!(resp.start_handle.handle, 15);
376             assert_eq!(resp.end_handle.handle, 17);
377         });
378     }
379 
380     #[test]
test_multiple_registered_service_change_indication()381     fn test_multiple_registered_service_change_indication() {
382         block_on_locally(async {
383             // arrange: two connections, both registered
384             let gatt_db = init_gatt_db();
385             let (att_db_1, _bearer, mut rx1) = add_connection(&gatt_db, TCB_IDX);
386             let (att_db_2, _bearer, mut rx2) = add_connection(&gatt_db, ANOTHER_TCB_IDX);
387 
388             register_for_indication(&att_db_1, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
389             register_for_indication(&att_db_2, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
390 
391             let (gatt_datastore, _) = MockDatastore::new();
392             let gatt_datastore = Rc::new(gatt_datastore);
393 
394             // act: register some new service
395             gatt_db
396                 .add_service_with_handles(
397                     GattServiceWithHandle {
398                         handle: AttHandle(15),
399                         type_: SERVICE_TYPE,
400                         characteristics: vec![GattCharacteristicWithHandle {
401                             handle: AttHandle(17),
402                             type_: CHARACTERISTIC_TYPE,
403                             permissions: AttPermissions::empty(),
404                             descriptors: vec![],
405                         }],
406                     },
407                     gatt_datastore,
408                 )
409                 .unwrap();
410 
411             // assert: both connections received the service change indication
412             let resp1 = rx1.recv().await.unwrap();
413             let resp2 = rx2.recv().await.unwrap();
414             assert!(matches!(resp1._child_, AttChild::AttHandleValueIndication(_)));
415             assert!(matches!(resp2._child_, AttChild::AttHandleValueIndication(_)));
416         });
417     }
418 
419     #[test]
test_one_unregistered_service_change_indication()420     fn test_one_unregistered_service_change_indication() {
421         block_on_locally(async {
422             // arrange: two connections, only the first is registered
423             let gatt_db = init_gatt_db();
424             let (att_db_1, _bearer, mut rx1) = add_connection(&gatt_db, TCB_IDX);
425             let (_, _bearer, mut rx2) = add_connection(&gatt_db, ANOTHER_TCB_IDX);
426 
427             register_for_indication(&att_db_1, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
428 
429             let (gatt_datastore, _) = MockDatastore::new();
430             let gatt_datastore = Rc::new(gatt_datastore);
431 
432             // act: register some new service
433             gatt_db
434                 .add_service_with_handles(
435                     GattServiceWithHandle {
436                         handle: AttHandle(15),
437                         type_: SERVICE_TYPE,
438                         characteristics: vec![GattCharacteristicWithHandle {
439                             handle: AttHandle(17),
440                             type_: CHARACTERISTIC_TYPE,
441                             permissions: AttPermissions::empty(),
442                             descriptors: vec![],
443                         }],
444                     },
445                     gatt_datastore,
446                 )
447                 .unwrap();
448 
449             // assert: the first connection received the service change indication
450             let resp1 = rx1.recv().await.unwrap();
451             assert!(matches!(resp1._child_, AttChild::AttHandleValueIndication(_)));
452             // assert: the second connection received nothing
453             assert!(try_await(async move { rx2.recv().await }).await.is_err());
454         });
455     }
456 
457     #[test]
test_one_disconnected_service_change_indication()458     fn test_one_disconnected_service_change_indication() {
459         block_on_locally(async {
460             // arrange: two connections, both register, but the second one disconnects
461             let gatt_db = init_gatt_db();
462             let (att_db_1, _bearer, mut rx1) = add_connection(&gatt_db, TCB_IDX);
463             let (att_db_2, bearer_2, mut rx2) = add_connection(&gatt_db, ANOTHER_TCB_IDX);
464 
465             register_for_indication(&att_db_1, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
466             register_for_indication(&att_db_2, SERVICE_CHANGE_CCC_DESCRIPTOR_HANDLE).await.unwrap();
467 
468             drop(bearer_2);
469             gatt_db.on_bearer_dropped(ANOTHER_TCB_IDX);
470 
471             let (gatt_datastore, _) = MockDatastore::new();
472             let gatt_datastore = Rc::new(gatt_datastore);
473 
474             // act: register some new service
475             gatt_db
476                 .add_service_with_handles(
477                     GattServiceWithHandle {
478                         handle: AttHandle(15),
479                         type_: SERVICE_TYPE,
480                         characteristics: vec![GattCharacteristicWithHandle {
481                             handle: AttHandle(17),
482                             type_: CHARACTERISTIC_TYPE,
483                             permissions: AttPermissions::empty(),
484                             descriptors: vec![],
485                         }],
486                     },
487                     gatt_datastore,
488                 )
489                 .unwrap();
490 
491             // assert: the first connection received the service change indication
492             let resp1 = rx1.recv().await.unwrap();
493             assert!(matches!(resp1._child_, AttChild::AttHandleValueIndication(_)));
494             // assert: the second connection is closed
495             assert!(rx2.recv().await.is_none());
496         });
497     }
498 }
499