1 //! This library provides helper functions to parse info from advertising data.
2
3 use std::collections::HashMap;
4
5 use bt_topshim::bindings::root::bluetooth::Uuid;
6
7 // Advertising data types.
8 const FLAGS: u8 = 0x01;
9 const COMPLETE_LIST_16_BIT_SERVICE_UUIDS: u8 = 0x03;
10 const COMPLETE_LIST_32_BIT_SERVICE_UUIDS: u8 = 0x05;
11 const COMPLETE_LIST_128_BIT_SERVICE_UUIDS: u8 = 0x07;
12 const SHORTENED_LOCAL_NAME: u8 = 0x08;
13 const COMPLETE_LOCAL_NAME: u8 = 0x09;
14 const SERVICE_DATA_16_BIT_UUID: u8 = 0x16;
15 const SERVICE_DATA_32_BIT_UUID: u8 = 0x20;
16 const SERVICE_DATA_128_BIT_UUID: u8 = 0x21;
17 const MANUFACTURER_SPECIFIC_DATA: u8 = 0xff;
18
19 struct AdvDataIterator<'a> {
20 data: &'a [u8],
21 data_type: u8,
22 cur: usize, // to keep current position
23 }
24
25 // Iterates over Advertising Data's elements having the given AD type. `next()`
26 // returns the next slice of the advertising data element excluding the length
27 // and type.
28 impl<'a> Iterator for AdvDataIterator<'a> {
29 type Item = &'a [u8];
next(&mut self) -> Option<&'a [u8]>30 fn next(&mut self) -> Option<&'a [u8]> {
31 let mut i = self.cur;
32 while i < self.data.len() {
33 let len: usize = self.data[i].into();
34 if (len == 0) || (i + len >= self.data.len()) {
35 break;
36 }
37 if self.data[i + 1] == self.data_type {
38 self.cur = i + len + 1;
39 return Some(&self.data[i + 2..self.cur]);
40 }
41 i += len + 1;
42 }
43 None
44 }
45 }
46
iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator47 fn iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator {
48 AdvDataIterator { data, data_type, cur: 0 }
49 }
50
51 // Helper function to extract flags from advertising data
extract_flags(bytes: &[u8]) -> u852 pub fn extract_flags(bytes: &[u8]) -> u8 {
53 iterate_adv_data(bytes, FLAGS).next().map_or(0, |v| v[0])
54 }
55
56 // Helper function to extract service uuids (128bit) from advertising data
extract_service_uuids(bytes: &[u8]) -> Vec<Uuid>57 pub fn extract_service_uuids(bytes: &[u8]) -> Vec<Uuid> {
58 iterate_adv_data(bytes, COMPLETE_LIST_16_BIT_SERVICE_UUIDS)
59 .flat_map(|slice| slice.chunks(2))
60 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok())
61 .chain(
62 iterate_adv_data(bytes, COMPLETE_LIST_32_BIT_SERVICE_UUIDS)
63 .flat_map(|slice| slice.chunks(4))
64 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok()),
65 )
66 .chain(
67 iterate_adv_data(bytes, COMPLETE_LIST_128_BIT_SERVICE_UUIDS)
68 .flat_map(|slice| slice.chunks(16))
69 .filter_map(|chunk| Uuid::try_from_little_endian(chunk).ok()),
70 )
71 .collect()
72 }
73
74 // Helper function to extract name from advertising data
extract_name(bytes: &[u8]) -> String75 pub fn extract_name(bytes: &[u8]) -> String {
76 iterate_adv_data(bytes, COMPLETE_LOCAL_NAME)
77 .next()
78 .or(iterate_adv_data(bytes, SHORTENED_LOCAL_NAME).next())
79 .map_or("".to_string(), |v| String::from_utf8_lossy(v).to_string())
80 }
81
82 // Helper function to extract service data from advertising data
extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>>83 pub fn extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>> {
84 iterate_adv_data(bytes, SERVICE_DATA_16_BIT_UUID)
85 .filter_map(|slice| {
86 Uuid::try_from_little_endian(slice.get(0..2)?)
87 .ok()
88 .map(|uuid| (uuid.to_string(), slice[2..].to_vec()))
89 })
90 .chain(iterate_adv_data(bytes, SERVICE_DATA_32_BIT_UUID).filter_map(|slice| {
91 Uuid::try_from_little_endian(slice.get(0..4)?)
92 .ok()
93 .map(|uuid| (uuid.to_string(), slice[4..].to_vec()))
94 }))
95 .chain(iterate_adv_data(bytes, SERVICE_DATA_128_BIT_UUID).filter_map(|slice| {
96 Uuid::try_from_little_endian(slice.get(0..16)?)
97 .ok()
98 .map(|uuid| (uuid.to_string(), slice[16..].to_vec()))
99 }))
100 .collect()
101 }
102
103 // Helper function to extract manufacturer data from advertising data
extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>>104 pub fn extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>> {
105 iterate_adv_data(bytes, MANUFACTURER_SPECIFIC_DATA)
106 .filter_map(|slice| {
107 slice.get(0..2)?.try_into().ok().map(|be| (u16::from_be_bytes(be), slice[2..].to_vec()))
108 })
109 .collect()
110 }
111
112 #[cfg(test)]
113 mod tests {
114 use super::*;
115
116 #[test]
test_extract_flags()117 fn test_extract_flags() {
118 let payload: Vec<u8> = vec![
119 2,
120 FLAGS,
121 3,
122 17,
123 COMPLETE_LIST_128_BIT_SERVICE_UUIDS,
124 0,
125 1,
126 2,
127 3,
128 4,
129 5,
130 6,
131 7,
132 8,
133 9,
134 10,
135 11,
136 12,
137 13,
138 14,
139 15,
140 ];
141 let flags = extract_flags(payload.as_slice());
142 assert_eq!(flags, 3);
143 }
144
145 #[test]
test_extract_service_uuids()146 fn test_extract_service_uuids() {
147 let payload: Vec<u8> = vec![2, FLAGS, 3];
148 let uuids = extract_service_uuids(payload.as_slice());
149 assert_eq!(uuids.len(), 0);
150
151 let payload: Vec<u8> = vec![
152 2,
153 FLAGS,
154 3,
155 3,
156 COMPLETE_LIST_16_BIT_SERVICE_UUIDS,
157 0x2C,
158 0xFE,
159 5,
160 COMPLETE_LIST_32_BIT_SERVICE_UUIDS,
161 2,
162 3,
163 4,
164 5,
165 17,
166 COMPLETE_LIST_128_BIT_SERVICE_UUIDS,
167 0,
168 1,
169 2,
170 3,
171 4,
172 5,
173 6,
174 7,
175 8,
176 9,
177 10,
178 11,
179 12,
180 13,
181 14,
182 15,
183 ];
184 let uuids = extract_service_uuids(payload.as_slice());
185 assert_eq!(uuids.len(), 3);
186 assert_eq!(
187 uuids[0],
188 Uuid::from([
189 0x0, 0x0, 0xFE, 0x2C, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34,
190 0xfb
191 ])
192 );
193 assert_eq!(
194 uuids[1],
195 Uuid::from([
196 0x5, 0x4, 0x3, 0x2, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34,
197 0xfb
198 ])
199 );
200 assert_eq!(uuids[2], Uuid::from([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]));
201 }
202
203 #[test]
test_extract_name()204 fn test_extract_name() {
205 let payload: Vec<u8> = vec![2, FLAGS, 3];
206 let name = extract_name(payload.as_slice());
207 assert_eq!(name, "");
208
209 let payload: Vec<u8> = vec![2, FLAGS, 3, 5, COMPLETE_LOCAL_NAME, 116, 101, 115, 116];
210 let name = extract_name(payload.as_slice());
211 assert_eq!(name, "test");
212
213 let payload: Vec<u8> = vec![2, FLAGS, 3, 5, SHORTENED_LOCAL_NAME, 116, 101, 115, 116];
214 let name = extract_name(payload.as_slice());
215 assert_eq!(name, "test");
216 }
217
218 #[test]
test_extract_service_data()219 fn test_extract_service_data() {
220 let payload: Vec<u8> = vec![2, FLAGS, 3];
221 let service_data = extract_service_data(payload.as_slice());
222 assert_eq!(service_data.len(), 0);
223
224 let payload: Vec<u8> = vec![
225 4,
226 SERVICE_DATA_16_BIT_UUID,
227 0x2C,
228 0xFE,
229 0xFF,
230 6,
231 SERVICE_DATA_32_BIT_UUID,
232 2,
233 3,
234 4,
235 5,
236 0xFE,
237 18,
238 SERVICE_DATA_128_BIT_UUID,
239 0,
240 1,
241 2,
242 3,
243 4,
244 5,
245 6,
246 7,
247 8,
248 9,
249 10,
250 11,
251 12,
252 13,
253 14,
254 15,
255 16,
256 17,
257 SERVICE_DATA_128_BIT_UUID,
258 1,
259 2,
260 3,
261 4,
262 5,
263 6,
264 7,
265 8,
266 9,
267 10,
268 11,
269 12,
270 13,
271 14,
272 15,
273 16,
274 ];
275 let service_data = extract_service_data(payload.as_slice());
276 assert_eq!(service_data.len(), 4);
277 let expected_uuid = Uuid::from([
278 0x0, 0x0, 0xFE, 0x2C, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
279 ])
280 .to_string();
281 assert_eq!(service_data.get(&expected_uuid), Some(&vec![0xFF]));
282 let expected_uuid = Uuid::from([
283 0x5, 0x4, 0x3, 0x2, 0x0, 0x0, 0x10, 0x0, 0x80, 0x0, 0x0, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
284 ])
285 .to_string();
286 assert_eq!(service_data.get(&expected_uuid), Some(&vec![0xFE]));
287 let expected_uuid =
288 Uuid::from([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]).to_string();
289 assert_eq!(service_data.get(&expected_uuid), Some(&vec![16]));
290 let expected_uuid =
291 Uuid::from([16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]).to_string();
292 assert_eq!(service_data.get(&expected_uuid), Some(&vec![]));
293 }
294
295 #[test]
test_extract_manufacturer_data()296 fn test_extract_manufacturer_data() {
297 let payload: Vec<u8> = vec![2, FLAGS, 3];
298 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
299 assert_eq!(manufacturer_data.len(), 0);
300
301 let payload: Vec<u8> = vec![2, MANUFACTURER_SPECIFIC_DATA, 0];
302 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
303 assert_eq!(manufacturer_data.len(), 0);
304
305 let payload: Vec<u8> =
306 vec![4, MANUFACTURER_SPECIFIC_DATA, 0, 1, 2, 3, MANUFACTURER_SPECIFIC_DATA, 1, 2];
307 let manufacturer_data = extract_manufacturer_data(payload.as_slice());
308 assert_eq!(manufacturer_data.len(), 2);
309 assert_eq!(manufacturer_data.get(&1), Some(&vec![2]));
310 assert_eq!(manufacturer_data.get(&258), Some(&vec![]));
311 }
312 }
313