| // Copyright 2023 Google LLC |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use bt_common::core::{AdvertisingSetId, PaInterval}; |
| use bt_common::generic_audio::metadata_ltv::*; |
| use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError}; |
| use bt_common::Uuid; |
| |
| const ADDRESS_BYTE_SIZE: usize = 6; |
| const NUM_SUBGROUPS_BYTE_SIZE: usize = 1; |
| const PA_SYNC_BYTE_SIZE: usize = 1; |
| const SOURCE_ID_BYTE_SIZE: usize = 1; |
| |
| /// 16-bit UUID value for the characteristics offered by the Broadcast Audio Scan Service. |
| pub const BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID: Uuid = Uuid::from_u16(0x2BC7); |
| pub const BROADCAST_RECEIVE_STATE_UUID: Uuid = Uuid::from_u16(0x2BC8); |
| |
| pub type SourceId = u8; |
| |
| /// Broadcast_ID is a 3-byte data on the wire. |
| /// See Broadcast Audio Scan Service (BASS) spec v1.0 Table 3.5 for details. |
| #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| pub struct BroadcastId(u32); |
| |
| impl BroadcastId { |
| // On the wire, Broadcast_ID is transported in 3 bytes. |
| const BYTE_SIZE: usize = 3; |
| |
| #[cfg(test)] |
| pub fn new(raw_value: u32) -> Self { |
| Self(raw_value) |
| } |
| |
| pub fn raw_value(&self) -> u32 { |
| self.0 |
| } |
| } |
| |
| impl TryFrom<u32> for BroadcastId { |
| type Error = PacketError; |
| |
| fn try_from(value: u32) -> Result<Self, Self::Error> { |
| const MAX_VALUE: u32 = 0xFFFFFF; |
| if value > MAX_VALUE { |
| return Err(PacketError::InvalidParameter(format!( |
| "Broadcast ID cannot exceed 3 bytes" |
| ))); |
| } |
| Ok(BroadcastId(value)) |
| } |
| } |
| |
| impl Decodable for BroadcastId { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() != Self::BYTE_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| |
| let padded_bytes = [buf[0], buf[1], buf[2], 0x00]; |
| Ok((BroadcastId(u32::from_le_bytes(padded_bytes)), Self::BYTE_SIZE)) |
| } |
| } |
| |
| impl Encodable for BroadcastId { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| // Since 3-byte value is being fit into u32, we ignore the most significant byte. |
| buf[0..3].copy_from_slice(&self.0.to_le_bytes()[0..3]); |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::BYTE_SIZE |
| } |
| } |
| |
| /// Broadcast Audio Scan Control Point characteristic opcode as defined in |
| /// Broadcast Audio Scan Service spec v1.0 Section 3.1. |
| #[repr(u8)] |
| #[derive(Debug, PartialEq)] |
| pub enum ControlPointOpcode { |
| RemoteScanStopped = 0x00, |
| RemoteScanStarted = 0x01, |
| AddSource = 0x02, |
| ModifySource = 0x03, |
| SetBroadcastCode = 0x04, |
| RemoveSource = 0x05, |
| } |
| |
| impl ControlPointOpcode { |
| const BYTE_SIZE: usize = 1; |
| } |
| |
| impl TryFrom<u8> for ControlPointOpcode { |
| type Error = PacketError; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| let val = match value { |
| 0x00 => Self::RemoteScanStopped, |
| 0x01 => Self::RemoteScanStarted, |
| 0x02 => Self::AddSource, |
| 0x03 => Self::ModifySource, |
| 0x04 => Self::SetBroadcastCode, |
| 0x05 => Self::RemoveSource, |
| _ => return Err(PacketError::OutOfRange), |
| }; |
| Ok(val) |
| } |
| } |
| |
| /// Trait for objects that represent a Broadcast Audio Scan Control Point characteristic. |
| /// When written by a client, the Broadcast Audio Scan Control Point characteristic is defined as an |
| /// 8-bit enumerated value, known as the opcode, followed by zero or more parameter octets. |
| /// The opcode represents the operation that would be performed in the Broadcast Audio Scan Service server. |
| /// See BASS spec v1.0 Section 3.1 for details. |
| pub trait ControlPointOperation { |
| // Returns the expected opcode for this operation. |
| fn opcode() -> ControlPointOpcode; |
| |
| // Given the raw encoded value of the opcode, verifies it and returns the equivalent ControlPointOpcode object. |
| fn check_opcode(raw_value: u8) -> Result<ControlPointOpcode, PacketError> { |
| let expected = Self::opcode(); |
| let got = ControlPointOpcode::try_from(raw_value)?; |
| if got != expected { |
| return Err(PacketError::InvalidParameter(format!( |
| "got opcode {got:?}, expected {expected:?}" |
| ))); |
| } |
| Ok(got) |
| } |
| } |
| |
| /// See BASS spec v1.0 Section 3.1.1.2 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct RemoteScanStoppedOperation; |
| |
| impl ControlPointOperation for RemoteScanStoppedOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::RemoteScanStopped |
| } |
| } |
| |
| impl Decodable for RemoteScanStoppedOperation { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < ControlPointOpcode::BYTE_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let _ = Self::check_opcode(buf[0])?; |
| Ok((RemoteScanStoppedOperation, ControlPointOpcode::BYTE_SIZE)) |
| } |
| } |
| |
| impl Encodable for RemoteScanStoppedOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| buf[0] = Self::opcode() as u8; |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| ControlPointOpcode::BYTE_SIZE |
| } |
| } |
| |
| /// See BASS spec v1.0 Section 3.1.1.3 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct RemoteScanStartedOperation; |
| |
| impl ControlPointOperation for RemoteScanStartedOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::RemoteScanStarted |
| } |
| } |
| |
| impl Decodable for RemoteScanStartedOperation { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < ControlPointOpcode::BYTE_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let _ = Self::check_opcode(buf[0])?; |
| Ok((RemoteScanStartedOperation, ControlPointOpcode::BYTE_SIZE)) |
| } |
| } |
| |
| impl Encodable for RemoteScanStartedOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| buf[0] = Self::opcode() as u8; |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| ControlPointOpcode::BYTE_SIZE |
| } |
| } |
| |
| /// See BASS spec v1.0 Section 3.1.1.4 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct AddSourceOperation { |
| advertiser_address_type: AddressType, |
| // Address in little endian. |
| advertiser_address: [u8; ADDRESS_BYTE_SIZE], |
| advertising_sid: AdvertisingSetId, |
| broadcast_id: BroadcastId, |
| pa_sync: PaSync, |
| pa_interval: PaInterval, |
| subgroups: Vec<BigSubgroup>, |
| } |
| |
| impl AddSourceOperation { |
| const MIN_PACKET_SIZE: usize = ControlPointOpcode::BYTE_SIZE |
| + AddressType::BYTE_SIZE |
| + ADDRESS_BYTE_SIZE |
| + AdvertisingSetId::BYTE_SIZE |
| + BroadcastId::BYTE_SIZE |
| + PA_SYNC_BYTE_SIZE |
| + PaInterval::BYTE_SIZE |
| + NUM_SUBGROUPS_BYTE_SIZE; |
| |
| pub fn new( |
| address_type: AddressType, |
| advertiser_address: [u8; ADDRESS_BYTE_SIZE], |
| sid: u8, |
| broadcast_id: u32, |
| pa_sync: PaSync, |
| pa_interval: u16, |
| subgroups: Vec<BigSubgroup>, |
| ) -> Self { |
| AddSourceOperation { |
| advertiser_address_type: address_type, |
| advertiser_address, |
| advertising_sid: AdvertisingSetId(sid), |
| broadcast_id: BroadcastId(broadcast_id), |
| pa_sync, |
| pa_interval: PaInterval(pa_interval), |
| subgroups, |
| } |
| } |
| } |
| |
| impl ControlPointOperation for AddSourceOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::AddSource |
| } |
| } |
| |
| impl Decodable for AddSourceOperation { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < Self::MIN_PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| |
| let _ = Self::check_opcode(buf[0])?; |
| let advertiser_address_type = AddressType::try_from(buf[1])?; |
| let mut advertiser_address = [0; ADDRESS_BYTE_SIZE]; |
| advertiser_address.clone_from_slice(&buf[2..8]); |
| let advertising_sid = AdvertisingSetId(buf[8]); |
| let broadcast_id = BroadcastId::decode(&buf[9..12])?.0; |
| let pa_sync = PaSync::try_from(buf[12])?; |
| let pa_interval = PaInterval(u16::from_le_bytes(buf[13..15].try_into().unwrap())); |
| let num_subgroups = buf[15] as usize; |
| let mut subgroups = Vec::new(); |
| |
| let mut idx: usize = 16; |
| for _i in 0..num_subgroups { |
| if buf.len() <= idx { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let decoded = BigSubgroup::decode(&buf[idx..])?; |
| subgroups.push(decoded.0); |
| idx += decoded.1; |
| } |
| Ok(( |
| Self { |
| advertiser_address_type, |
| advertiser_address, |
| advertising_sid, |
| broadcast_id, |
| pa_sync, |
| pa_interval, |
| subgroups, |
| }, |
| idx, |
| )) |
| } |
| } |
| |
| impl Encodable for AddSourceOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = Self::opcode() as u8; |
| buf[1] = self.advertiser_address_type as u8; |
| buf[2..8].copy_from_slice(&self.advertiser_address); |
| buf[8] = self.advertising_sid.0; |
| self.broadcast_id.encode(&mut buf[9..12])?; |
| buf[12] = self.pa_sync as u8; |
| buf[13..15].copy_from_slice(&self.pa_interval.0.to_le_bytes()); |
| buf[15] = self |
| .subgroups |
| .len() |
| .try_into() |
| .map_err(|_| PacketError::InvalidParameter("Num_Subgroups".to_string()))?; |
| let mut idx = 16; |
| for s in &self.subgroups { |
| s.encode(&mut buf[idx..])?; |
| idx += s.encoded_len(); |
| } |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::MIN_PACKET_SIZE + self.subgroups.iter().fold(0, |acc, g| acc + g.encoded_len()) |
| } |
| } |
| |
| /// See Broadcast Audio Scan Service spec v1.0 Section 3.1.1.5 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct ModifySourceOperation { |
| source_id: SourceId, |
| pa_sync: PaSync, |
| pa_interval: PaInterval, |
| subgroups: Vec<BigSubgroup>, |
| } |
| |
| impl ModifySourceOperation { |
| const MIN_PACKET_SIZE: usize = ControlPointOpcode::BYTE_SIZE |
| + SOURCE_ID_BYTE_SIZE |
| + PA_SYNC_BYTE_SIZE |
| + PaInterval::BYTE_SIZE |
| + NUM_SUBGROUPS_BYTE_SIZE; |
| |
| pub fn new( |
| source_id: SourceId, |
| pa_sync: PaSync, |
| pa_interval: u16, |
| subgroups: Vec<BigSubgroup>, |
| ) -> Self { |
| ModifySourceOperation { |
| source_id, |
| pa_sync, |
| pa_interval: PaInterval(pa_interval), |
| subgroups, |
| } |
| } |
| } |
| |
| impl ControlPointOperation for ModifySourceOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::ModifySource |
| } |
| } |
| |
| impl Decodable for ModifySourceOperation { |
| type Error = PacketError; |
| |
| // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params. |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < Self::MIN_PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let _ = Self::check_opcode(buf[0])?; |
| let source_id = buf[1]; |
| let pa_sync = PaSync::try_from(buf[2])?; |
| let pa_interval = PaInterval(u16::from_le_bytes(buf[3..5].try_into().unwrap())); |
| let num_subgroups = buf[5] as usize; |
| let mut subgroups = Vec::new(); |
| |
| let mut idx = 6; |
| for _i in 0..num_subgroups { |
| if buf.len() < idx + BigSubgroup::MIN_PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let decoded = BigSubgroup::decode(&buf[idx..])?; |
| subgroups.push(decoded.0); |
| idx += decoded.1; |
| } |
| Ok((Self { source_id, pa_sync, pa_interval, subgroups }, idx)) |
| } |
| } |
| |
| impl Encodable for ModifySourceOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = Self::opcode() as u8; |
| buf[1] = self.source_id; |
| buf[2] = self.pa_sync as u8; |
| buf[3..5].copy_from_slice(&self.pa_interval.0.to_le_bytes()); |
| buf[5] = self |
| .subgroups |
| .len() |
| .try_into() |
| .map_err(|_| PacketError::InvalidParameter("Num_Subgroups".to_string()))?; |
| let mut idx = 6; |
| for s in &self.subgroups { |
| s.encode(&mut buf[idx..])?; |
| idx += s.encoded_len(); |
| } |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::MIN_PACKET_SIZE + self.subgroups.iter().fold(0, |acc, g| acc + g.encoded_len()) |
| } |
| } |
| |
| /// See Broadcast Audio Scan Service spec v1.0 Section 3.1.1.6 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct SetBroadcastCodeOperation { |
| source_id: SourceId, |
| broadcast_code: [u8; 16], |
| } |
| |
| impl SetBroadcastCodeOperation { |
| const BROADCAST_CODE_LEN: usize = 16; |
| const PACKET_SIZE: usize = |
| ControlPointOpcode::BYTE_SIZE + SOURCE_ID_BYTE_SIZE + Self::BROADCAST_CODE_LEN; |
| |
| pub fn new(source_id: SourceId, broadcast_code: [u8; 16]) -> Self { |
| SetBroadcastCodeOperation { source_id, broadcast_code } |
| } |
| } |
| |
| impl ControlPointOperation for SetBroadcastCodeOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::SetBroadcastCode |
| } |
| } |
| |
| impl Decodable for SetBroadcastCodeOperation { |
| type Error = PacketError; |
| |
| // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params. |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < Self::PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let _ = Self::check_opcode(buf[0])?; |
| let source_id = buf[1]; |
| let mut broadcast_code = [0; Self::BROADCAST_CODE_LEN]; |
| broadcast_code.copy_from_slice(&buf[2..2 + Self::BROADCAST_CODE_LEN]); |
| Ok((Self { source_id, broadcast_code }, Self::PACKET_SIZE)) |
| } |
| } |
| |
| impl Encodable for SetBroadcastCodeOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = Self::opcode() as u8; |
| buf[1] = self.source_id; |
| buf[2..2 + Self::BROADCAST_CODE_LEN].copy_from_slice(&self.broadcast_code); |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::PACKET_SIZE |
| } |
| } |
| |
| /// See Broadcast Audio Scan Service spec v1.0 Section 3.1.1.7 for details. |
| #[derive(Debug, PartialEq)] |
| pub struct RemoveSourceOperation(SourceId); |
| |
| impl RemoveSourceOperation { |
| const PACKET_SIZE: usize = ControlPointOpcode::BYTE_SIZE + SOURCE_ID_BYTE_SIZE; |
| |
| pub fn new(source_id: SourceId) -> Self { |
| RemoveSourceOperation(source_id) |
| } |
| } |
| |
| impl ControlPointOperation for RemoveSourceOperation { |
| fn opcode() -> ControlPointOpcode { |
| ControlPointOpcode::RemoveSource |
| } |
| } |
| |
| impl Decodable for RemoveSourceOperation { |
| type Error = PacketError; |
| |
| // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params. |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < Self::PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let _ = Self::check_opcode(buf[0])?; |
| let source_id = buf[1]; |
| Ok((RemoveSourceOperation(source_id), Self::PACKET_SIZE)) |
| } |
| } |
| |
| impl Encodable for RemoveSourceOperation { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = Self::opcode() as u8; |
| buf[1] = self.0; |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::PACKET_SIZE |
| } |
| } |
| |
| #[repr(u8)] |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum PaSync { |
| DoNotSync = 0x00, |
| SyncPastAvailable = 0x01, |
| SyncPastUnavailable = 0x02, |
| } |
| |
| impl TryFrom<u8> for PaSync { |
| type Error = PacketError; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0x00 => Ok(Self::DoNotSync), |
| 0x01 => Ok(Self::SyncPastAvailable), |
| 0x02 => Ok(Self::SyncPastUnavailable), |
| _ => Err(PacketError::OutOfRange), |
| } |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub struct BigSubgroup { |
| // 4-octet bitfield. Bit 0-30 = BIS_index[1-31] |
| // 0x00000000: 0b0 = Do not synchronize to BIS_index[x] |
| // 0xxxxxxxxx: 0b1 = Synchronize to BIS_index[x] |
| // 0xFFFFFFFF: means No preference if used in BroadcastAudioScanControlPoint, |
| // Failed to sync if used in ReceiveState. |
| bis_sync_bitfield: u32, |
| metadata: Metadata, |
| } |
| |
| impl BigSubgroup { |
| const BIS_SYNC_BYTE_SIZE: usize = 4; |
| const METADATA_LENGTH_BYTE_SIZE: usize = 1; |
| |
| const MIN_PACKET_SIZE: usize = Self::BIS_SYNC_BYTE_SIZE + Self::METADATA_LENGTH_BYTE_SIZE; |
| const BIS_SYNC_NO_PREFERENCE: u32 = 0xFFFFFFFF; |
| |
| pub fn new(bis_sync_bitfield: Option<u32>) -> Self { |
| Self { |
| bis_sync_bitfield: bis_sync_bitfield.unwrap_or(Self::BIS_SYNC_NO_PREFERENCE), |
| metadata: vec![], |
| } |
| } |
| |
| pub fn with_metadata(self, metadata: Metadata) -> Self { |
| Self { bis_sync_bitfield: self.bis_sync_bitfield, metadata } |
| } |
| } |
| |
| impl Decodable for BigSubgroup { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < BigSubgroup::MIN_PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap()); |
| let metadata_len = buf[4] as usize; |
| let mut metadata = Vec::new(); |
| |
| let mut start_idx = 5; |
| if buf.len() < start_idx + metadata_len { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| |
| while start_idx < 5 + metadata_len { |
| let (data, len) = Metadatum::decode(&buf[start_idx..]) |
| .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?; |
| metadata.push(data); |
| start_idx += len; |
| } |
| if start_idx != 5 + metadata_len { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| Ok((BigSubgroup { bis_sync_bitfield: bis_sync, metadata }, start_idx)) |
| } |
| } |
| |
| impl Encodable for BigSubgroup { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0..4].copy_from_slice(&self.bis_sync_bitfield.to_le_bytes()); |
| let metadata_len = self |
| .metadata |
| .iter() |
| .fold(0, |acc, m| acc + m.encoded_len()) |
| .try_into() |
| .map_err(|_| PacketError::InvalidParameter("Metadata".to_string()))?; |
| buf[4] = metadata_len; |
| let mut next_idx = 5; |
| for m in &self.metadata { |
| m.encode(&mut buf[next_idx..]) |
| .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?; |
| next_idx += m.encoded_len(); |
| } |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| Self::MIN_PACKET_SIZE + self.metadata.iter().map(Encodable::encoded_len).sum::<usize>() |
| } |
| } |
| |
| /// See Broadcast Audio Scan Service spec v1.0 Table 3.5 for details. |
| #[repr(u8)] |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum AddressType { |
| // Public Device Address or Public Identity Address. |
| Public = 0x00, |
| // Random Device Address or Random (static) Identity Address. |
| Random = 0x01, |
| } |
| |
| impl AddressType { |
| const BYTE_SIZE: usize = 1; |
| } |
| |
| impl TryFrom<u8> for AddressType { |
| type Error = PacketError; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0x00 => Ok(Self::Public), |
| 0x01 => Ok(Self::Random), |
| _ => Err(PacketError::OutOfRange), |
| } |
| } |
| } |
| |
| /// Broadcast Receive State characteristic as defined in |
| /// Broadcast Audio Scan Service spec v1.0 Section 3.2. |
| /// The Broadcast Receive State characteristic is used by the server to expose information |
| /// about a Broadcast Source. If the server has not written a Source_ID value to the |
| /// Broadcast Receive State characteristic, the Broadcast Recieve State characteristic value |
| /// shall be empty. |
| #[derive(Debug, PartialEq)] |
| pub enum BroadcastReceiveState { |
| Empty, |
| NonEmpty(ReceiveState), |
| } |
| |
| impl Decodable for BroadcastReceiveState { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() == 0 { |
| return Ok((Self::Empty, 0)); |
| } |
| let res = ReceiveState::decode(&buf[..])?; |
| Ok((Self::NonEmpty(res.0), res.1)) |
| } |
| } |
| |
| impl Encodable for BroadcastReceiveState { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| match self { |
| Self::Empty => Ok(()), |
| Self::NonEmpty(state) => state.encode(&mut buf[..]), |
| } |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| match self { |
| Self::Empty => 0, |
| Self::NonEmpty(state) => state.encoded_len(), |
| } |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| pub struct ReceiveState { |
| source_id: SourceId, |
| source_address_type: AddressType, |
| // Address in little endian. |
| source_address: [u8; ADDRESS_BYTE_SIZE], |
| source_adv_sid: AdvertisingSetId, |
| broadcast_id: BroadcastId, |
| pa_sync_state: PaSyncState, |
| // Represents BIG_Encryption param with optional Bad_Code param. |
| big_encryption: EncryptionStatus, |
| subgroups: Vec<BigSubgroup>, |
| } |
| |
| impl ReceiveState { |
| const MIN_PACKET_SIZE: usize = SOURCE_ID_BYTE_SIZE |
| + AddressType::BYTE_SIZE |
| + ADDRESS_BYTE_SIZE |
| + AdvertisingSetId::BYTE_SIZE |
| + BroadcastId::BYTE_SIZE |
| + PA_SYNC_BYTE_SIZE |
| + EncryptionStatus::MIN_PACKET_SIZE |
| + NUM_SUBGROUPS_BYTE_SIZE; |
| |
| #[cfg(test)] |
| pub fn new( |
| source_id: u8, |
| source_address_type: AddressType, |
| source_address: [u8; ADDRESS_BYTE_SIZE], |
| source_adv_sid: u8, |
| broadcast_id: u32, |
| pa_sync_state: PaSyncState, |
| big_encryption: EncryptionStatus, |
| subgroups: Vec<BigSubgroup>, |
| ) -> ReceiveState { |
| Self { |
| source_id, |
| source_address_type, |
| source_address, |
| source_adv_sid: AdvertisingSetId(source_adv_sid), |
| broadcast_id: BroadcastId(broadcast_id), |
| pa_sync_state, |
| big_encryption, |
| subgroups, |
| } |
| } |
| |
| pub fn pa_sync_state(&self) -> PaSyncState { |
| self.pa_sync_state |
| } |
| |
| pub fn big_encryption(&self) -> EncryptionStatus { |
| self.big_encryption |
| } |
| |
| pub fn broadcast_id(&self) -> BroadcastId { |
| self.broadcast_id |
| } |
| |
| pub fn source_id(&self) -> SourceId { |
| self.source_id |
| } |
| } |
| |
| impl Decodable for ReceiveState { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < Self::MIN_PACKET_SIZE { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| |
| let source_id = buf[0]; |
| let source_address_type = AddressType::try_from(buf[1])?; |
| let mut source_address = [0; ADDRESS_BYTE_SIZE]; |
| source_address.clone_from_slice(&buf[2..8]); |
| let source_adv_sid = AdvertisingSetId(buf[8]); |
| let broadcast_id = BroadcastId::decode(&buf[9..12])?.0; |
| let pa_sync_state = PaSyncState::try_from(buf[12])?; |
| |
| let decoded = EncryptionStatus::decode(&buf[13..])?; |
| let big_encryption = decoded.0; |
| let mut idx = 13 + decoded.1; |
| if buf.len() <= idx { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let num_subgroups = buf[idx] as usize; |
| let mut subgroups = Vec::new(); |
| idx += 1; |
| for _i in 0..num_subgroups { |
| if buf.len() <= idx { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| let (subgroup, consumed) = BigSubgroup::decode(&buf[idx..])?; |
| subgroups.push(subgroup); |
| idx += consumed; |
| } |
| Ok(( |
| ReceiveState { |
| source_id, |
| source_address_type, |
| source_address, |
| source_adv_sid, |
| broadcast_id, |
| pa_sync_state, |
| big_encryption, |
| subgroups, |
| }, |
| idx, |
| )) |
| } |
| } |
| |
| impl Encodable for ReceiveState { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = self.source_id; |
| buf[1] = self.source_address_type as u8; |
| buf[2..8].copy_from_slice(&self.source_address); |
| buf[8] = self.source_adv_sid.0; |
| self.broadcast_id.encode(&mut buf[9..12])?; |
| buf[12] = self.pa_sync_state as u8; |
| let mut idx = 13 + self.big_encryption.encoded_len(); |
| self.big_encryption.encode(&mut buf[13..idx])?; |
| buf[idx] = self |
| .subgroups |
| .len() |
| .try_into() |
| .map_err(|_| PacketError::InvalidParameter("Metadata".to_string()))?; |
| idx += 1; |
| for s in &self.subgroups { |
| s.encode(&mut buf[idx..])?; |
| idx += s.encoded_len(); |
| } |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| // Length including Source_ID, Source_Address_Type, Source_Address, Source_Adv_SID, Broadcast_ID, PA_Sync_State, BIG_Encryption, Bad_Code, |
| // Num_Subgroups and subgroup-related params. |
| SOURCE_ID_BYTE_SIZE |
| + AddressType::BYTE_SIZE |
| + self.source_address.len() |
| + AdvertisingSetId::BYTE_SIZE |
| + self.broadcast_id.encoded_len() |
| + PA_SYNC_BYTE_SIZE |
| + self.big_encryption.encoded_len() |
| + NUM_SUBGROUPS_BYTE_SIZE |
| + self.subgroups.iter().map(Encodable::encoded_len).sum::<usize>() |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| #[repr(u8)] |
| pub enum PaSyncState { |
| NotSynced = 0x00, |
| SyncInfoRequest = 0x01, |
| Synced = 0x02, |
| FailedToSync = 0x03, |
| NoPast = 0x04, |
| } |
| |
| impl TryFrom<u8> for PaSyncState { |
| type Error = PacketError; |
| |
| fn try_from(value: u8) -> Result<Self, Self::Error> { |
| match value { |
| 0x00 => Ok(Self::NotSynced), |
| 0x01 => Ok(Self::SyncInfoRequest), |
| 0x02 => Ok(Self::Synced), |
| 0x03 => Ok(Self::FailedToSync), |
| 0x04 => Ok(Self::NoPast), |
| _ => Err(PacketError::OutOfRange), |
| } |
| } |
| } |
| |
| /// Represents BIG_Encryption and Bad_Code params from BASS spec v.1.0 Table 3.9. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum EncryptionStatus { |
| NotEncrypted, |
| BroadcastCodeRequired, |
| Decrypting, |
| BadCode([u8; 16]), |
| } |
| |
| impl EncryptionStatus { |
| // Should at least include the BIG_Encryption enum value which is 1 byte long. |
| const MIN_PACKET_SIZE: usize = 1; |
| |
| // Returns the u8 value that represents the status of encryption |
| // as described for BIG_Encryption parameter. |
| pub const fn raw_value(self) -> u8 { |
| match self { |
| EncryptionStatus::NotEncrypted => 0x00, |
| EncryptionStatus::BroadcastCodeRequired => 0x01, |
| EncryptionStatus::Decrypting => 0x02, |
| EncryptionStatus::BadCode(_) => 0x03, |
| } |
| } |
| } |
| |
| impl Decodable for EncryptionStatus { |
| type Error = PacketError; |
| |
| fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { |
| if buf.len() < 1 { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| match buf[0] { |
| 0x00 => Ok((Self::NotEncrypted, 1)), |
| 0x01 => Ok((Self::BroadcastCodeRequired, 1)), |
| 0x02 => Ok((Self::Decrypting, 1)), |
| 0x03 => { |
| if buf.len() < 17 { |
| return Err(PacketError::UnexpectedDataLength); |
| } |
| Ok((Self::BadCode(buf[1..17].try_into().unwrap()), 17)) |
| } |
| _ => Err(PacketError::OutOfRange), |
| } |
| } |
| } |
| |
| impl Encodable for EncryptionStatus { |
| type Error = PacketError; |
| |
| fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> { |
| if buf.len() < self.encoded_len() { |
| return Err(PacketError::BufferTooSmall); |
| } |
| |
| buf[0] = self.raw_value(); |
| match self { |
| EncryptionStatus::BadCode(code) => buf[1..17].copy_from_slice(code), |
| _ => {} |
| } |
| Ok(()) |
| } |
| |
| fn encoded_len(&self) -> core::primitive::usize { |
| match self { |
| // For Bad_Code value, we also have to encrypt the incorrect |
| // 16-octet Broadcast_Code. See BASS spec Table 3.9. |
| EncryptionStatus::BadCode(_) => 1 + 16, |
| _ => 1, |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn broadcast_id() { |
| let id = BroadcastId(0x000A0B0C); |
| |
| assert_eq!(id.encoded_len(), 3); |
| let mut buf = vec![0; id.encoded_len()]; |
| let _ = id.encode(&mut buf[..]).expect("should have succeeded"); |
| |
| let bytes = vec![0x0C, 0x0B, 0x0A]; |
| assert_eq!(buf, bytes); |
| |
| let (got, bytes) = BroadcastId::decode(&bytes).expect("should succeed"); |
| assert_eq!(got, id); |
| assert_eq!(bytes, BroadcastId::BYTE_SIZE); |
| let got = BroadcastId::try_from(u32::from_le_bytes([0x0C, 0x0B, 0x0A, 0x00])) |
| .expect("should succeed"); |
| assert_eq!(got, id); |
| } |
| |
| #[test] |
| fn encryption_status_enum() { |
| let not_encrypted = EncryptionStatus::NotEncrypted; |
| let encrypted = EncryptionStatus::BroadcastCodeRequired; |
| let decrypting = EncryptionStatus::Decrypting; |
| let bad_code = EncryptionStatus::BadCode([ |
| 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, |
| 0x02, 0x01, |
| ]); |
| |
| assert_eq!(0x00, not_encrypted.raw_value()); |
| assert_eq!(0x01, encrypted.raw_value()); |
| assert_eq!(0x02, decrypting.raw_value()); |
| assert_eq!(0x03, bad_code.raw_value()); |
| } |
| |
| #[test] |
| fn encryption_status() { |
| // Encoding not encrypted status. |
| let not_encrypted = EncryptionStatus::NotEncrypted; |
| assert_eq!(not_encrypted.encoded_len(), 1); |
| let mut buf = vec![0; not_encrypted.encoded_len()]; |
| let _ = not_encrypted.encode(&mut buf[..]).expect("should not fail"); |
| |
| let bytes = vec![0x00]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding not encrypted. |
| let decoded = EncryptionStatus::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, not_encrypted); |
| assert_eq!(decoded.1, 1); |
| |
| // Encoding bad code status with code. |
| let bad_code = EncryptionStatus::BadCode([ |
| 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, |
| 0x02, 0x01, |
| ]); |
| assert_eq!(bad_code.encoded_len(), 17); |
| let mut buf = vec![0; bad_code.encoded_len()]; |
| let _ = bad_code.encode(&mut buf[..]).expect("should not fail"); |
| |
| let bytes = vec![ |
| 0x03, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, |
| 0x03, 0x02, 0x01, |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Deocoding bad code statsu with code. |
| let decoded = EncryptionStatus::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, bad_code); |
| assert_eq!(decoded.1, 17); |
| } |
| |
| #[test] |
| fn invalid_encryption_status() { |
| // Cannot encode into empty buffer. |
| let not_encrypted = EncryptionStatus::NotEncrypted; |
| let mut buf = vec![]; |
| let _ = not_encrypted.encode(&mut buf[..]).expect_err("should fail"); |
| |
| // Not enough buffer space for encoding. |
| let bad_code = EncryptionStatus::BadCode([ |
| 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, |
| 0x02, 0x01, |
| ]); |
| let mut buf = vec![0; 1]; |
| let _ = bad_code.encode(&mut buf[..]).expect_err("should fail"); |
| |
| // Cannot decode empty buffer. |
| let buf = vec![]; |
| let _ = EncryptionStatus::decode(&buf).expect_err("should fail"); |
| |
| // Bad code status with no code. |
| let buf = vec![0x03]; |
| let _ = EncryptionStatus::decode(&buf).expect_err("should fail"); |
| } |
| |
| #[test] |
| fn remote_scan_stopped() { |
| // Encoding remote scan stopped. |
| let stopped = RemoteScanStoppedOperation; |
| assert_eq!(stopped.encoded_len(), 1); |
| let mut buf = vec![0u8; stopped.encoded_len()]; |
| stopped.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![0x00]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding remote scan stopped. |
| let decoded = RemoteScanStoppedOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, stopped); |
| assert_eq!(decoded.1, 1); |
| } |
| |
| #[test] |
| fn remote_scan_started() { |
| // Encoding remote scan started. |
| let started = RemoteScanStartedOperation; |
| assert_eq!(started.encoded_len(), 1); |
| let mut buf = vec![0u8; started.encoded_len()]; |
| started.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![0x01]; |
| assert_eq!(buf, vec![0x01]); |
| |
| // Decoding remote scan started. |
| let decoded = RemoteScanStartedOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, started); |
| assert_eq!(decoded.1, 1); |
| } |
| |
| #[test] |
| fn add_source_without_subgroups() { |
| // Encoding operation with no subgroups. |
| let op = AddSourceOperation::new( |
| AddressType::Public, |
| [0x04, 0x10, 0x00, 0x00, 0x00, 0x00], |
| 1, |
| 0x11, |
| PaSync::DoNotSync, |
| PaInterval::UNKNOWN_VALUE, |
| vec![], |
| ); |
| assert_eq!(op.encoded_len(), 16); |
| let mut buf = vec![0u8; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![ |
| 0x02, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0xFF, |
| 0xFF, 0x00, |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding operation with no subgroups. |
| let decoded = AddSourceOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 16); |
| } |
| |
| #[test] |
| fn add_source_with_subgroups() { |
| // Encoding operation with subgroups. |
| let subgroups = vec![BigSubgroup::new(None).with_metadata(vec![ |
| Metadatum::PreferredAudioContexts(ContextType::MEDIA | ContextType::GAME), // encoded_len = 4 |
| Metadatum::ProgramInfo("test".to_string()), // encoded_len = 6 |
| ])]; |
| let op = AddSourceOperation::new( |
| AddressType::Random, |
| [0x04, 0x10, 0x00, 0x00, 0x00, 0x00], |
| 1, |
| 0x11, |
| PaSync::SyncPastAvailable, |
| PaInterval::UNKNOWN_VALUE, |
| subgroups, |
| ); |
| assert_eq!(op.encoded_len(), 31); // 16 for minimum params and params 15 for the subgroup. |
| let mut buf = vec![0u8; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![ |
| 0x02, 0x01, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x01, 0xFF, |
| 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, // BIS_Sync, Metdata_Length |
| 0x03, 0x01, 0x0C, 0x00, // Preferred_Audio_Contexts metdataum |
| 0x05, 0x03, 0x74, 0x65, 0x73, 0x074, // Program_Info metadatum |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding operation with subgroups. |
| let decoded = AddSourceOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 31); |
| } |
| |
| #[test] |
| fn modify_source_without_subgroups() { |
| // Encoding operation with no subgroups. |
| let op = ModifySourceOperation::new(0x0A, PaSync::SyncPastAvailable, 0x1004, vec![]); |
| assert_eq!(op.encoded_len(), 6); |
| let mut buf = vec![0u8; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![0x03, 0x0A, 0x01, 0x04, 0x10, 0x00]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding operation with no subgroups. |
| let decoded = ModifySourceOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 6); |
| } |
| |
| #[test] |
| fn modify_source_with_subgroups() { |
| // Encoding operation with subgroups. |
| let subgroups = vec![ |
| BigSubgroup::new(None).with_metadata(vec![Metadatum::ParentalRating(Rating::AllAge)]), // encoded_len = 8 |
| BigSubgroup::new(Some(0x000000FE)) |
| .with_metadata(vec![Metadatum::BroadcastAudioImmediateRenderingFlag]), // encoded_len = 7 |
| ]; |
| let op = ModifySourceOperation::new( |
| 0x0B, |
| PaSync::DoNotSync, |
| PaInterval::UNKNOWN_VALUE, |
| subgroups, |
| ); |
| assert_eq!(op.encoded_len(), 21); // 6 for minimum params and params 15 for two subgroups. |
| let mut buf = vec![0u8; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("shoud succeed"); |
| |
| let bytes = vec![ |
| 0x03, 0x0B, 0x00, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x02, 0x06, |
| 0x01, // First subgroup. |
| 0xFE, 0x00, 0x00, 0x00, 0x02, 0x01, 0x09, // Second subgroup. |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding operation with subgroups. |
| let decoded = ModifySourceOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 21); |
| } |
| |
| #[test] |
| fn set_broadcast_code() { |
| // Encoding. |
| let op = SetBroadcastCodeOperation::new( |
| 0x0A, |
| [ |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, |
| 0x15, 0x16, |
| ], |
| ); |
| assert_eq!(op.encoded_len(), 18); |
| let mut buf = vec![0; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("should succeed"); |
| |
| let bytes = vec![ |
| 0x04, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, |
| 0x13, 0x14, 0x15, 0x16, |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding. |
| let decoded = SetBroadcastCodeOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 18); |
| } |
| |
| #[test] |
| fn remove_source() { |
| // Encoding. |
| let op = RemoveSourceOperation::new(0x0A); |
| assert_eq!(op.encoded_len(), 2); |
| let mut buf = vec![0; op.encoded_len()]; |
| op.encode(&mut buf[..]).expect("should succeed"); |
| |
| let bytes = vec![0x05, 0x0A]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding. |
| let decoded = RemoveSourceOperation::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, op); |
| assert_eq!(decoded.1, 2); |
| } |
| |
| #[test] |
| fn broadcast_receive_state_without_subgroups() { |
| // Encoding. |
| let state = BroadcastReceiveState::NonEmpty(ReceiveState { |
| source_id: 0x01, |
| source_address_type: AddressType::Public, |
| source_address: [0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A], |
| source_adv_sid: AdvertisingSetId(0x01), |
| broadcast_id: BroadcastId(0x00010203), |
| pa_sync_state: PaSyncState::Synced, |
| big_encryption: EncryptionStatus::BadCode([ |
| 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x7, 0x06, 0x05, 0x04, 0x03, |
| 0x02, 0x01, |
| ]), |
| subgroups: vec![], |
| }); |
| assert_eq!(state.encoded_len(), 31); |
| let mut buf = vec![0; state.encoded_len()]; |
| state.encode(&mut buf[..]).expect("should succeed"); |
| |
| let bytes = vec![ |
| 0x01, 0x00, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x01, 0x03, 0x02, 0x01, 0x02, 0x03, |
| 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, |
| 0x02, 0x01, // Bad_Code with the code. |
| 0x00, |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding. |
| let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, state); |
| assert_eq!(decoded.1, 31); |
| } |
| |
| #[test] |
| fn broadcast_receive_state_with_subgroups() { |
| // Encoding |
| let state = BroadcastReceiveState::NonEmpty(ReceiveState { |
| source_id: 0x01, |
| source_address_type: AddressType::Random, |
| source_address: [0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A], |
| source_adv_sid: AdvertisingSetId(0x01), |
| broadcast_id: BroadcastId(0x00010203), |
| pa_sync_state: PaSyncState::NotSynced, |
| big_encryption: EncryptionStatus::NotEncrypted, |
| subgroups: vec![ |
| BigSubgroup::new(None) |
| .with_metadata(vec![Metadatum::ParentalRating(Rating::AllAge)]), // encoded_len = 8 |
| ], |
| }); |
| assert_eq!(state.encoded_len(), 23); |
| let mut buf = vec![0; state.encoded_len()]; |
| state.encode(&mut buf[..]).expect("should succeed"); |
| |
| let bytes = vec![ |
| 0x01, 0x01, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x01, 0x03, 0x02, 0x01, 0x00, 0x00, |
| 0x01, // 1 Subgroup. |
| 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x02, 0x06, 0x01, // Subgroup. |
| ]; |
| assert_eq!(buf, bytes); |
| |
| // Decoding. |
| let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed"); |
| assert_eq!(decoded.0, state); |
| assert_eq!(decoded.1, 23); |
| } |
| } |