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