• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use pdl_runtime::Packet;
2 use std::rc::Rc;
3 use std::sync::{Arc, Mutex};
4 
5 use bluetooth_core::core::uuid::Uuid;
6 use bluetooth_core::gatt::ffi::AttributeBackingType;
7 use bluetooth_core::gatt::ids::{AdvertiserId, AttHandle, ServerId, TransportIndex};
8 use bluetooth_core::gatt::mocks::mock_datastore::{MockDatastore, MockDatastoreEvents};
9 use bluetooth_core::gatt::mocks::mock_transport::MockAttTransport;
10 use bluetooth_core::gatt::server::gatt_database::{
11     AttPermissions, GattCharacteristicWithHandle, GattDescriptorWithHandle, GattServiceWithHandle,
12     CHARACTERISTIC_UUID, PRIMARY_SERVICE_DECLARATION_UUID,
13 };
14 use bluetooth_core::gatt::server::isolation_manager::IsolationManager;
15 use bluetooth_core::gatt::server::services::gap::DEVICE_NAME_UUID;
16 use bluetooth_core::gatt::server::services::gatt::{
17     CLIENT_CHARACTERISTIC_CONFIGURATION_UUID, GATT_SERVICE_UUID, SERVICE_CHANGE_UUID,
18 };
19 use bluetooth_core::gatt::server::{GattModule, IndicationError};
20 use bluetooth_core::gatt::{self};
21 use bluetooth_core::packets::att::{self, AttErrorCode};
22 
23 use tokio::sync::mpsc::error::TryRecvError;
24 use tokio::sync::mpsc::UnboundedReceiver;
25 use tokio::task::spawn_local;
26 use utils::start_test;
27 
28 mod utils;
29 
30 const TCB_IDX: TransportIndex = TransportIndex(1);
31 const SERVER_ID: ServerId = ServerId(2);
32 const ADVERTISER_ID: AdvertiserId = AdvertiserId(3);
33 
34 const ANOTHER_TCB_IDX: TransportIndex = TransportIndex(2);
35 const ANOTHER_SERVER_ID: ServerId = ServerId(3);
36 const ANOTHER_ADVERTISER_ID: AdvertiserId = AdvertiserId(4);
37 
38 const SERVICE_HANDLE: AttHandle = AttHandle(6);
39 const CHARACTERISTIC_HANDLE: AttHandle = AttHandle(8);
40 const DESCRIPTOR_HANDLE: AttHandle = AttHandle(9);
41 
42 const SERVICE_TYPE: Uuid = Uuid::new(0x0102);
43 const CHARACTERISTIC_TYPE: Uuid = Uuid::new(0x0103);
44 const DESCRIPTOR_TYPE: Uuid = Uuid::new(0x0104);
45 
46 const DATA: [u8; 4] = [1, 2, 3, 4];
47 const ANOTHER_DATA: [u8; 4] = [5, 6, 7, 8];
48 
start_gatt_module() -> (gatt::server::GattModule, UnboundedReceiver<(TransportIndex, att::Att)>)49 fn start_gatt_module() -> (gatt::server::GattModule, UnboundedReceiver<(TransportIndex, att::Att)>)
50 {
51     let (transport, transport_rx) = MockAttTransport::new();
52     let arbiter = IsolationManager::new();
53     let gatt = GattModule::new(Rc::new(transport), Arc::new(Mutex::new(arbiter)));
54 
55     (gatt, transport_rx)
56 }
57 
create_server_and_open_connection( gatt: &mut GattModule, ) -> UnboundedReceiver<MockDatastoreEvents>58 fn create_server_and_open_connection(
59     gatt: &mut GattModule,
60 ) -> UnboundedReceiver<MockDatastoreEvents> {
61     gatt.open_gatt_server(SERVER_ID).unwrap();
62     let (datastore, data_rx) = MockDatastore::new();
63     gatt.register_gatt_service(
64         SERVER_ID,
65         GattServiceWithHandle {
66             handle: SERVICE_HANDLE,
67             type_: SERVICE_TYPE,
68             characteristics: vec![GattCharacteristicWithHandle {
69                 handle: CHARACTERISTIC_HANDLE,
70                 type_: CHARACTERISTIC_TYPE,
71                 permissions: AttPermissions::READABLE
72                     | AttPermissions::WRITABLE_WITH_RESPONSE
73                     | AttPermissions::INDICATE,
74                 descriptors: vec![GattDescriptorWithHandle {
75                     handle: DESCRIPTOR_HANDLE,
76                     type_: DESCRIPTOR_TYPE,
77                     permissions: AttPermissions::READABLE | AttPermissions::WRITABLE_WITH_RESPONSE,
78                 }],
79             }],
80         },
81         datastore,
82     )
83     .unwrap();
84     gatt.get_isolation_manager().associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
85     gatt.on_le_connect(TCB_IDX, Some(ADVERTISER_ID)).unwrap();
86     data_rx
87 }
88 
89 #[test]
test_service_read()90 fn test_service_read() {
91     start_test(async move {
92         // arrange
93         let (mut gatt, mut transport_rx) = start_gatt_module();
94 
95         create_server_and_open_connection(&mut gatt);
96 
97         // act
98         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
99             att::AttReadRequest { attribute_handle: SERVICE_HANDLE.into() }.try_into().unwrap(),
100         );
101         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
102 
103         // assert
104         assert_eq!(tcb_idx, TCB_IDX);
105         assert_eq!(
106             Ok(resp),
107             att::AttReadResponse {
108                 value: att::GattServiceDeclarationValue { uuid: SERVICE_TYPE.into() }
109                     .encode_to_vec()
110                     .unwrap(),
111             }
112             .try_into()
113         );
114     })
115 }
116 
117 #[test]
test_server_closed_while_connected()118 fn test_server_closed_while_connected() {
119     start_test(async move {
120         // arrange: set up a connection to a closed server
121         let (mut gatt, mut transport_rx) = start_gatt_module();
122 
123         // open a server and connect
124         create_server_and_open_connection(&mut gatt);
125         gatt.close_gatt_server(SERVER_ID).unwrap();
126 
127         // act: read from the closed server
128         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
129             att::AttReadRequest { attribute_handle: SERVICE_HANDLE.into() }.try_into().unwrap(),
130         );
131         let (_, resp) = transport_rx.recv().await.unwrap();
132 
133         // assert that the read failed, but that a response was provided
134         assert_eq!(
135             Ok(resp),
136             att::AttErrorResponse {
137                 opcode_in_error: att::AttOpcode::ReadRequest,
138                 handle_in_error: SERVICE_HANDLE.into(),
139                 error_code: AttErrorCode::InvalidHandle
140             }
141             .try_into()
142         )
143     });
144 }
145 
146 #[test]
test_characteristic_read()147 fn test_characteristic_read() {
148     start_test(async move {
149         // arrange
150         let (mut gatt, mut transport_rx) = start_gatt_module();
151         let mut data_rx = create_server_and_open_connection(&mut gatt);
152 
153         // act
154         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
155             att::AttReadRequest { attribute_handle: CHARACTERISTIC_HANDLE.into() }
156                 .try_into()
157                 .unwrap(),
158         );
159         let tx = if let MockDatastoreEvents::Read(
160             TCB_IDX,
161             CHARACTERISTIC_HANDLE,
162             AttributeBackingType::Characteristic,
163             tx,
164         ) = data_rx.recv().await.unwrap()
165         {
166             tx
167         } else {
168             unreachable!()
169         };
170         tx.send(Ok(DATA.to_vec())).unwrap();
171         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
172 
173         // assert
174         assert_eq!(tcb_idx, TCB_IDX);
175         assert_eq!(Ok(resp), att::AttReadResponse { value: DATA.into() }.try_into());
176     })
177 }
178 
179 #[test]
test_characteristic_write()180 fn test_characteristic_write() {
181     start_test(async move {
182         // arrange
183         let (mut gatt, mut transport_rx) = start_gatt_module();
184         let mut data_rx = create_server_and_open_connection(&mut gatt);
185 
186         // act
187         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
188             att::AttWriteRequest { handle: CHARACTERISTIC_HANDLE.into(), value: DATA.into() }
189                 .try_into()
190                 .unwrap(),
191         );
192         let (tx, written_data) = if let MockDatastoreEvents::Write(
193             TCB_IDX,
194             CHARACTERISTIC_HANDLE,
195             AttributeBackingType::Characteristic,
196             written_data,
197             tx,
198         ) = data_rx.recv().await.unwrap()
199         {
200             (tx, written_data)
201         } else {
202             unreachable!()
203         };
204         tx.send(Ok(())).unwrap();
205         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
206 
207         // assert
208         assert_eq!(tcb_idx, TCB_IDX);
209         assert_eq!(Ok(resp), att::AttWriteResponse {}.try_into());
210         assert_eq!(&DATA, written_data.as_slice());
211     })
212 }
213 
214 #[test]
test_send_indication()215 fn test_send_indication() {
216     start_test(async move {
217         // arrange
218         let (mut gatt, mut transport_rx) = start_gatt_module();
219         create_server_and_open_connection(&mut gatt);
220 
221         // act
222         let pending_indication = spawn_local(
223             gatt.get_bearer(TCB_IDX).unwrap().send_indication(CHARACTERISTIC_HANDLE, DATA.into()),
224         );
225 
226         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
227 
228         gatt.get_bearer(TCB_IDX)
229             .unwrap()
230             .handle_packet(att::AttHandleValueConfirmation {}.try_into().unwrap());
231 
232         // assert
233         assert!(matches!(pending_indication.await.unwrap(), Ok(())));
234         assert_eq!(tcb_idx, TCB_IDX);
235         assert_eq!(
236             Ok(resp),
237             att::AttHandleValueIndication {
238                 handle: CHARACTERISTIC_HANDLE.into(),
239                 value: DATA.into(),
240             }
241             .try_into()
242         );
243     })
244 }
245 
246 #[test]
test_send_indication_and_disconnect()247 fn test_send_indication_and_disconnect() {
248     start_test(async move {
249         // arrange
250         let (mut gatt, mut transport_rx) = start_gatt_module();
251 
252         create_server_and_open_connection(&mut gatt);
253 
254         // act: send an indication, then disconnect
255         let pending_indication = spawn_local(
256             gatt.get_bearer(TCB_IDX)
257                 .unwrap()
258                 .send_indication(CHARACTERISTIC_HANDLE, vec![1, 2, 3, 4]),
259         );
260         transport_rx.recv().await.unwrap();
261         gatt.on_le_disconnect(TCB_IDX).unwrap();
262 
263         // assert: the pending indication resolves appropriately
264         assert!(matches!(
265             pending_indication.await.unwrap(),
266             Err(IndicationError::ConnectionDroppedWhileWaitingForConfirmation)
267         ));
268     })
269 }
270 
271 #[test]
test_write_to_descriptor()272 fn test_write_to_descriptor() {
273     start_test(async move {
274         // arrange
275         let (mut gatt, mut transport_rx) = start_gatt_module();
276         let mut data_rx = create_server_and_open_connection(&mut gatt);
277 
278         // act
279         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
280             att::AttWriteRequest { handle: DESCRIPTOR_HANDLE.into(), value: DATA.into() }
281                 .try_into()
282                 .unwrap(),
283         );
284         let (tx, written_data) = if let MockDatastoreEvents::Write(
285             TCB_IDX,
286             DESCRIPTOR_HANDLE,
287             AttributeBackingType::Descriptor,
288             written_data,
289             tx,
290         ) = data_rx.recv().await.unwrap()
291         {
292             (tx, written_data)
293         } else {
294             unreachable!()
295         };
296         tx.send(Ok(())).unwrap();
297         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
298 
299         // assert
300         assert_eq!(tcb_idx, TCB_IDX);
301         assert_eq!(Ok(resp), att::AttWriteResponse {}.try_into());
302         assert_eq!(&DATA, written_data.as_slice());
303     })
304 }
305 
306 #[test]
test_multiple_servers()307 fn test_multiple_servers() {
308     start_test(async move {
309         // arrange
310         let (mut gatt, mut transport_rx) = start_gatt_module();
311         // open the default server (SERVER_ID on CONN_ID)
312         let mut data_rx_1 = create_server_and_open_connection(&mut gatt);
313         // open a second server and connect to it (ANOTHER_SERVER_ID on ANOTHER_CONN_ID)
314         let (datastore, mut data_rx_2) = MockDatastore::new();
315         gatt.open_gatt_server(ANOTHER_SERVER_ID).unwrap();
316         gatt.register_gatt_service(
317             ANOTHER_SERVER_ID,
318             GattServiceWithHandle {
319                 handle: SERVICE_HANDLE,
320                 type_: SERVICE_TYPE,
321                 characteristics: vec![GattCharacteristicWithHandle {
322                     handle: CHARACTERISTIC_HANDLE,
323                     type_: CHARACTERISTIC_TYPE,
324                     permissions: AttPermissions::READABLE,
325                     descriptors: vec![],
326                 }],
327             },
328             datastore,
329         )
330         .unwrap();
331         gatt.get_isolation_manager()
332             .associate_server_with_advertiser(ANOTHER_SERVER_ID, ANOTHER_ADVERTISER_ID);
333         gatt.on_le_connect(ANOTHER_TCB_IDX, Some(ANOTHER_ADVERTISER_ID)).unwrap();
334 
335         // act: read from both connections
336         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
337             att::AttReadRequest { attribute_handle: CHARACTERISTIC_HANDLE.into() }
338                 .try_into()
339                 .unwrap(),
340         );
341         gatt.get_bearer(ANOTHER_TCB_IDX).unwrap().handle_packet(
342             att::AttReadRequest { attribute_handle: CHARACTERISTIC_HANDLE.into() }
343                 .try_into()
344                 .unwrap(),
345         );
346         // service the first read with `data`
347         let MockDatastoreEvents::Read(TCB_IDX, _, _, tx) = data_rx_1.recv().await.unwrap() else {
348             unreachable!()
349         };
350         tx.send(Ok(DATA.to_vec())).unwrap();
351         // and then the second read with `another_data`
352         let MockDatastoreEvents::Read(ANOTHER_TCB_IDX, _, _, tx) = data_rx_2.recv().await.unwrap()
353         else {
354             unreachable!()
355         };
356         tx.send(Ok(ANOTHER_DATA.to_vec())).unwrap();
357 
358         // receive both response packets
359         let (tcb_idx_1, resp_1) = transport_rx.recv().await.unwrap();
360         let (tcb_idx_2, resp_2) = transport_rx.recv().await.unwrap();
361 
362         // assert: the responses were routed to the correct connections
363         assert_eq!(tcb_idx_1, TCB_IDX);
364         assert_eq!(Ok(resp_1), att::AttReadResponse { value: DATA.to_vec() }.try_into());
365         assert_eq!(tcb_idx_2, ANOTHER_TCB_IDX);
366         assert_eq!(Ok(resp_2), att::AttReadResponse { value: ANOTHER_DATA.to_vec() }.try_into());
367     })
368 }
369 
370 #[test]
test_read_device_name()371 fn test_read_device_name() {
372     start_test(async move {
373         // arrange
374         let (mut gatt, mut transport_rx) = start_gatt_module();
375         create_server_and_open_connection(&mut gatt);
376 
377         // act: try to read the device name
378         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
379             att::AttReadByTypeRequest {
380                 starting_handle: AttHandle(1).into(),
381                 ending_handle: AttHandle(0xFFFF).into(),
382                 attribute_type: DEVICE_NAME_UUID.into(),
383             }
384             .try_into()
385             .unwrap(),
386         );
387         let (tcb_idx, resp) = transport_rx.recv().await.unwrap();
388 
389         // assert: the name should not be readable
390         assert_eq!(tcb_idx, TCB_IDX);
391         assert_eq!(
392             Ok(resp),
393             att::AttErrorResponse {
394                 opcode_in_error: att::AttOpcode::ReadByTypeRequest,
395                 handle_in_error: AttHandle(1).into(),
396                 error_code: AttErrorCode::InsufficientAuthentication,
397             }
398             .try_into()
399         );
400     });
401 }
402 
403 #[test]
test_ignored_service_change_indication()404 fn test_ignored_service_change_indication() {
405     start_test(async move {
406         // arrange
407         let (mut gatt, mut transport_rx) = start_gatt_module();
408         create_server_and_open_connection(&mut gatt);
409 
410         // act: add a new service
411         let (datastore, _) = MockDatastore::new();
412         gatt.register_gatt_service(
413             SERVER_ID,
414             GattServiceWithHandle {
415                 handle: AttHandle(30),
416                 type_: SERVICE_TYPE,
417                 characteristics: vec![],
418             },
419             datastore,
420         )
421         .unwrap();
422 
423         // assert: no packets should be sent
424         assert_eq!(transport_rx.try_recv().unwrap_err(), TryRecvError::Empty);
425     });
426 }
427 
428 #[test]
test_service_change_indication()429 fn test_service_change_indication() {
430     start_test(async move {
431         // arrange
432         let (mut gatt, mut transport_rx) = start_gatt_module();
433         create_server_and_open_connection(&mut gatt);
434 
435         // act: discover the GATT server
436         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
437             att::AttFindByTypeValueRequest {
438                 starting_handle: AttHandle::MIN.into(),
439                 ending_handle: AttHandle::MAX.into(),
440                 attribute_type: PRIMARY_SERVICE_DECLARATION_UUID.try_into().unwrap(),
441                 attribute_value: att::UuidAsAttData { uuid: GATT_SERVICE_UUID.into() }
442                     .encode_to_vec()
443                     .unwrap(),
444             }
445             .try_into()
446             .unwrap(),
447         );
448         let Ok(resp): Result<att::AttFindByTypeValueResponse, _> =
449             transport_rx.recv().await.unwrap().1.try_into()
450         else {
451             unreachable!()
452         };
453         let (starting_handle, ending_handle) = (
454             resp.handles_info[0].clone().found_attribute_handle,
455             resp.handles_info[0].clone().group_end_handle,
456         );
457         // act: discover the service changed characteristic
458         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
459             att::AttReadByTypeRequest {
460                 starting_handle,
461                 ending_handle,
462                 attribute_type: CHARACTERISTIC_UUID.into(),
463             }
464             .try_into()
465             .unwrap(),
466         );
467 
468         let Ok(resp): Result<att::AttReadByTypeResponse, _> =
469             transport_rx.recv().await.unwrap().1.try_into()
470         else {
471             unreachable!()
472         };
473         let service_change_char_handle: AttHandle = resp
474             .data
475             .into_iter()
476             .find_map(|characteristic| {
477                 let value = characteristic.value.to_vec();
478                 let decl =
479                     att::GattCharacteristicDeclarationValue::decode_full(value.as_slice()).unwrap();
480 
481                 if SERVICE_CHANGE_UUID == decl.uuid.try_into().unwrap() {
482                     Some(decl.handle.into())
483                 } else {
484                     None
485                 }
486             })
487             .unwrap();
488         // act: find the CCC descriptor for the service changed characteristic
489         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
490             att::AttFindInformationRequest {
491                 starting_handle: service_change_char_handle.into(),
492                 ending_handle: AttHandle::MAX.into(),
493             }
494             .try_into()
495             .unwrap(),
496         );
497         let Ok(resp): Result<att::AttFindInformationResponse, _> =
498             transport_rx.recv().await.unwrap().1.try_into()
499         else {
500             unreachable!()
501         };
502         let Ok(resp): Result<att::AttFindInformationShortResponse, _> = resp.try_into() else {
503             unreachable!()
504         };
505         let service_change_descriptor_handle = resp
506             .data
507             .into_iter()
508             .find_map(|attr| {
509                 if attr.uuid == CLIENT_CHARACTERISTIC_CONFIGURATION_UUID.try_into().unwrap() {
510                     Some(attr.handle)
511                 } else {
512                     None
513                 }
514             })
515             .unwrap();
516         // act: register for indications on this handle
517         gatt.get_bearer(TCB_IDX).unwrap().handle_packet(
518             att::AttWriteRequest {
519                 handle: service_change_descriptor_handle,
520                 value: att::GattClientCharacteristicConfiguration {
521                     notification: 0,
522                     indication: 1,
523                 }
524                 .encode_to_vec()
525                 .unwrap(),
526             }
527             .try_into()
528             .unwrap(),
529         );
530         let Ok(_): Result<att::AttWriteResponse, _> =
531             transport_rx.recv().await.unwrap().1.try_into()
532         else {
533             unreachable!()
534         };
535         // act: add a new service
536         let (datastore, _) = MockDatastore::new();
537         gatt.register_gatt_service(
538             SERVER_ID,
539             GattServiceWithHandle {
540                 handle: AttHandle(30),
541                 type_: SERVICE_TYPE,
542                 characteristics: vec![],
543             },
544             datastore,
545         )
546         .unwrap();
547 
548         // assert: we got an indication
549         let Ok(indication): Result<att::AttHandleValueIndication, _> =
550             transport_rx.recv().await.unwrap().1.try_into()
551         else {
552             unreachable!()
553         };
554         assert_eq!(indication.handle, service_change_char_handle.into());
555         assert_eq!(
556             Ok(indication.value.into()),
557             att::GattServiceChanged {
558                 start_handle: AttHandle(30).into(),
559                 end_handle: AttHandle(30).into(),
560             }
561             .encode_to_vec()
562         );
563     });
564 }
565 
566 #[test]
test_closing_gatt_server_unisolates_advertiser()567 fn test_closing_gatt_server_unisolates_advertiser() {
568     start_test(async move {
569         // arrange
570         let (mut gatt, _) = start_gatt_module();
571         gatt.open_gatt_server(SERVER_ID).unwrap();
572         gatt.get_isolation_manager().associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
573 
574         // act
575         gatt.close_gatt_server(SERVER_ID).unwrap();
576 
577         // assert
578         let is_advertiser_isolated =
579             gatt.get_isolation_manager().is_advertiser_isolated(ADVERTISER_ID);
580         assert!(!is_advertiser_isolated);
581     });
582 }
583 
584 #[test]
test_disconnection_unisolates_connection()585 fn test_disconnection_unisolates_connection() {
586     start_test(async move {
587         // arrange
588         let (mut gatt, _) = start_gatt_module();
589         create_server_and_open_connection(&mut gatt);
590 
591         // act
592         gatt.on_le_disconnect(TCB_IDX).unwrap();
593 
594         // assert
595         let is_connection_isolated = gatt.get_isolation_manager().is_connection_isolated(TCB_IDX);
596         assert!(!is_connection_isolated);
597     });
598 }
599