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