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