• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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