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