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