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