// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! BLE advertisements. use crate::wrapper::assigned_numbers::{COMPANY_IDS, SERVICE_IDS}; use crate::wrapper::core::{Uuid128, Uuid16, Uuid32}; use itertools::Itertools; use nom::{combinator, multi, number}; use std::fmt; use strum::IntoEnumIterator; /// The numeric code for a common data type. /// /// For known types, see [CommonDataType], or use this type directly for non-assigned codes. #[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)] pub struct CommonDataTypeCode(u8); impl From for CommonDataTypeCode { fn from(value: CommonDataType) -> Self { let byte = match value { CommonDataType::Flags => 0x01, CommonDataType::IncompleteListOf16BitServiceClassUuids => 0x02, CommonDataType::CompleteListOf16BitServiceClassUuids => 0x03, CommonDataType::IncompleteListOf32BitServiceClassUuids => 0x04, CommonDataType::CompleteListOf32BitServiceClassUuids => 0x05, CommonDataType::IncompleteListOf128BitServiceClassUuids => 0x06, CommonDataType::CompleteListOf128BitServiceClassUuids => 0x07, CommonDataType::ShortenedLocalName => 0x08, CommonDataType::CompleteLocalName => 0x09, CommonDataType::TxPowerLevel => 0x0A, CommonDataType::ClassOfDevice => 0x0D, CommonDataType::SimplePairingHashC192 => 0x0E, CommonDataType::SimplePairingRandomizerR192 => 0x0F, // These two both really have type code 0x10! D: CommonDataType::DeviceId => 0x10, CommonDataType::SecurityManagerTkValue => 0x10, CommonDataType::SecurityManagerOutOfBandFlags => 0x11, CommonDataType::PeripheralConnectionIntervalRange => 0x12, CommonDataType::ListOf16BitServiceSolicitationUuids => 0x14, CommonDataType::ListOf128BitServiceSolicitationUuids => 0x15, CommonDataType::ServiceData16BitUuid => 0x16, CommonDataType::PublicTargetAddress => 0x17, CommonDataType::RandomTargetAddress => 0x18, CommonDataType::Appearance => 0x19, CommonDataType::AdvertisingInterval => 0x1A, CommonDataType::LeBluetoothDeviceAddress => 0x1B, CommonDataType::LeRole => 0x1C, CommonDataType::SimplePairingHashC256 => 0x1D, CommonDataType::SimplePairingRandomizerR256 => 0x1E, CommonDataType::ListOf32BitServiceSolicitationUuids => 0x1F, CommonDataType::ServiceData32BitUuid => 0x20, CommonDataType::ServiceData128BitUuid => 0x21, CommonDataType::LeSecureConnectionsConfirmationValue => 0x22, CommonDataType::LeSecureConnectionsRandomValue => 0x23, CommonDataType::Uri => 0x24, CommonDataType::IndoorPositioning => 0x25, CommonDataType::TransportDiscoveryData => 0x26, CommonDataType::LeSupportedFeatures => 0x27, CommonDataType::ChannelMapUpdateIndication => 0x28, CommonDataType::PbAdv => 0x29, CommonDataType::MeshMessage => 0x2A, CommonDataType::MeshBeacon => 0x2B, CommonDataType::BigInfo => 0x2C, CommonDataType::BroadcastCode => 0x2D, CommonDataType::ResolvableSetIdentifier => 0x2E, CommonDataType::AdvertisingIntervalLong => 0x2F, CommonDataType::ThreeDInformationData => 0x3D, CommonDataType::ManufacturerSpecificData => 0xFF, }; Self(byte) } } impl From for CommonDataTypeCode { fn from(value: u8) -> Self { Self(value) } } impl From for u8 { fn from(value: CommonDataTypeCode) -> Self { value.0 } } /// Data types for assigned type codes. /// /// See Bluetooth Assigned Numbers ยง 2.3 #[derive(Debug, Clone, Copy, PartialEq, Eq, strum_macros::EnumIter)] #[allow(missing_docs)] pub enum CommonDataType { Flags, IncompleteListOf16BitServiceClassUuids, CompleteListOf16BitServiceClassUuids, IncompleteListOf32BitServiceClassUuids, CompleteListOf32BitServiceClassUuids, IncompleteListOf128BitServiceClassUuids, CompleteListOf128BitServiceClassUuids, ShortenedLocalName, CompleteLocalName, TxPowerLevel, ClassOfDevice, SimplePairingHashC192, SimplePairingRandomizerR192, DeviceId, SecurityManagerTkValue, SecurityManagerOutOfBandFlags, PeripheralConnectionIntervalRange, ListOf16BitServiceSolicitationUuids, ListOf128BitServiceSolicitationUuids, ServiceData16BitUuid, PublicTargetAddress, RandomTargetAddress, Appearance, AdvertisingInterval, LeBluetoothDeviceAddress, LeRole, SimplePairingHashC256, SimplePairingRandomizerR256, ListOf32BitServiceSolicitationUuids, ServiceData32BitUuid, ServiceData128BitUuid, LeSecureConnectionsConfirmationValue, LeSecureConnectionsRandomValue, Uri, IndoorPositioning, TransportDiscoveryData, LeSupportedFeatures, ChannelMapUpdateIndication, PbAdv, MeshMessage, MeshBeacon, BigInfo, BroadcastCode, ResolvableSetIdentifier, AdvertisingIntervalLong, ThreeDInformationData, ManufacturerSpecificData, } impl CommonDataType { /// Iterate over the zero, one, or more matching types for the provided code. /// /// `0x10` maps to both Device Id and Security Manager TK Value, so multiple matching types /// may exist for a single code. pub fn for_type_code(code: CommonDataTypeCode) -> impl Iterator { Self::iter().filter(move |t| CommonDataTypeCode::from(*t) == code) } /// Apply type-specific human-oriented formatting to data, if any is applicable pub fn format_data(&self, data: &[u8]) -> Option { match self { Self::Flags => Some(Flags::matching(data).map(|f| format!("{:?}", f)).join(",")), Self::CompleteListOf16BitServiceClassUuids | Self::IncompleteListOf16BitServiceClassUuids | Self::ListOf16BitServiceSolicitationUuids => { combinator::complete(multi::many0(Uuid16::parse_le))(data) .map(|(_res, uuids)| { uuids .into_iter() .map(|uuid| { SERVICE_IDS .get(&uuid) .map(|name| format!("{:?} ({name})", uuid)) .unwrap_or_else(|| format!("{:?}", uuid)) }) .join(", ") }) .ok() } Self::CompleteListOf32BitServiceClassUuids | Self::IncompleteListOf32BitServiceClassUuids | Self::ListOf32BitServiceSolicitationUuids => { combinator::complete(multi::many0(Uuid32::parse))(data) .map(|(_res, uuids)| uuids.into_iter().map(|u| format!("{:?}", u)).join(", ")) .ok() } Self::CompleteListOf128BitServiceClassUuids | Self::IncompleteListOf128BitServiceClassUuids | Self::ListOf128BitServiceSolicitationUuids => { combinator::complete(multi::many0(Uuid128::parse_le))(data) .map(|(_res, uuids)| uuids.into_iter().map(|u| format!("{:?}", u)).join(", ")) .ok() } Self::ServiceData16BitUuid => Uuid16::parse_le(data) .map(|(rem, uuid)| { format!( "service={:?}, data={}", SERVICE_IDS .get(&uuid) .map(|name| format!("{:?} ({name})", uuid)) .unwrap_or_else(|| format!("{:?}", uuid)), hex::encode_upper(rem) ) }) .ok(), Self::ServiceData32BitUuid => Uuid32::parse(data) .map(|(rem, uuid)| format!("service={:?}, data={}", uuid, hex::encode_upper(rem))) .ok(), Self::ServiceData128BitUuid => Uuid128::parse_le(data) .map(|(rem, uuid)| format!("service={:?}, data={}", uuid, hex::encode_upper(rem))) .ok(), Self::ShortenedLocalName | Self::CompleteLocalName => { std::str::from_utf8(data).ok().map(|s| format!("\"{}\"", s)) } Self::TxPowerLevel => { let (_, tx) = combinator::complete(number::complete::i8::<_, nom::error::Error<_>>)(data) .ok()?; Some(tx.to_string()) } Self::ManufacturerSpecificData => { let (rem, id) = Uuid16::parse_le(data).ok()?; Some(format!( "company={}, data=0x{}", COMPANY_IDS .get(&id) .map(|s| s.to_string()) .unwrap_or_else(|| format!("{:?}", id)), hex::encode_upper(rem) )) } _ => None, } } } impl fmt::Display for CommonDataType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CommonDataType::Flags => write!(f, "Flags"), CommonDataType::IncompleteListOf16BitServiceClassUuids => { write!(f, "Incomplete List of 16-bit Service Class UUIDs") } CommonDataType::CompleteListOf16BitServiceClassUuids => { write!(f, "Complete List of 16-bit Service Class UUIDs") } CommonDataType::IncompleteListOf32BitServiceClassUuids => { write!(f, "Incomplete List of 32-bit Service Class UUIDs") } CommonDataType::CompleteListOf32BitServiceClassUuids => { write!(f, "Complete List of 32-bit Service Class UUIDs") } CommonDataType::ListOf16BitServiceSolicitationUuids => { write!(f, "List of 16-bit Service Solicitation UUIDs") } CommonDataType::ListOf32BitServiceSolicitationUuids => { write!(f, "List of 32-bit Service Solicitation UUIDs") } CommonDataType::ListOf128BitServiceSolicitationUuids => { write!(f, "List of 128-bit Service Solicitation UUIDs") } CommonDataType::IncompleteListOf128BitServiceClassUuids => { write!(f, "Incomplete List of 128-bit Service Class UUIDs") } CommonDataType::CompleteListOf128BitServiceClassUuids => { write!(f, "Complete List of 128-bit Service Class UUIDs") } CommonDataType::ShortenedLocalName => write!(f, "Shortened Local Name"), CommonDataType::CompleteLocalName => write!(f, "Complete Local Name"), CommonDataType::TxPowerLevel => write!(f, "TX Power Level"), CommonDataType::ClassOfDevice => write!(f, "Class of Device"), CommonDataType::SimplePairingHashC192 => { write!(f, "Simple Pairing Hash C-192") } CommonDataType::SimplePairingHashC256 => { write!(f, "Simple Pairing Hash C 256") } CommonDataType::SimplePairingRandomizerR192 => { write!(f, "Simple Pairing Randomizer R-192") } CommonDataType::SimplePairingRandomizerR256 => { write!(f, "Simple Pairing Randomizer R 256") } CommonDataType::DeviceId => write!(f, "Device Id"), CommonDataType::SecurityManagerTkValue => { write!(f, "Security Manager TK Value") } CommonDataType::SecurityManagerOutOfBandFlags => { write!(f, "Security Manager Out of Band Flags") } CommonDataType::PeripheralConnectionIntervalRange => { write!(f, "Peripheral Connection Interval Range") } CommonDataType::ServiceData16BitUuid => { write!(f, "Service Data 16-bit UUID") } CommonDataType::ServiceData32BitUuid => { write!(f, "Service Data 32-bit UUID") } CommonDataType::ServiceData128BitUuid => { write!(f, "Service Data 128-bit UUID") } CommonDataType::PublicTargetAddress => write!(f, "Public Target Address"), CommonDataType::RandomTargetAddress => write!(f, "Random Target Address"), CommonDataType::Appearance => write!(f, "Appearance"), CommonDataType::AdvertisingInterval => write!(f, "Advertising Interval"), CommonDataType::LeBluetoothDeviceAddress => { write!(f, "LE Bluetooth Device Address") } CommonDataType::LeRole => write!(f, "LE Role"), CommonDataType::LeSecureConnectionsConfirmationValue => { write!(f, "LE Secure Connections Confirmation Value") } CommonDataType::LeSecureConnectionsRandomValue => { write!(f, "LE Secure Connections Random Value") } CommonDataType::LeSupportedFeatures => write!(f, "LE Supported Features"), CommonDataType::Uri => write!(f, "URI"), CommonDataType::IndoorPositioning => write!(f, "Indoor Positioning"), CommonDataType::TransportDiscoveryData => { write!(f, "Transport Discovery Data") } CommonDataType::ChannelMapUpdateIndication => { write!(f, "Channel Map Update Indication") } CommonDataType::PbAdv => write!(f, "PB-ADV"), CommonDataType::MeshMessage => write!(f, "Mesh Message"), CommonDataType::MeshBeacon => write!(f, "Mesh Beacon"), CommonDataType::BigInfo => write!(f, "BIGIInfo"), CommonDataType::BroadcastCode => write!(f, "Broadcast Code"), CommonDataType::ResolvableSetIdentifier => { write!(f, "Resolvable Set Identifier") } CommonDataType::AdvertisingIntervalLong => { write!(f, "Advertising Interval Long") } CommonDataType::ThreeDInformationData => write!(f, "3D Information Data"), CommonDataType::ManufacturerSpecificData => { write!(f, "Manufacturer Specific Data") } } } } /// Accumulates advertisement data to broadcast on a [crate::wrapper::device::Device]. #[derive(Debug, Clone, Default)] pub struct AdvertisementDataBuilder { encoded_data: Vec, } impl AdvertisementDataBuilder { /// Returns a new, empty instance. pub fn new() -> Self { Self { encoded_data: Vec::new(), } } /// Append advertising data to the builder. /// /// Returns an error if the data cannot be appended. pub fn append( &mut self, type_code: impl Into, data: &[u8], ) -> Result<(), AdvertisementDataBuilderError> { self.encoded_data.push( data.len() .try_into() .ok() .and_then(|len: u8| len.checked_add(1)) .ok_or(AdvertisementDataBuilderError::DataTooLong)?, ); self.encoded_data.push(type_code.into().0); self.encoded_data.extend_from_slice(data); Ok(()) } pub(crate) fn into_bytes(self) -> Vec { self.encoded_data } } /// Errors that can occur when building advertisement data with [AdvertisementDataBuilder]. #[derive(Debug, PartialEq, Eq, thiserror::Error)] pub enum AdvertisementDataBuilderError { /// The provided adv data is too long to be encoded #[error("Data too long")] DataTooLong, } #[derive(PartialEq, Eq, strum_macros::EnumIter)] #[allow(missing_docs)] /// Features in the Flags AD pub enum Flags { LeLimited, LeDiscoverable, NoBrEdr, BrEdrController, BrEdrHost, } impl fmt::Debug for Flags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.short_name()) } } impl Flags { /// Iterates over the flags that are present in the provided `flags` bytes. pub fn matching(flags: &[u8]) -> impl Iterator + '_ { // The encoding is not clear from the spec: do we look at the first byte? or the last? // In practice it's only one byte. let first_byte = flags.first().unwrap_or(&0_u8); Self::iter().filter(move |f| { let mask = match f { Flags::LeLimited => 0x01_u8, Flags::LeDiscoverable => 0x02, Flags::NoBrEdr => 0x04, Flags::BrEdrController => 0x08, Flags::BrEdrHost => 0x10, }; mask & first_byte > 0 }) } /// An abbreviated form of the flag name. /// /// See [Flags::name] for the full name. pub fn short_name(&self) -> &'static str { match self { Flags::LeLimited => "LE Limited", Flags::LeDiscoverable => "LE General", Flags::NoBrEdr => "No BR/EDR", Flags::BrEdrController => "BR/EDR C", Flags::BrEdrHost => "BR/EDR H", } } /// The human-readable name of the flag. /// /// See [Flags::short_name] for a shorter string for use if compactness is important. pub fn name(&self) -> &'static str { match self { Flags::LeLimited => "LE Limited Discoverable Mode", Flags::LeDiscoverable => "LE General Discoverable Mode", Flags::NoBrEdr => "BR/EDR Not Supported", Flags::BrEdrController => "Simultaneous LE and BR/EDR (Controller)", Flags::BrEdrHost => "Simultaneous LE and BR/EDR (Host)", } } }