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