1 //! The GAP service as defined in Core Spec 5.3 Vol 3C Section 12
2
3 use std::rc::Rc;
4
5 use anyhow::Result;
6 use async_trait::async_trait;
7
8 use crate::core::uuid::Uuid;
9 use crate::gatt::callbacks::GattDatastore;
10 use crate::gatt::ffi::AttributeBackingType;
11 use crate::gatt::ids::{AttHandle, TransportIndex};
12 use crate::gatt::server::gatt_database::{
13 AttPermissions, GattCharacteristicWithHandle, GattDatabase, GattServiceWithHandle,
14 };
15 use crate::packets::att::AttErrorCode;
16
17 struct GapService;
18
19 // Must lie in the range specified by GATT_GAP_START_HANDLE from legacy stack
20 const GAP_SERVICE_HANDLE: AttHandle = AttHandle(20);
21 const DEVICE_NAME_HANDLE: AttHandle = AttHandle(22);
22 const DEVICE_APPEARANCE_HANDLE: AttHandle = AttHandle(24);
23
24 /// The UUID used for the GAP service (Assigned Numbers 3.4.1 Services by Name)
25 pub const GAP_SERVICE_UUID: Uuid = Uuid::new(0x1800);
26 /// The UUID used for the Device Name characteristic (Assigned Numbers 3.8.1 Characteristics by Name)
27 pub const DEVICE_NAME_UUID: Uuid = Uuid::new(0x2A00);
28 /// The UUID used for the Device Appearance characteristic (Assigned Numbers 3.8.1 Characteristics by Name)
29 pub const DEVICE_APPEARANCE_UUID: Uuid = Uuid::new(0x2A01);
30
31 #[async_trait(?Send)]
32 impl GattDatastore for GapService {
read( &self, _: TransportIndex, handle: AttHandle, _: AttributeBackingType, ) -> Result<Vec<u8>, AttErrorCode>33 async fn read(
34 &self,
35 _: TransportIndex,
36 handle: AttHandle,
37 _: AttributeBackingType,
38 ) -> Result<Vec<u8>, AttErrorCode> {
39 match handle {
40 DEVICE_NAME_HANDLE => {
41 // for non-bonded peers, don't let them read the device name
42 // TODO(aryarahul): support discoverability, when we make this the main GATT server
43 Err(AttErrorCode::InsufficientAuthentication)
44 }
45 // 0x0000 from AssignedNumbers => "Unknown"
46 DEVICE_APPEARANCE_HANDLE => Ok(vec![0x00, 0x00]),
47 _ => unreachable!("unexpected handle read"),
48 }
49 }
50
write( &self, _: TransportIndex, _: AttHandle, _: AttributeBackingType, _: &[u8], ) -> Result<(), AttErrorCode>51 async fn write(
52 &self,
53 _: TransportIndex,
54 _: AttHandle,
55 _: AttributeBackingType,
56 _: &[u8],
57 ) -> Result<(), AttErrorCode> {
58 unreachable!("no GAP data should be writable")
59 }
60 }
61
62 /// Register the GAP service in the provided GATT database.
register_gap_service(database: &mut GattDatabase) -> Result<()>63 pub fn register_gap_service(database: &mut GattDatabase) -> Result<()> {
64 database.add_service_with_handles(
65 // GAP Service
66 GattServiceWithHandle {
67 handle: GAP_SERVICE_HANDLE,
68 type_: GAP_SERVICE_UUID,
69 // Device Name
70 characteristics: vec![
71 GattCharacteristicWithHandle {
72 handle: DEVICE_NAME_HANDLE,
73 type_: DEVICE_NAME_UUID,
74 permissions: AttPermissions::READABLE,
75 descriptors: vec![],
76 },
77 // Appearance
78 GattCharacteristicWithHandle {
79 handle: DEVICE_APPEARANCE_HANDLE,
80 type_: DEVICE_APPEARANCE_UUID,
81 permissions: AttPermissions::READABLE,
82 descriptors: vec![],
83 },
84 ],
85 },
86 Rc::new(GapService),
87 )
88 }
89
90 #[cfg(test)]
91 mod test {
92 use super::*;
93
94 use crate::core::shared_box::SharedBox;
95 use crate::gatt::server::att_database::AttDatabase;
96 use crate::gatt::server::gatt_database::{
97 GattDatabase, CHARACTERISTIC_UUID, PRIMARY_SERVICE_DECLARATION_UUID,
98 };
99 use crate::utils::task::block_on_locally;
100
101 const TCB_IDX: TransportIndex = TransportIndex(1);
102
init_dbs() -> (SharedBox<GattDatabase>, impl AttDatabase)103 fn init_dbs() -> (SharedBox<GattDatabase>, impl AttDatabase) {
104 let mut gatt_database = GattDatabase::new();
105 register_gap_service(&mut gatt_database).unwrap();
106 let gatt_database = SharedBox::new(gatt_database);
107 let att_database = gatt_database.get_att_database(TCB_IDX);
108 (gatt_database, att_database)
109 }
110
111 #[test]
test_gap_service_discovery()112 fn test_gap_service_discovery() {
113 // arrange
114 let (_gatt_db, att_db) = init_dbs();
115
116 // act: discover all services
117 let attrs = att_db.list_attributes();
118
119 // assert: 1 service + (2 characteristics) * (declaration + value attrs) = 5 attrs
120 assert_eq!(attrs.len(), 5);
121 // assert: value handles are correct
122 assert_eq!(attrs[0].handle, GAP_SERVICE_HANDLE);
123 assert_eq!(attrs[2].handle, DEVICE_NAME_HANDLE);
124 assert_eq!(attrs[4].handle, DEVICE_APPEARANCE_HANDLE);
125 // assert: types are correct
126 assert_eq!(attrs[0].type_, PRIMARY_SERVICE_DECLARATION_UUID);
127 assert_eq!(attrs[1].type_, CHARACTERISTIC_UUID);
128 assert_eq!(attrs[2].type_, DEVICE_NAME_UUID);
129 assert_eq!(attrs[3].type_, CHARACTERISTIC_UUID);
130 assert_eq!(attrs[4].type_, DEVICE_APPEARANCE_UUID);
131 // assert: permissions of value attrs are correct
132 assert_eq!(attrs[2].permissions, AttPermissions::READABLE);
133 assert_eq!(attrs[4].permissions, AttPermissions::READABLE);
134 }
135
136 #[test]
test_read_device_name_not_discoverable()137 fn test_read_device_name_not_discoverable() {
138 // arrange
139 let (_gatt_db, att_db) = init_dbs();
140
141 // act: try to read the device name
142 let name = block_on_locally(att_db.read_attribute(DEVICE_NAME_HANDLE));
143
144 // assert: the name is not readable
145 assert_eq!(name, Err(AttErrorCode::InsufficientAuthentication));
146 }
147
148 #[test]
test_read_device_appearance()149 fn test_read_device_appearance() {
150 // arrange
151 let (_gatt_db, att_db) = init_dbs();
152
153 // act: try to read the device name
154 let name = block_on_locally(att_db.read_attribute(DEVICE_APPEARANCE_HANDLE));
155
156 // assert: the name is not readable
157 assert_eq!(name, Ok(vec![0x00, 0x00]));
158 }
159 }
160