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