blob: 6f84cc5b688184ede0253c852e08cbc29d160519 [file] [log] [blame]
// 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<CommonDataType> 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<u8> for CommonDataTypeCode {
fn from(value: u8) -> Self {
Self(value)
}
}
impl From<CommonDataTypeCode> 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<Item = CommonDataType> {
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<String> {
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<u8>,
}
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<CommonDataTypeCode>,
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<u8> {
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<Item = Self> + '_ {
// 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)",
}
}
}