1 //! These are the interfaces between the GattModule and JNI. The synchronous
2 //! interface is mapped to the asynchronous interface using the
3 //! CallbackTransactionManager;
4 
5 mod callback_transaction_manager;
6 
7 pub use callback_transaction_manager::{CallbackResponseError, CallbackTransactionManager};
8 
9 use async_trait::async_trait;
10 use log::warn;
11 
12 use crate::packets::AttErrorCode;
13 
14 use super::{
15     ffi::AttributeBackingType,
16     ids::{AttHandle, ConnectionId, TransactionId, TransportIndex},
17     server::IndicationError,
18 };
19 
20 /// These callbacks are expected to be made available to the GattModule from
21 /// JNI.
22 pub trait GattCallbacks {
23     /// Invoked when a client tries to read a characteristic/descriptor. Expects
24     /// a response using bluetooth::gatt::send_response();
on_server_read( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, )25     fn on_server_read(
26         &self,
27         conn_id: ConnectionId,
28         trans_id: TransactionId,
29         handle: AttHandle,
30         attr_type: AttributeBackingType,
31         offset: u32,
32     );
33 
34     /// Invoked when a client tries to write a characteristic/descriptor.
35     /// Expects a response using bluetooth::gatt::send_response();
36     #[allow(clippy::too_many_arguments)] // needed to match the C++ interface
on_server_write( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, write_type: GattWriteType, value: &[u8], )37     fn on_server_write(
38         &self,
39         conn_id: ConnectionId,
40         trans_id: TransactionId,
41         handle: AttHandle,
42         attr_type: AttributeBackingType,
43         write_type: GattWriteType,
44         value: &[u8],
45     );
46 
47     /// Invoked when a handle value indication transaction completes
48     /// (either due to an error, link loss, or the peer confirming it)
on_indication_sent_confirmation( &self, conn_id: ConnectionId, result: Result<(), IndicationError>, )49     fn on_indication_sent_confirmation(
50         &self,
51         conn_id: ConnectionId,
52         result: Result<(), IndicationError>,
53     );
54 
55     /// Execute or cancel any prepared writes
on_execute( &self, conn_id: ConnectionId, trans_id: TransactionId, decision: TransactionDecision, )56     fn on_execute(
57         &self,
58         conn_id: ConnectionId,
59         trans_id: TransactionId,
60         decision: TransactionDecision,
61     );
62 }
63 
64 /// The various write types available (requests + commands)
65 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
66 pub enum GattWriteType {
67     /// Reliable, expects a response (WRITE_REQ or PREPARE_WRITE_REQ)
68     Request(GattWriteRequestType),
69     /// Unreliable, no response required (WRITE_CMD)
70     Command,
71 }
72 
73 /// The types of write requests (that need responses)
74 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
75 pub enum GattWriteRequestType {
76     /// Atomic (WRITE_REQ)
77     Request,
78     /// Transactional, should not be committed yet (PREPARE_WRITE_REQ)
79     Prepare {
80         /// The byte offset at which to write
81         offset: u32,
82     },
83 }
84 
85 /// Whether to commit or cancel a transaction
86 #[derive(Clone, Copy, Debug)]
87 pub enum TransactionDecision {
88     /// Commit all pending writes
89     Execute,
90     /// Discard all pending writes
91     Cancel,
92 }
93 
94 /// This interface is an "async" version of the above, and is passed directly
95 /// into the GattModule
96 #[async_trait(?Send)]
97 pub trait RawGattDatastore {
98     /// Read a characteristic from the specified connection at the given handle.
read( &self, tcb_idx: TransportIndex, handle: AttHandle, offset: u32, attr_type: AttributeBackingType, ) -> Result<Vec<u8>, AttErrorCode>99     async fn read(
100         &self,
101         tcb_idx: TransportIndex,
102         handle: AttHandle,
103         offset: u32,
104         attr_type: AttributeBackingType,
105     ) -> Result<Vec<u8>, AttErrorCode>;
106 
107     /// Write data to a given characteristic on the specified connection.
write( &self, tcb_idx: TransportIndex, handle: AttHandle, attr_type: AttributeBackingType, write_type: GattWriteRequestType, data: &[u8], ) -> Result<(), AttErrorCode>108     async fn write(
109         &self,
110         tcb_idx: TransportIndex,
111         handle: AttHandle,
112         attr_type: AttributeBackingType,
113         write_type: GattWriteRequestType,
114         data: &[u8],
115     ) -> Result<(), AttErrorCode>;
116 
117     /// Write data to a given characteristic on the specified connection, without waiting
118     /// for a response from the upper layer.
write_no_response( &self, tcb_idx: TransportIndex, handle: AttHandle, attr_type: AttributeBackingType, data: &[u8], )119     fn write_no_response(
120         &self,
121         tcb_idx: TransportIndex,
122         handle: AttHandle,
123         attr_type: AttributeBackingType,
124         data: &[u8],
125     );
126 
127     /// Execute or cancel any prepared writes
execute( &self, tcb_idx: TransportIndex, decision: TransactionDecision, ) -> Result<(), AttErrorCode>128     async fn execute(
129         &self,
130         tcb_idx: TransportIndex,
131         decision: TransactionDecision,
132     ) -> Result<(), AttErrorCode>;
133 }
134 
135 /// This interface simplifies the interface of RawGattDatastore by rejecting all unsupported
136 /// operations, rather than requiring clients to do so.
137 #[async_trait(?Send)]
138 pub trait GattDatastore {
139     /// Read a characteristic from the specified connection at the given handle.
read( &self, tcb_idx: TransportIndex, handle: AttHandle, attr_type: AttributeBackingType, ) -> Result<Vec<u8>, AttErrorCode>140     async fn read(
141         &self,
142         tcb_idx: TransportIndex,
143         handle: AttHandle,
144         attr_type: AttributeBackingType,
145     ) -> Result<Vec<u8>, AttErrorCode>;
146 
147     /// Write data to a given characteristic on the specified connection.
write( &self, tcb_idx: TransportIndex, handle: AttHandle, attr_type: AttributeBackingType, data: &[u8], ) -> Result<(), AttErrorCode>148     async fn write(
149         &self,
150         tcb_idx: TransportIndex,
151         handle: AttHandle,
152         attr_type: AttributeBackingType,
153         data: &[u8],
154     ) -> Result<(), AttErrorCode>;
155 }
156 
157 #[async_trait(?Send)]
158 impl<T: GattDatastore + ?Sized> RawGattDatastore for T {
159     /// Read a characteristic from the specified connection at the given handle.
read( &self, tcb_idx: TransportIndex, handle: AttHandle, offset: u32, attr_type: AttributeBackingType, ) -> Result<Vec<u8>, AttErrorCode>160     async fn read(
161         &self,
162         tcb_idx: TransportIndex,
163         handle: AttHandle,
164         offset: u32,
165         attr_type: AttributeBackingType,
166     ) -> Result<Vec<u8>, AttErrorCode> {
167         if offset != 0 {
168             warn!("got read blob request for non-long attribute {handle:?}");
169             return Err(AttErrorCode::ATTRIBUTE_NOT_LONG);
170         }
171         self.read(tcb_idx, handle, attr_type).await
172     }
173 
174     /// Write data to a given characteristic on the specified connection.
write( &self, tcb_idx: TransportIndex, handle: AttHandle, attr_type: AttributeBackingType, write_type: GattWriteRequestType, data: &[u8], ) -> Result<(), AttErrorCode>175     async fn write(
176         &self,
177         tcb_idx: TransportIndex,
178         handle: AttHandle,
179         attr_type: AttributeBackingType,
180         write_type: GattWriteRequestType,
181         data: &[u8],
182     ) -> Result<(), AttErrorCode> {
183         match write_type {
184             GattWriteRequestType::Prepare { .. } => {
185                 warn!("got prepare write attempt on {tcb_idx:?} to characteristic {handle:?} not supporting write_without_response");
186                 Err(AttErrorCode::WRITE_REQUEST_REJECTED)
187             }
188             GattWriteRequestType::Request => self.write(tcb_idx, handle, attr_type, data).await,
189         }
190     }
191 
write_no_response( &self, tcb_idx: TransportIndex, handle: AttHandle, _: AttributeBackingType, _: &[u8], )192     fn write_no_response(
193         &self,
194         tcb_idx: TransportIndex,
195         handle: AttHandle,
196         _: AttributeBackingType,
197         _: &[u8],
198     ) {
199         // silently drop, since there's no way to return an error
200         warn!("got write command on {tcb_idx:?} to characteristic {handle:?} not supporting write_without_response");
201     }
202 
203     /// Execute or cancel any prepared writes
execute(&self, _: TransportIndex, _: TransactionDecision) -> Result<(), AttErrorCode>204     async fn execute(&self, _: TransportIndex, _: TransactionDecision) -> Result<(), AttErrorCode> {
205         // we never do prepared writes, so who cares
206         Ok(())
207     }
208 }
209 
210 #[cfg(test)]
211 mod test {
212     use tokio::{sync::mpsc::error::TryRecvError, task::spawn_local};
213 
214     use crate::{
215         gatt::mocks::mock_datastore::{MockDatastore, MockDatastoreEvents},
216         utils::task::block_on_locally,
217     };
218 
219     use super::*;
220 
221     const TCB_IDX: TransportIndex = TransportIndex(1);
222     const HANDLE: AttHandle = AttHandle(1);
223     const DATA: [u8; 4] = [1, 2, 3, 4];
224 
225     #[test]
test_regular_read_invoke()226     fn test_regular_read_invoke() {
227         block_on_locally(async {
228             // arrange
229             let (datastore, mut rx) = MockDatastore::new();
230 
231             // act: send read request
232             spawn_local(async move {
233                 RawGattDatastore::read(
234                     &datastore,
235                     TCB_IDX,
236                     HANDLE,
237                     0,
238                     AttributeBackingType::Characteristic,
239                 )
240                 .await
241             });
242             let resp = rx.recv().await.unwrap();
243 
244             // assert: got read event
245             assert!(matches!(
246                 resp,
247                 MockDatastoreEvents::Read(TCB_IDX, HANDLE, AttributeBackingType::Characteristic, _)
248             ));
249         });
250     }
251 
252     #[test]
test_regular_read_response()253     fn test_regular_read_response() {
254         block_on_locally(async {
255             // arrange
256             let (datastore, mut rx) = MockDatastore::new();
257 
258             // act: send read request
259             let pending = spawn_local(async move {
260                 RawGattDatastore::read(
261                     &datastore,
262                     TCB_IDX,
263                     HANDLE,
264                     0,
265                     AttributeBackingType::Characteristic,
266                 )
267                 .await
268             });
269             let resp = rx.recv().await.unwrap();
270             let MockDatastoreEvents::Read(_, _, _, resp) = resp else {
271                 unreachable!();
272             };
273             resp.send(Err(AttErrorCode::APPLICATION_ERROR)).unwrap();
274 
275             // assert: got the supplied response
276             assert_eq!(pending.await.unwrap(), Err(AttErrorCode::APPLICATION_ERROR));
277         });
278     }
279 
280     #[test]
test_rejected_read_blob()281     fn test_rejected_read_blob() {
282         // arrange
283         let (datastore, mut rx) = MockDatastore::new();
284 
285         // act: send read blob request
286         let resp = block_on_locally(RawGattDatastore::read(
287             &datastore,
288             TCB_IDX,
289             HANDLE,
290             1,
291             AttributeBackingType::Characteristic,
292         ));
293 
294         // assert: got the correct error code
295         assert_eq!(resp, Err(AttErrorCode::ATTRIBUTE_NOT_LONG));
296         // assert: no pending events
297         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
298     }
299 
300     #[test]
test_write_request_invoke()301     fn test_write_request_invoke() {
302         block_on_locally(async {
303             // arrange
304             let (datastore, mut rx) = MockDatastore::new();
305 
306             // act: send write request
307             spawn_local(async move {
308                 RawGattDatastore::write(
309                     &datastore,
310                     TCB_IDX,
311                     HANDLE,
312                     AttributeBackingType::Characteristic,
313                     GattWriteRequestType::Request,
314                     &DATA,
315                 )
316                 .await
317             });
318             let resp = rx.recv().await.unwrap();
319 
320             // assert: got write event
321             assert!(matches!(
322                 resp,
323                 MockDatastoreEvents::Write(
324                     TCB_IDX,
325                     HANDLE,
326                     AttributeBackingType::Characteristic,
327                     _,
328                     _
329                 )
330             ));
331         });
332     }
333 
334     #[test]
test_write_request_response()335     fn test_write_request_response() {
336         block_on_locally(async {
337             // arrange
338             let (datastore, mut rx) = MockDatastore::new();
339 
340             // act: send write request
341             let pending = spawn_local(async move {
342                 RawGattDatastore::write(
343                     &datastore,
344                     TCB_IDX,
345                     HANDLE,
346                     AttributeBackingType::Characteristic,
347                     GattWriteRequestType::Request,
348                     &DATA,
349                 )
350                 .await
351             });
352             let resp = rx.recv().await.unwrap();
353             let MockDatastoreEvents::Write(_, _, _, _, resp) = resp else {
354                 unreachable!();
355             };
356             resp.send(Err(AttErrorCode::APPLICATION_ERROR)).unwrap();
357 
358             // assert: got the supplied response
359             assert_eq!(pending.await.unwrap(), Err(AttErrorCode::APPLICATION_ERROR));
360         });
361     }
362 
363     #[test]
test_rejected_prepared_write()364     fn test_rejected_prepared_write() {
365         // arrange
366         let (datastore, mut rx) = MockDatastore::new();
367 
368         // act: send prepare write request
369         let resp = block_on_locally(RawGattDatastore::write(
370             &datastore,
371             TCB_IDX,
372             HANDLE,
373             AttributeBackingType::Characteristic,
374             GattWriteRequestType::Prepare { offset: 1 },
375             &DATA,
376         ));
377 
378         // assert: got the correct error code
379         assert_eq!(resp, Err(AttErrorCode::WRITE_REQUEST_REJECTED));
380         // assert: no event sent up
381         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
382     }
383 
384     #[test]
test_dropped_write_command()385     fn test_dropped_write_command() {
386         // arrange
387         let (datastore, mut rx) = MockDatastore::new();
388 
389         // act: send write command
390         RawGattDatastore::write_no_response(
391             &datastore,
392             TCB_IDX,
393             HANDLE,
394             AttributeBackingType::Characteristic,
395             &DATA,
396         );
397 
398         // assert: no event sent up
399         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
400     }
401 
402     #[test]
test_execute_noop()403     fn test_execute_noop() {
404         // arrange
405         let (datastore, mut rx) = MockDatastore::new();
406 
407         // act: send execute request
408         let resp = block_on_locally(RawGattDatastore::execute(
409             &datastore,
410             TCB_IDX,
411             TransactionDecision::Execute,
412         ));
413 
414         // assert: succeeds trivially
415         assert!(resp.is_ok());
416         // assert: no event sent up
417         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
418     }
419 }
420