Loading system/gd/rust/linux/client/src/dbus_iface.rs +5 −0 Original line number Diff line number Diff line Loading @@ -237,6 +237,7 @@ struct ScanFilterDBus { #[dbus_propmap(ScanResult)] struct ScanResultDBus { name: String, address: String, addr_type: u8, event_type: u16, Loading @@ -246,6 +247,10 @@ struct ScanResultDBus { tx_power: i8, rssi: i8, periodic_adv_int: u16, flags: u8, service_uuids: Vec<Uuid128Bit>, service_data: HashMap<String, Vec<u8>>, manufacturer_data: HashMap<u16, Vec<u8>>, adv_data: Vec<u8>, } Loading system/gd/rust/linux/service/src/iface_bluetooth_gatt.rs +5 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ struct ScanSettingsDBus { #[dbus_propmap(ScanResult)] struct ScanResultDBus { name: String, address: String, addr_type: u8, event_type: u16, Loading @@ -219,6 +220,10 @@ struct ScanResultDBus { tx_power: i8, rssi: i8, periodic_adv_int: u16, flags: u8, service_uuids: Vec<Uuid128Bit>, service_data: HashMap<String, Vec<u8>>, manufacturer_data: HashMap<u16, Vec<u8>>, adv_data: Vec<u8>, } Loading system/gd/rust/linux/stack/src/bluetooth_gatt.rs +12 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ use bt_topshim::profiles::gatt::{ GattServerCallbacksDispatcher, GattStatus, LePhy, }; use bt_topshim::topstack; use bt_utils::adv_parser; use crate::bluetooth::{Bluetooth, IBluetooth}; use crate::bluetooth_adv::{ Loading Loading @@ -631,6 +632,7 @@ pub struct ScanSettings { /// Represents scan result #[derive(Debug)] pub struct ScanResult { pub name: String, pub address: String, pub addr_type: u8, pub event_type: u16, Loading @@ -640,6 +642,11 @@ pub struct ScanResult { pub tx_power: i8, pub rssi: i8, pub periodic_adv_int: u16, pub flags: u8, pub service_uuids: Vec<Uuid128Bit>, /// A map of 128-bit UUID and its corresponding service data. pub service_data: HashMap<String, Vec<u8>>, pub manufacturer_data: HashMap<u16, Vec<u8>>, pub adv_data: Vec<u8>, } Loading Loading @@ -2314,6 +2321,7 @@ impl BtifGattScannerCallbacks for BluetoothGatt { ) { self.scanner_callbacks.for_all_callbacks(|callback| { callback.on_scan_result(ScanResult { name: adv_parser::extract_name(adv_data.as_slice()), address: address.to_string(), addr_type, event_type, Loading @@ -2323,6 +2331,10 @@ impl BtifGattScannerCallbacks for BluetoothGatt { tx_power, rssi, periodic_adv_int, flags: adv_parser::extract_flags(adv_data.as_slice()), service_uuids: adv_parser::extract_service_uuids(adv_data.as_slice()), service_data: adv_parser::extract_service_data(adv_data.as_slice()), manufacturer_data: adv_parser::extract_manufacturer_data(adv_data.as_slice()), adv_data: adv_data.clone(), }); }); Loading system/gd/rust/linux/utils/Cargo.toml +2 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ version = "0.0.1" edition = "2021" [dependencies] bt_topshim = { path = "../../topshim" } libc = "0.2" log = "0.4.14" nix = "0.19" Loading system/gd/rust/linux/utils/src/adv_parser.rs 0 → 100644 +250 −0 Original line number Diff line number Diff line //! This library provides helper functions to parse info from advertising data. use std::collections::HashMap; use bt_topshim::bindings::root::bluetooth::Uuid; use bt_topshim::btif::Uuid128Bit; // Advertising data types. const FLAGS: u8 = 0x01; const COMPLETE_LIST_128_BIT_SERVICE_UUIDS: u8 = 0x07; const SHORTENED_LOCAL_NAME: u8 = 0x08; const COMPLETE_LOCAL_NAME: u8 = 0x09; const SERVICE_DATA_128_BIT_UUID: u8 = 0x21; const MANUFACTURER_SPECIFIC_DATA: u8 = 0xff; struct AdvDataIterator<'a> { data: &'a [u8], data_type: u8, cur: usize, // to keep current position } // Iterates over Advertising Data's elements having the given AD type. `next()` // returns the next slice of the advertising data element excluding the length // and type. impl<'a> Iterator for AdvDataIterator<'a> { type Item = &'a [u8]; fn next(&mut self) -> Option<&'a [u8]> { let mut i = self.cur; while i < self.data.len() { let len: usize = self.data[i].into(); if (len == 0) || (i + len >= self.data.len()) { break; } if self.data[i + 1] == self.data_type { self.cur = i + len + 1; return Some(&self.data[i + 2..self.cur]); } i += len + 1; } None } } fn iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator { AdvDataIterator { data, data_type, cur: 0 } } // Helper function to extract flags from advertising data pub fn extract_flags(bytes: &[u8]) -> u8 { iterate_adv_data(bytes, FLAGS).next().map_or(0, |v| v[0]) } // Helper function to extract service uuids (128bit) from advertising data pub fn extract_service_uuids(bytes: &[u8]) -> Vec<Uuid128Bit> { iterate_adv_data(bytes, COMPLETE_LIST_128_BIT_SERVICE_UUIDS) .flat_map(|slice| slice.chunks(16)) .filter_map(|chunk| Uuid::try_from(chunk.to_vec()).ok().map(|uuid| uuid.uu)) .collect() } // Helper function to extract name from advertising data pub fn extract_name(bytes: &[u8]) -> String { iterate_adv_data(bytes, COMPLETE_LOCAL_NAME) .next() .or(iterate_adv_data(bytes, SHORTENED_LOCAL_NAME).next()) .map_or("".to_string(), |v| String::from_utf8_lossy(v).to_string()) } // Helper function to extract service data from advertising data pub fn extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>> { iterate_adv_data(bytes, SERVICE_DATA_128_BIT_UUID) .filter_map(|slice| { Uuid::try_from(slice.get(0..16)?.to_vec()) .ok() .map(|uuid| (uuid.to_string(), slice[16..].to_vec())) }) .collect() } // Helper function to extract manufacturer data from advertising data pub fn extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>> { iterate_adv_data(bytes, MANUFACTURER_SPECIFIC_DATA) .filter_map(|slice| { slice.get(0..2)?.try_into().ok().map(|be| (u16::from_be_bytes(be), slice[2..].to_vec())) }) .collect() } #[cfg(test)] mod tests { use super::*; #[test] fn test_extract_flags() { let payload: Vec<u8> = vec![ 2, FLAGS, 3, 17, COMPLETE_LIST_128_BIT_SERVICE_UUIDS, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; let flags = extract_flags(payload.as_slice()); assert_eq!(flags, 3); } #[test] fn test_extract_service_uuids() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let uuids = extract_service_uuids(payload.as_slice()); assert_eq!(uuids.len(), 0); let payload: Vec<u8> = vec![ 2, FLAGS, 3, 17, COMPLETE_LIST_128_BIT_SERVICE_UUIDS, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; let uuids = extract_service_uuids(payload.as_slice()); assert_eq!(uuids.len(), 1); assert_eq!( uuids[0], Uuid::try_from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]).unwrap().uu ); } #[test] fn test_extract_name() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let name = extract_name(payload.as_slice()); assert_eq!(name, ""); let payload: Vec<u8> = vec![2, FLAGS, 3, 5, COMPLETE_LOCAL_NAME, 116, 101, 115, 116]; let name = extract_name(payload.as_slice()); assert_eq!(name, "test"); let payload: Vec<u8> = vec![2, FLAGS, 3, 5, SHORTENED_LOCAL_NAME, 116, 101, 115, 116]; let name = extract_name(payload.as_slice()); assert_eq!(name, "test"); } #[test] fn test_extract_service_data() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let service_data = extract_service_data(payload.as_slice()); assert_eq!(service_data.len(), 0); let payload: Vec<u8> = vec![ 18, SERVICE_DATA_128_BIT_UUID, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, SERVICE_DATA_128_BIT_UUID, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ]; let service_data = extract_service_data(payload.as_slice()); assert_eq!(service_data.len(), 2); let expected_uuid = Uuid::try_from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) .unwrap() .to_string(); assert_eq!(service_data.get(&expected_uuid), Some(&vec![16])); let expected_uuid = Uuid::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) .unwrap() .to_string(); assert_eq!(service_data.get(&expected_uuid), Some(&vec![])); } #[test] fn test_extract_manufacturer_data() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 0); let payload: Vec<u8> = vec![2, MANUFACTURER_SPECIFIC_DATA, 0]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 0); let payload: Vec<u8> = vec![4, MANUFACTURER_SPECIFIC_DATA, 0, 1, 2, 3, MANUFACTURER_SPECIFIC_DATA, 1, 2]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 2); assert_eq!(manufacturer_data.get(&1), Some(&vec![2])); assert_eq!(manufacturer_data.get(&258), Some(&vec![])); } } Loading
system/gd/rust/linux/client/src/dbus_iface.rs +5 −0 Original line number Diff line number Diff line Loading @@ -237,6 +237,7 @@ struct ScanFilterDBus { #[dbus_propmap(ScanResult)] struct ScanResultDBus { name: String, address: String, addr_type: u8, event_type: u16, Loading @@ -246,6 +247,10 @@ struct ScanResultDBus { tx_power: i8, rssi: i8, periodic_adv_int: u16, flags: u8, service_uuids: Vec<Uuid128Bit>, service_data: HashMap<String, Vec<u8>>, manufacturer_data: HashMap<u16, Vec<u8>>, adv_data: Vec<u8>, } Loading
system/gd/rust/linux/service/src/iface_bluetooth_gatt.rs +5 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ struct ScanSettingsDBus { #[dbus_propmap(ScanResult)] struct ScanResultDBus { name: String, address: String, addr_type: u8, event_type: u16, Loading @@ -219,6 +220,10 @@ struct ScanResultDBus { tx_power: i8, rssi: i8, periodic_adv_int: u16, flags: u8, service_uuids: Vec<Uuid128Bit>, service_data: HashMap<String, Vec<u8>>, manufacturer_data: HashMap<u16, Vec<u8>>, adv_data: Vec<u8>, } Loading
system/gd/rust/linux/stack/src/bluetooth_gatt.rs +12 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ use bt_topshim::profiles::gatt::{ GattServerCallbacksDispatcher, GattStatus, LePhy, }; use bt_topshim::topstack; use bt_utils::adv_parser; use crate::bluetooth::{Bluetooth, IBluetooth}; use crate::bluetooth_adv::{ Loading Loading @@ -631,6 +632,7 @@ pub struct ScanSettings { /// Represents scan result #[derive(Debug)] pub struct ScanResult { pub name: String, pub address: String, pub addr_type: u8, pub event_type: u16, Loading @@ -640,6 +642,11 @@ pub struct ScanResult { pub tx_power: i8, pub rssi: i8, pub periodic_adv_int: u16, pub flags: u8, pub service_uuids: Vec<Uuid128Bit>, /// A map of 128-bit UUID and its corresponding service data. pub service_data: HashMap<String, Vec<u8>>, pub manufacturer_data: HashMap<u16, Vec<u8>>, pub adv_data: Vec<u8>, } Loading Loading @@ -2314,6 +2321,7 @@ impl BtifGattScannerCallbacks for BluetoothGatt { ) { self.scanner_callbacks.for_all_callbacks(|callback| { callback.on_scan_result(ScanResult { name: adv_parser::extract_name(adv_data.as_slice()), address: address.to_string(), addr_type, event_type, Loading @@ -2323,6 +2331,10 @@ impl BtifGattScannerCallbacks for BluetoothGatt { tx_power, rssi, periodic_adv_int, flags: adv_parser::extract_flags(adv_data.as_slice()), service_uuids: adv_parser::extract_service_uuids(adv_data.as_slice()), service_data: adv_parser::extract_service_data(adv_data.as_slice()), manufacturer_data: adv_parser::extract_manufacturer_data(adv_data.as_slice()), adv_data: adv_data.clone(), }); }); Loading
system/gd/rust/linux/utils/Cargo.toml +2 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ version = "0.0.1" edition = "2021" [dependencies] bt_topshim = { path = "../../topshim" } libc = "0.2" log = "0.4.14" nix = "0.19" Loading
system/gd/rust/linux/utils/src/adv_parser.rs 0 → 100644 +250 −0 Original line number Diff line number Diff line //! This library provides helper functions to parse info from advertising data. use std::collections::HashMap; use bt_topshim::bindings::root::bluetooth::Uuid; use bt_topshim::btif::Uuid128Bit; // Advertising data types. const FLAGS: u8 = 0x01; const COMPLETE_LIST_128_BIT_SERVICE_UUIDS: u8 = 0x07; const SHORTENED_LOCAL_NAME: u8 = 0x08; const COMPLETE_LOCAL_NAME: u8 = 0x09; const SERVICE_DATA_128_BIT_UUID: u8 = 0x21; const MANUFACTURER_SPECIFIC_DATA: u8 = 0xff; struct AdvDataIterator<'a> { data: &'a [u8], data_type: u8, cur: usize, // to keep current position } // Iterates over Advertising Data's elements having the given AD type. `next()` // returns the next slice of the advertising data element excluding the length // and type. impl<'a> Iterator for AdvDataIterator<'a> { type Item = &'a [u8]; fn next(&mut self) -> Option<&'a [u8]> { let mut i = self.cur; while i < self.data.len() { let len: usize = self.data[i].into(); if (len == 0) || (i + len >= self.data.len()) { break; } if self.data[i + 1] == self.data_type { self.cur = i + len + 1; return Some(&self.data[i + 2..self.cur]); } i += len + 1; } None } } fn iterate_adv_data(data: &[u8], data_type: u8) -> AdvDataIterator { AdvDataIterator { data, data_type, cur: 0 } } // Helper function to extract flags from advertising data pub fn extract_flags(bytes: &[u8]) -> u8 { iterate_adv_data(bytes, FLAGS).next().map_or(0, |v| v[0]) } // Helper function to extract service uuids (128bit) from advertising data pub fn extract_service_uuids(bytes: &[u8]) -> Vec<Uuid128Bit> { iterate_adv_data(bytes, COMPLETE_LIST_128_BIT_SERVICE_UUIDS) .flat_map(|slice| slice.chunks(16)) .filter_map(|chunk| Uuid::try_from(chunk.to_vec()).ok().map(|uuid| uuid.uu)) .collect() } // Helper function to extract name from advertising data pub fn extract_name(bytes: &[u8]) -> String { iterate_adv_data(bytes, COMPLETE_LOCAL_NAME) .next() .or(iterate_adv_data(bytes, SHORTENED_LOCAL_NAME).next()) .map_or("".to_string(), |v| String::from_utf8_lossy(v).to_string()) } // Helper function to extract service data from advertising data pub fn extract_service_data(bytes: &[u8]) -> HashMap<String, Vec<u8>> { iterate_adv_data(bytes, SERVICE_DATA_128_BIT_UUID) .filter_map(|slice| { Uuid::try_from(slice.get(0..16)?.to_vec()) .ok() .map(|uuid| (uuid.to_string(), slice[16..].to_vec())) }) .collect() } // Helper function to extract manufacturer data from advertising data pub fn extract_manufacturer_data(bytes: &[u8]) -> HashMap<u16, Vec<u8>> { iterate_adv_data(bytes, MANUFACTURER_SPECIFIC_DATA) .filter_map(|slice| { slice.get(0..2)?.try_into().ok().map(|be| (u16::from_be_bytes(be), slice[2..].to_vec())) }) .collect() } #[cfg(test)] mod tests { use super::*; #[test] fn test_extract_flags() { let payload: Vec<u8> = vec![ 2, FLAGS, 3, 17, COMPLETE_LIST_128_BIT_SERVICE_UUIDS, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; let flags = extract_flags(payload.as_slice()); assert_eq!(flags, 3); } #[test] fn test_extract_service_uuids() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let uuids = extract_service_uuids(payload.as_slice()); assert_eq!(uuids.len(), 0); let payload: Vec<u8> = vec![ 2, FLAGS, 3, 17, COMPLETE_LIST_128_BIT_SERVICE_UUIDS, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ]; let uuids = extract_service_uuids(payload.as_slice()); assert_eq!(uuids.len(), 1); assert_eq!( uuids[0], Uuid::try_from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]).unwrap().uu ); } #[test] fn test_extract_name() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let name = extract_name(payload.as_slice()); assert_eq!(name, ""); let payload: Vec<u8> = vec![2, FLAGS, 3, 5, COMPLETE_LOCAL_NAME, 116, 101, 115, 116]; let name = extract_name(payload.as_slice()); assert_eq!(name, "test"); let payload: Vec<u8> = vec![2, FLAGS, 3, 5, SHORTENED_LOCAL_NAME, 116, 101, 115, 116]; let name = extract_name(payload.as_slice()); assert_eq!(name, "test"); } #[test] fn test_extract_service_data() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let service_data = extract_service_data(payload.as_slice()); assert_eq!(service_data.len(), 0); let payload: Vec<u8> = vec![ 18, SERVICE_DATA_128_BIT_UUID, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, SERVICE_DATA_128_BIT_UUID, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ]; let service_data = extract_service_data(payload.as_slice()); assert_eq!(service_data.len(), 2); let expected_uuid = Uuid::try_from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) .unwrap() .to_string(); assert_eq!(service_data.get(&expected_uuid), Some(&vec![16])); let expected_uuid = Uuid::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) .unwrap() .to_string(); assert_eq!(service_data.get(&expected_uuid), Some(&vec![])); } #[test] fn test_extract_manufacturer_data() { let payload: Vec<u8> = vec![2, FLAGS, 3]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 0); let payload: Vec<u8> = vec![2, MANUFACTURER_SPECIFIC_DATA, 0]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 0); let payload: Vec<u8> = vec![4, MANUFACTURER_SPECIFIC_DATA, 0, 1, 2, 3, MANUFACTURER_SPECIFIC_DATA, 1, 2]; let manufacturer_data = extract_manufacturer_data(payload.as_slice()); assert_eq!(manufacturer_data.len(), 2); assert_eq!(manufacturer_data.get(&1), Some(&vec![2])); assert_eq!(manufacturer_data.get(&258), Some(&vec![])); } }