rust/bt-ascs: Initial implementaiton Audio Stream Control Service crate, including initial decoding of control point operaitons and initially empty Events from the service. Test: added unit tests, cargo test Change-Id: I1945b5e306783a6b514d6da8ba5dda66fcf7b8f0 Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/1722 Commit-Queue: Marie Janssen <jamuraa@google.com> Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 650f02c..ddc7870 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml
@@ -24,6 +24,7 @@ assert_matches = "1.5.0" bitfield = "0.14.0" futures = "=0.3.31" +futures-test = "=0.3.31" lazy_static = "1.5" log = { version = "0.4.27", features = [ "kv", "std" ] } num = { version = "0.4.3", features = ["rand"] }
diff --git a/rust/bt-ascs/Cargo.toml b/rust/bt-ascs/Cargo.toml new file mode 100644 index 0000000..723aa6f --- /dev/null +++ b/rust/bt-ascs/Cargo.toml
@@ -0,0 +1,19 @@ +[package] +name = "bt-ascs" +version = "0.0.1" +license.workspace = true +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bt-common.workspace = true +bt-gatt.workspace = true +thiserror.workspace = true +futures.workspace = true +pin-project.workspace = true +log.workspace = true + +[dev-dependencies] +futures-test.workspace = true +bt-gatt = { workspace = true, features = ["test-utils"] }
diff --git a/rust/bt-ascs/LICENSE b/rust/bt-ascs/LICENSE new file mode 120000 index 0000000..30cff74 --- /dev/null +++ b/rust/bt-ascs/LICENSE
@@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file
diff --git a/rust/bt-ascs/src/lib.rs b/rust/bt-ascs/src/lib.rs new file mode 100644 index 0000000..f3f7380 --- /dev/null +++ b/rust/bt-ascs/src/lib.rs
@@ -0,0 +1,10 @@ +// Copyright 2024 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub mod server; +pub mod types; +pub use types::Error; + +#[cfg(test)] +pub mod tests;
diff --git a/rust/bt-ascs/src/server.rs b/rust/bt-ascs/src/server.rs new file mode 100644 index 0000000..12b8274 --- /dev/null +++ b/rust/bt-ascs/src/server.rs
@@ -0,0 +1,378 @@ +// Copyright 2024 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use bt_gatt::Characteristic; +use bt_gatt::server::{ReadResponder, WriteResponder}; +use bt_gatt::types::{ + AttributePermissions, CharacteristicProperty, GattError, Handle, SecurityLevels, +}; +use futures::task::{Poll, Waker}; +use futures::{Future, Stream, stream::FusedStream}; +use pin_project::pin_project; +use std::collections::HashMap; + +use bt_common::{PeerId, Uuid}; +use bt_gatt::server::{LocalService, Server, ServiceDefinition, ServiceId}; + +use crate::types::*; + +#[pin_project(project = LocalServiceProj)] +enum LocalServiceState<T: bt_gatt::ServerTypes> { + NotPublished { + waker: Option<Waker>, + }, + Preparing { + #[pin] + fut: T::LocalServiceFut, + }, + Published { + service: T::LocalService, + #[pin] + events: T::ServiceEventStream, + }, + Terminated, +} + +impl<T: bt_gatt::ServerTypes> Default for LocalServiceState<T> { + fn default() -> Self { + Self::NotPublished { waker: None } + } +} + +impl<T: bt_gatt::ServerTypes> LocalServiceState<T> { + fn service(&self) -> Option<&T::LocalService> { + let Self::Published { service, .. } = self else { + return None; + }; + Some(service) + } +} + +impl<T: bt_gatt::ServerTypes> Stream for LocalServiceState<T> { + type Item = Result<bt_gatt::server::ServiceEvent<T>, Error>; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll<Option<Self::Item>> { + // SAFETY: + // - Wakers are Unpin + // - We re-pin the structurally pinned futures in Preparing and Published + // (service is untouched) + // - Terminated is empty + loop { + match self.as_mut().project() { + LocalServiceProj::Terminated => return Poll::Ready(None), + LocalServiceProj::NotPublished { .. } => { + self.as_mut() + .set(LocalServiceState::NotPublished { waker: Some(cx.waker().clone()) }); + return Poll::Pending; + } + LocalServiceProj::Preparing { fut } => { + let service_result = futures::ready!(fut.poll(cx)); + let Ok(service) = service_result else { + self.as_mut().set(LocalServiceState::Terminated); + return Poll::Ready(Some(Err(Error::PublishError( + service_result.err().unwrap(), + )))); + }; + let events = service.publish(); + self.as_mut().set(LocalServiceState::Published { service, events }); + continue; + } + LocalServiceProj::Published { service: _, events } => { + let item = futures::ready!(events.poll_next(cx)); + let Some(gatt_result) = item else { + self.as_mut().set(LocalServiceState::Terminated); + return Poll::Ready(Some(Err(Error::PublishError( + "GATT server terminated".into(), + )))); + }; + let Ok(event) = gatt_result else { + self.as_mut().set(LocalServiceState::Terminated); + return Poll::Ready(Some(Err(Error::PublishError( + gatt_result.err().unwrap(), + )))); + }; + return Poll::Ready(Some(Ok(event))); + } + } + } + } +} + +impl<T: bt_gatt::ServerTypes> FusedStream for LocalServiceState<T> { + fn is_terminated(&self) -> bool { + match self { + Self::Terminated => true, + _ => false, + } + } +} + +impl<T: bt_gatt::ServerTypes> LocalServiceState<T> { + fn is_not_published(&self) -> bool { + matches!(self, LocalServiceState::NotPublished { .. }) + } +} + +#[pin_project] +pub struct AudioStreamControlServiceServer<T: bt_gatt::ServerTypes> { + service_def: ServiceDefinition, + #[pin] + local_service: LocalServiceState<T>, + default_client_endpoints: ClientEndpoints, + client_endpoints: HashMap<PeerId, ClientEndpoints>, +} + +const CONTROL_POINT_HANDLE: Handle = Handle(1); +const BASE_ENDPOINT_HANDLE: Handle = Handle(2); + +pub const ASCS_UUID: Uuid = Uuid::from_u16(0x184E); + +// As only one ASCS service is allowed on a host, define an arbitrary ServiceId +// that every AudioStreamControlServiceServer will attempt to use so publishing +// multiple will fail. +pub(crate) const ASCS_SERVICE_ID: ServiceId = ServiceId::new(1123901); + +impl<T: bt_gatt::ServerTypes> AudioStreamControlServiceServer<T> { + pub fn new(source_count: u8, sink_count: u8) -> Self { + let default_client_endpoints = ClientEndpoints::new(source_count, sink_count); + let mut chars: Vec<Characteristic> = (&default_client_endpoints).into(); + chars.push(Self::build_control_point()); + let mut service_def = ServiceDefinition::new( + ASCS_SERVICE_ID, + ASCS_UUID, + bt_gatt::types::ServiceKind::Primary, + ); + for c in chars { + service_def.add_characteristic(c).unwrap(); + } + Self { + service_def, + local_service: Default::default(), + default_client_endpoints, + client_endpoints: Default::default(), + } + } + + fn build_control_point() -> Characteristic { + let properties = CharacteristicProperty::Write + | CharacteristicProperty::WriteWithoutResponse + | CharacteristicProperty::Notify; + let permissions = + AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required()); + Characteristic { + handle: CONTROL_POINT_HANDLE, + uuid: Uuid::from_u16(0x2BC6), + properties, + permissions, + descriptors: Vec::new(), + } + } + + pub fn publish(&mut self, server: &T::Server) -> Result<(), Error> { + if !self.local_service.is_not_published() { + return Err(Error::AlreadyPublished); + } + let LocalServiceState::NotPublished { waker } = std::mem::replace( + &mut self.local_service, + LocalServiceState::Preparing { fut: server.prepare(self.service_def.clone()) }, + ) else { + unreachable!(); + }; + waker.map(Waker::wake); + Ok(()) + } + + pub fn release(&mut self, _id: AseId) -> Result<(), Error> { + unimplemented!() + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum AudioDirection { + Sink, + Source, +} + +impl From<&AudioDirection> for bt_common::Uuid { + fn from(value: &AudioDirection) -> Self { + match value { + AudioDirection::Sink => Uuid::from_u16(0x2BC4), + AudioDirection::Source => Uuid::from_u16(0x2BC5), + } + } +} + +#[derive(Debug, Clone)] +struct AudioStreamEndpoint { + handle: Handle, + direction: AudioDirection, + ase_id: AseId, + state: AseState, + // TODO(b/433287917): Add Additional Parameters for other states. + // Currently only works for Idle and Releasing states. +} + +impl AudioStreamEndpoint { + fn into_char_value(&self) -> Vec<u8> { + let mut value = Vec::with_capacity(2); + value.push(self.ase_id.into()); + value.push(self.state.into()); + // TODO: add the additional_ase_parameters for the other states + value + } +} + +impl From<&AudioStreamEndpoint> for Characteristic { + fn from(value: &AudioStreamEndpoint) -> Self { + let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify; + let permissions = + AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required()); + Characteristic { + handle: value.handle, + uuid: (&value.direction).into(), + properties, + permissions, + descriptors: Vec::new(), + } + } +} + +struct ClientEndpoints { + endpoints: HashMap<AseId, AudioStreamEndpoint>, + handles: HashMap<Handle, AseId>, +} + +impl ClientEndpoints { + fn new(source_count: u8, sink_count: u8) -> Self { + let dir_iter = std::iter::repeat(AudioDirection::Source) + .take(source_count as usize) + .chain(std::iter::repeat(AudioDirection::Sink).take(sink_count as usize)); + // AseIds shall not have an id of 0 + let (endpoints, handles) = (1..) + .zip(dir_iter) + .map(|(raw_ase_id, direction)| { + let handle = Handle(BASE_ENDPOINT_HANDLE.0 + raw_ase_id as u64); + let ase_id = AseId(raw_ase_id); + ( + ( + ase_id, + AudioStreamEndpoint { handle, ase_id, direction, state: AseState::Idle }, + ), + (handle, ase_id), + ) + }) + .unzip(); + Self { endpoints, handles } + } + + fn clone_for_peer(&self, _peer_id: PeerId) -> Self { + // TODO: Randomize the ASE_IDs. Handles need to stay the same. + Self { endpoints: self.endpoints.clone(), handles: self.handles.clone() } + } +} + +impl From<&ClientEndpoints> for Vec<Characteristic> { + fn from(value: &ClientEndpoints) -> Self { + value.endpoints.values().map(Into::into).collect() + } +} + +pub enum ServiceEvent {} + +impl<T: bt_gatt::ServerTypes> Stream for AudioStreamControlServiceServer<T> { + type Item = Result<ServiceEvent, Error>; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll<Option<Self::Item>> { + let mut this = self.project(); + loop { + let event = match futures::ready!(this.local_service.as_mut().poll_next(cx)) { + None => return Poll::Ready(None), + Some(Err(e)) => return Poll::Ready(Some(Err(e))), + Some(Ok(event)) => event, + }; + use bt_gatt::server::ServiceEvent::*; + let peer_id = event.peer_id(); + let peer_entry = this + .client_endpoints + .entry(peer_id) + .or_insert_with(|| this.default_client_endpoints.clone_for_peer(peer_id)); + match event { + Read { handle, offset, responder, .. } => { + let offset = offset as usize; + if handle == CONTROL_POINT_HANDLE { + responder.error(GattError::ReadNotPermitted); + continue; + } + let Some(ase_id) = peer_entry.handles.get(&handle) else { + responder.error(GattError::InvalidHandle); + continue; + }; + let Some(endpoint) = peer_entry.endpoints.get(ase_id) else { + responder.error(GattError::UnlikelyError); + continue; + }; + let value = endpoint.into_char_value(); + if offset > value.len() { + responder.error(GattError::InvalidOffset); + continue; + } + responder.respond(&value[offset..]); + continue; + } + Write { peer_id, handle, offset, value, responder } => { + if handle != CONTROL_POINT_HANDLE { + responder.error(GattError::WriteNotPermitted); + continue; + } + if offset != 0 { + // Offset write isn't allowed by the service? + // TODO: determine if partial writes should be allowed + responder.error(GattError::InvalidOffset); + continue; + } + responder.acknowledge(); + let _op = match AseControlOperation::try_from(value.to_owned()) { + Ok(op) => op, + Err(e) => { + let service_ref = this.local_service.as_ref(); + + let value = e.notify_value(); + service_ref.service().unwrap().notify( + &CONTROL_POINT_HANDLE, + &value[..], + &[peer_id], + ); + continue; + } + }; + // TODO: Do the operation here, possibly notifying things + continue; + } + ClientConfiguration { peer_id, handle, notification_type } => { + log::info!( + "ASCS Got ClientConfig for {peer_id:?}: {handle:?} {notification_type:?}" + ); + } + PeerInfo { peer_id, mtu, connected, .. } => { + log::info!( + "ASCS got PeerInfo {peer_id:?}: mtu {mtu:?}, connected: {connected:?}" + ); + } + _ => continue, + } + } + } +} + +impl<T: bt_gatt::ServerTypes> FusedStream for AudioStreamControlServiceServer<T> { + fn is_terminated(&self) -> bool { + self.local_service.is_terminated() + } +}
diff --git a/rust/bt-ascs/src/tests.rs b/rust/bt-ascs/src/tests.rs new file mode 100644 index 0000000..3e88f87 --- /dev/null +++ b/rust/bt-ascs/src/tests.rs
@@ -0,0 +1,143 @@ +// Copyright 2024 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use bt_common::{PeerId, Uuid}; +use bt_gatt::test_utils::{FakeServer, FakeServerEvent, FakeTypes}; +use bt_gatt::types::Handle; + +use futures::channel::mpsc::UnboundedReceiver; +use futures::{Stream, StreamExt}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::server::AudioStreamControlServiceServer; +use crate::server::ServiceEvent; +use crate::server::{ASCS_SERVICE_ID, ASCS_UUID}; +use crate::*; + +#[track_caller] +fn expect_service_event(events: &mut UnboundedReceiver<FakeServerEvent>) -> FakeServerEvent { + match events.poll_next_unpin(&mut futures_test::task::noop_context()) { + Poll::Ready(Some(event)) => event, + x => panic!("Expected fake server event, got {x:?}"), + } +} + +#[test] +fn publishes() { + let mut ascs_server = std::pin::pin!(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1)); + + let (count_waker, woken_count) = futures_test::task::new_count_waker(); + + // Polling the server before it's published should result in Poll::Pending + let poll_result = ascs_server.as_mut().poll_next(&mut Context::from_waker(&count_waker)); + + assert!(poll_result.is_pending()); + assert_eq!(woken_count.get(), 0); + + let (fake_server, mut events) = FakeServer::new(); + + let result = ascs_server.publish(&fake_server); + assert!(result.is_ok()); + + let already_published = ascs_server.publish(&fake_server); + + assert!(already_published.is_err()); + + assert_eq!(woken_count.get(), 1); + + let poll_result = ascs_server.poll_next(&mut Context::from_waker(&count_waker)); + + match expect_service_event(&mut events) { + FakeServerEvent::Published { id: _, definition } => { + assert_eq!(definition.uuid(), ASCS_UUID); + assert_eq!( + definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC4)).count(), + 1 + ); + assert_eq!( + definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC5)).count(), + 1 + ); + } + x => panic!("Expected published event, got {x:?}"), + }; + + // Should still be pending, even though we had some work to do. + assert!(poll_result.is_pending()); +} + +fn published_server() -> ( + Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, + FakeServer, + UnboundedReceiver<FakeServerEvent>, +) { + let mut ascs_server = Box::pin(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1)); + let (fake_server, events) = FakeServer::new(); + + let result = ascs_server.publish(&fake_server); + assert!(result.is_ok()); + + assert!(ascs_server.poll_next_unpin(&mut futures_test::task::noop_context()).is_pending()); + + (ascs_server, fake_server, events) +} + +fn poll_server( + server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, +) -> Poll<Option<core::result::Result<ServiceEvent, Error>>> { + server.poll_next_unpin(&mut futures_test::task::noop_context()) +} + +// Ignored because we currently do nothing with operations. +#[ignore] +#[test] +fn peers_are_separated() { + let (mut ascs_server, fake_server, mut server_events) = published_server(); + + // Read the sink uuid + fake_server.incoming_read(PeerId(1), ASCS_SERVICE_ID, Handle(2), 0); + + // Poll the ascs server, should not result in an ASCS event + assert!(poll_server(&mut ascs_server).is_pending()); + + // Should have the response + let peer_one_value; + match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) { + Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => { + assert_eq!(service_id, ASCS_SERVICE_ID); + peer_one_value = value.unwrap(); + } + x => panic!("Expected the read to be responded to got {x:?}"), + }; + + let ase_id = peer_one_value[0]; + + // Codec Configure the first peer ase_id + fake_server.incoming_write( + PeerId(1), + ASCS_SERVICE_ID, + Handle(1), + 0, + vec![0x01, ase_id, 0x01, 0x01, 0x06, 0x00], + ); + + // Still shouldn't have any event + assert!(poll_server(&mut ascs_server).is_pending()); + + // Read the sink id from another peer + fake_server.incoming_read(PeerId(2), ASCS_SERVICE_ID, Handle(2), 0); + + let peer_two_value; + match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) { + Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => { + assert_eq!(service_id, ASCS_SERVICE_ID); + peer_two_value = value.unwrap(); + } + x => panic!("Expected the read to be responded to got {x:?}"), + }; + + let _ase_id = peer_two_value[0]; + assert!(peer_one_value[1] != peer_two_value[1]); +}
diff --git a/rust/bt-ascs/src/types.rs b/rust/bt-ascs/src/types.rs new file mode 100644 index 0000000..2fb54ad --- /dev/null +++ b/rust/bt-ascs/src/types.rs
@@ -0,0 +1,991 @@ +// Copyright 2024 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use bt_common::packet_encoding::Decodable; +use bt_common::{codable_as_bitmask, decodable_enum}; +use thiserror::Error; + +use bt_common::core::CodecId; +use bt_common::generic_audio::metadata_ltv::Metadata; + +/// Error type +#[derive(Debug, Error)] +pub enum Error { + #[error("Reserved for Future Use: {0}")] + ReservedFutureUse(String), + #[error("Server Only Operation")] + ServerOnlyOperation, + #[error("Service is already published")] + AlreadyPublished, + #[error("Issue publishing service: {0}")] + PublishError(bt_gatt::types::Error), + #[error("Unsupported configuration: {0}")] + Unsupported(String), +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub enum ResponseCode { + Success { ase_id: AseId }, + UnsupportedOpcode, + InvalidLength, + InvalidAseId { value: u8 }, + InvalidAseStateMachineTransition { ase_id: AseId }, + InvalidAseDirection { ase_id: AseId }, + UnsupportedAudioCapablities { ase_id: AseId }, + ConfigurationParameterValue { ase_id: AseId, issue: ResponseIssue, reason: ResponseReason }, + Metadata { ase_id: AseId, issue: ResponseIssue, type_value: u8 }, + InsufficientResources { ase_id: AseId }, + UnspecifiedError { ase_id: AseId }, +} + +impl ResponseCode { + fn to_code(&self) -> u8 { + match self { + ResponseCode::Success { .. } => 0x00, + ResponseCode::UnsupportedOpcode => 0x01, + ResponseCode::InvalidLength => 0x02, + ResponseCode::InvalidAseId { .. } => 0x03, + ResponseCode::InvalidAseStateMachineTransition { .. } => 0x04, + ResponseCode::InvalidAseDirection { .. } => 0x05, + ResponseCode::UnsupportedAudioCapablities { .. } => 0x06, + ResponseCode::ConfigurationParameterValue { + issue: ResponseIssue::Unsupported, .. + } => 0x07, + ResponseCode::ConfigurationParameterValue { + issue: ResponseIssue::Rejected, .. + } => 0x08, + ResponseCode::ConfigurationParameterValue { issue: ResponseIssue::Invalid, .. } => 0x09, + ResponseCode::Metadata { issue: ResponseIssue::Unsupported, .. } => 0x0A, + ResponseCode::Metadata { issue: ResponseIssue::Rejected, .. } => 0x0B, + ResponseCode::Metadata { issue: ResponseIssue::Invalid, .. } => 0x0C, + ResponseCode::InsufficientResources { .. } => 0x0D, + ResponseCode::UnspecifiedError { .. } => 0x0E, + } + } + + fn reason_byte(&self) -> u8 { + match self { + ResponseCode::ConfigurationParameterValue { reason, .. } => (*reason).into(), + ResponseCode::Metadata { type_value, .. } => *type_value, + _ => 0x00, + } + } + + fn ase_id_value(&self) -> u8 { + match self { + ResponseCode::UnsupportedOpcode | ResponseCode::InvalidLength => 0x00, + ResponseCode::InvalidAseId { value } => *value, + ResponseCode::Success { ase_id } + | ResponseCode::InvalidAseStateMachineTransition { ase_id } + | ResponseCode::InvalidAseDirection { ase_id } + | ResponseCode::UnsupportedAudioCapablities { ase_id } + | ResponseCode::ConfigurationParameterValue { ase_id, .. } + | ResponseCode::Metadata { ase_id, .. } + | ResponseCode::InsufficientResources { ase_id } + | ResponseCode::UnspecifiedError { ase_id } => (*ase_id).into(), + } + } + + pub(crate) fn notify_value(&self) -> Vec<u8> { + [self.ase_id_value(), self.to_code(), self.reason_byte()].into() + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ResponseIssue { + Unsupported, + Rejected, + Invalid, +} + +decodable_enum! { +#[non_exhaustive] +pub enum ResponseReason<u8, bt_common::packet_encoding::Error, OutOfRange> { + CodecId = 0x01, + CodecSpecificConfiguration = 0x02, + SduInterval = 0x03, + Framing = 0x04, + Phy = 0x05, + MaximumSduSize = 0x06, + RetransmissionNumber = 0x07, + MaxTransportLatency = 0x08, + PresentationDelay = 0x09, + InvalidAseCisMapping = 0x0A, +} +} + +decodable_enum! { + +#[derive(Default)] +pub enum AseState<u8, bt_common::packet_encoding::Error, OutOfRange> { + #[default] + Idle = 0x00, + CodecConfigured = 0x01, + QosConfigured = 0x02, + Enabling = 0x03, + Streaming = 0x04, + Disabling = 0x05, + Releasing = 0x06, +} +} + +/// Audio Stream Endpoint Identifier +/// Exposed by the server in ASE characteristics +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AseId(pub u8); + +impl AseId { + const BYTE_SIZE: usize = 1; +} + +impl TryFrom<u8> for AseId { + type Error = ResponseCode; + + fn try_from(value: u8) -> Result<Self, Self::Error> { + if value == 0 { + return Err(ResponseCode::InvalidAseId { value }); + } + Ok(Self(value)) + } +} + +impl From<AseId> for u8 { + fn from(value: AseId) -> Self { + value.0 + } +} + +impl Decodable for AseId { + type Error = ResponseCode; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < 1 { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + (buf[0].try_into(), 1) + } +} + +decodable_enum! { + pub enum AseControlPointOpcode<u8, ResponseCode, UnsupportedOpcode> { + ConfigCodec = 0x01, + ConfigQos = 0x02, + Enable = 0x03, + ReceiverStartReady = 0x04, + Disable = 0x05, + ReceiverStopReady = 0x06, + UpdateMetadata = 0x07, + Release = 0x08, + } +} + +/// ASE Control Operations. These can be initiated by a server or client. +/// Defined in Table 4.6 of ASCS v1.0 +/// Marked non-exaustive as the remaining operations are RFU and new operations +/// could arrive but should be rejected if they are not recognized. +/// Some variants already contain responses as decoding errors are detected, +/// i.e. invalid parameters or metadata, which will be delivered after the +/// operation is complete with the results from the rest of the operation. +#[non_exhaustive] +#[derive(Debug, PartialEq, Clone)] +pub enum AseControlOperation { + ConfigCodec { codec_configurations: Vec<CodecConfiguration>, responses: Vec<ResponseCode> }, + ConfigQos { qos_configurations: Vec<QosConfiguration>, responses: Vec<ResponseCode> }, + Enable { ases_with_metadata: Vec<AseIdWithMetadata>, responses: Vec<ResponseCode> }, + ReceiverStartReady { ases: Vec<AseId> }, + // The only possible error here is InvalidLength + Disable { ases: Vec<AseId> }, + // The only possible error here is InvalidLength + ReceiverStopReady { ases: Vec<AseId> }, + UpdateMetadata { ases_with_metadata: Vec<AseIdWithMetadata>, responses: Vec<ResponseCode> }, + // The only possible error here is InvalidLength + Release { ases: Vec<AseId> }, + // This is only initiated by the server + Released, +} + +impl AseControlOperation { + const MIN_BYTE_SIZE: usize = 3; + + fn contains_invalid_length(&self) -> bool { + match self { + Self::ConfigCodec { responses, .. } + | Self::ConfigQos { responses, .. } + | Self::Enable { responses, .. } + | Self::UpdateMetadata { responses, .. } => { + responses.contains(&ResponseCode::InvalidLength) + } + _ => false, + } + } +} + +impl TryFrom<AseControlOperation> for u8 { + type Error = Error; + + fn try_from(value: AseControlOperation) -> Result<Self, Self::Error> { + match value { + AseControlOperation::ConfigCodec { .. } => Ok(0x01), + AseControlOperation::ConfigQos { .. } => Ok(0x02), + AseControlOperation::Enable { .. } => Ok(0x03), + AseControlOperation::ReceiverStartReady { .. } => Ok(0x04), + AseControlOperation::Disable { .. } => Ok(0x05), + AseControlOperation::ReceiverStopReady { .. } => Ok(0x06), + AseControlOperation::UpdateMetadata { .. } => Ok(0x07), + AseControlOperation::Release { .. } => Ok(0x08), + AseControlOperation::Released => Err(Error::ServerOnlyOperation), + } + } +} + +fn partition_results<T, E>(collection: Vec<Result<T, E>>) -> (Vec<T>, Vec<E>) { + let mut oks = Vec::with_capacity(collection.len()); + let mut errs = Vec::with_capacity(collection.len()); + for item in collection { + match item { + Ok(x) => oks.push(x), + Err(e) => errs.push(e), + } + } + (oks, errs) +} + +impl TryFrom<Vec<u8>> for AseControlOperation { + type Error = ResponseCode; + + fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { + if value.len() < Self::MIN_BYTE_SIZE { + return Err(ResponseCode::InvalidLength); + } + let operation: AseControlPointOpcode = value[0].try_into()?; + let number_of_ases = value[1] as usize; + if number_of_ases < 1 { + return Err(ResponseCode::InvalidLength); + } + let (op, consumed) = match operation { + AseControlPointOpcode::ConfigCodec => { + let (results, consumed) = + CodecConfiguration::decode_multiple(&value[2..], Some(number_of_ases)); + let (codec_configurations, responses) = partition_results(results); + (Self::ConfigCodec { codec_configurations, responses }, consumed) + } + AseControlPointOpcode::ConfigQos => { + let (results, consumed) = + QosConfiguration::decode_multiple(&value[2..], Some(number_of_ases)); + let (qos_configurations, responses) = partition_results(results); + (Self::ConfigQos { qos_configurations, responses }, consumed) + } + AseControlPointOpcode::Enable => { + let (results, consumed) = + AseIdWithMetadata::decode_multiple(&value[2..], Some(number_of_ases)); + let (ases_with_metadata, responses) = partition_results(results); + (Self::Enable { ases_with_metadata, responses }, consumed) + } + AseControlPointOpcode::ReceiverStartReady => { + // Only InvalidLength is possible + let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases)); + let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?; + (Self::ReceiverStartReady { ases }, consumed) + } + AseControlPointOpcode::Disable => { + // Only InvalidLength is possible + let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases)); + let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?; + (Self::Disable { ases }, consumed) + } + AseControlPointOpcode::ReceiverStopReady => { + // Only InvalidLength is possible + let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases)); + let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?; + (Self::ReceiverStopReady { ases }, consumed) + } + AseControlPointOpcode::UpdateMetadata => { + let (results, consumed) = + AseIdWithMetadata::decode_multiple(&value[2..], Some(number_of_ases)); + let (ases_with_metadata, responses) = partition_results(results); + (Self::UpdateMetadata { ases_with_metadata, responses }, consumed) + } + AseControlPointOpcode::Release => { + // Only InvalidLength is possible + let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases)); + let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?; + (Self::Release { ases }, consumed) + } + }; + // A client-initiated ASE Control operation shall also be defined as an invalid + // length operation if the total length of all parameters written by the + // client is not equal to the total length of all fixed parameters plus + // the length of any variable length parameters for that operation as + // defined in Section 5.1 through Section 5.8. + if (consumed + 2) != value.len() { + return Err(ResponseCode::InvalidLength); + } + if op.contains_invalid_length() { + return Err(ResponseCode::InvalidLength); + } + Ok(op) + } +} + +decodable_enum! { +pub enum TargetLatency<u8, bt_common::packet_encoding::Error, OutOfRange> { + TargetLowLatency = 0x01, + TargetBalanced = 0x02, + TargetHighReliability = 0x03, +} +} + +impl TargetLatency { + const BYTE_SIZE: usize = 1; +} + +decodable_enum! { +pub enum TargetPhy<u8, bt_common::packet_encoding::Error, OutOfRange> { + Le1MPhy = 0x01, + Le2MPhy = 0x02, + LeCodedPhy = 0x03, +} +} + +impl TargetPhy { + const BYTE_SIZE: usize = 1; +} + +decodable_enum! { + pub enum Phy<u8, bt_common::packet_encoding::Error, OutOfRange> { + Le1MPhy = 0b0001, + Le2MPhy = 0b0010, + LeCodedPhy = 0b0100, + } +} + +codable_as_bitmask!(Phy, u8); + +impl Phy { + const BYTE_SIZE: usize = 1; +} + +/// Represents Config Codec parameters for a single ASE. See ASCS v1.0.1 Section +/// 5.2. +#[derive(Debug, Clone, PartialEq)] +pub struct CodecConfiguration { + pub ase_id: AseId, + pub target_latency: TargetLatency, + pub target_phy: TargetPhy, + pub codec_id: CodecId, + pub codec_specific_configuration: Vec<u8>, +} + +impl CodecConfiguration { + const MIN_BYTE_SIZE: usize = + AseId::BYTE_SIZE + TargetLatency::BYTE_SIZE + TargetPhy::BYTE_SIZE + CodecId::BYTE_SIZE + 1; +} + +impl Decodable for CodecConfiguration { + type Error = ResponseCode; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < Self::MIN_BYTE_SIZE { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let codec_specific_configuration_len = buf[Self::MIN_BYTE_SIZE - 1] as usize; + let total_len = codec_specific_configuration_len + Self::MIN_BYTE_SIZE; + if buf.len() < total_len { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let try_decode_fn = |buf: &[u8]| { + let ase_id = AseId::try_from(buf[0])?; + let Ok(target_latency) = TargetLatency::try_from(buf[1]) else { + // TODO: unclear what to do if the target latency is out of range. + return Err(ResponseCode::ConfigurationParameterValue { + ase_id, + issue: ResponseIssue::Invalid, + reason: ResponseReason::MaxTransportLatency, + }); + }; + let Ok(target_phy) = TargetPhy::try_from(buf[2]) else { + return Err(ResponseCode::ConfigurationParameterValue { + ase_id, + issue: ResponseIssue::Unsupported, + reason: ResponseReason::Phy, + }); + }; + let Ok(codec_id) = CodecId::decode(&buf[3..]).0 else { + return Err(ResponseCode::ConfigurationParameterValue { + ase_id, + issue: ResponseIssue::Invalid, + reason: ResponseReason::CodecId, + }); + }; + let codec_specific_configuration = Vec::from( + &buf[Self::MIN_BYTE_SIZE..Self::MIN_BYTE_SIZE + codec_specific_configuration_len], + ); + Ok(Self { ase_id, target_latency, target_phy, codec_id, codec_specific_configuration }) + }; + (try_decode_fn(buf), total_len) + } +} + +/// Represents Config QoS parameters for a single ASE. See ASCS v1.0.1 Section +/// 5.2. +#[derive(Debug, Clone, PartialEq)] +pub struct QosConfiguration { + pub ase_id: AseId, + pub cig_id: CigId, + pub cis_id: CisId, + pub sdu_interval: SduInterval, + pub framing: Framing, + pub phy: Vec<Phy>, + pub max_sdu: MaxSdu, + pub retransmission_number: u8, + pub max_transport_latency: MaxTransportLatency, + pub presentation_delay: PresentationDelay, +} + +impl QosConfiguration { + const BYTE_SIZE: usize = AseId::BYTE_SIZE + + CigId::BYTE_SIZE + + CisId::BYTE_SIZE + + SduInterval::BYTE_SIZE + + Framing::BYTE_SIZE + + Phy::BYTE_SIZE + + MaxSdu::BYTE_SIZE + + 1 // retransmission_number + + MaxTransportLatency::BYTE_SIZE + + PresentationDelay::BYTE_SIZE; +} + +impl Decodable for QosConfiguration { + type Error = ResponseCode; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < QosConfiguration::BYTE_SIZE { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let try_decode_fn = |buf: &[u8]| { + let ase_id = AseId::try_from(buf[0])?; + let cig_id = + CigId::try_from(buf[1]).map_err(|_e| ResponseCode::UnspecifiedError { ase_id })?; + let cis_id = + CisId::try_from(buf[2]).map_err(|_e| ResponseCode::UnspecifiedError { ase_id })?; + let sdu_interval; + match SduInterval::decode(&buf[3..]) { + (Ok(interval), _) => { + sdu_interval = interval; + } + (Err(bt_common::packet_encoding::Error::BufferTooSmall), _) => { + return Err(ResponseCode::InvalidLength); + } + (Err(bt_common::packet_encoding::Error::OutOfRange), _) => { + return Err(ResponseCode::ConfigurationParameterValue { + ase_id, + issue: ResponseIssue::Invalid, + reason: ResponseReason::SduInterval, + }); + } + _ => unreachable!(), + }; + let Ok(framing) = Framing::try_from(buf[6]) else { + return Err(ResponseCode::ConfigurationParameterValue { + ase_id, + issue: ResponseIssue::Invalid, + reason: ResponseReason::Framing, + }); + }; + let phy = Phy::from_bits(buf[7]).collect(); + let max_sdu = [buf[8], buf[9]].try_into().map_err(|_e| { + ResponseCode::ConfigurationParameterValue { + issue: ResponseIssue::Invalid, + reason: ResponseReason::MaximumSduSize, + ase_id, + } + })?; + let retransmission_number = buf[10]; + let max_transport_latency = + MaxTransportLatency::decode(&buf[11..]).0.map_err(|e| match e { + bt_common::packet_encoding::Error::BufferTooSmall => { + ResponseCode::InvalidLength + } + bt_common::packet_encoding::Error::OutOfRange => { + ResponseCode::ConfigurationParameterValue { + issue: ResponseIssue::Invalid, + reason: ResponseReason::MaxTransportLatency, + ase_id, + } + } + _ => unreachable!(), + })?; + let presentation_delay = PresentationDelay::decode(&buf[13..]).0?; + Ok(Self { + ase_id, + cig_id, + cis_id, + sdu_interval, + framing, + phy, + max_sdu, + retransmission_number, + max_transport_latency, + presentation_delay, + }) + }; + (try_decode_fn(buf), QosConfiguration::BYTE_SIZE) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct CigId(u8); + +impl CigId { + const BYTE_SIZE: usize = 1; +} + +impl TryFrom<u8> for CigId { + type Error = bt_common::packet_encoding::Error; + + fn try_from(value: u8) -> Result<Self, Self::Error> { + if value > 0xEF { Err(Self::Error::OutOfRange) } else { Ok(CigId(value)) } + } +} + +impl From<CigId> for u8 { + fn from(value: CigId) -> Self { + value.0 + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct CisId(u8); + +impl CisId { + const BYTE_SIZE: usize = 1; +} + +impl TryFrom<u8> for CisId { + type Error = bt_common::packet_encoding::Error; + + fn try_from(value: u8) -> Result<Self, Self::Error> { + if value > 0xEF { Err(Self::Error::OutOfRange) } else { Ok(CisId(value)) } + } +} + +impl From<CisId> for u8 { + fn from(value: CisId) -> Self { + value.0 + } +} + +/// SDU Inteval parameter +/// This value is 24 bits long and little-endian on the wire. +/// It is stored native-endian here. +/// Valid range is [0x0000FF, 0x0FFFFF]. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct SduInterval(u32); + +impl SduInterval { + const BYTE_SIZE: usize = 3; +} + +impl Decodable for SduInterval { + type Error = bt_common::packet_encoding::Error; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < Self::BYTE_SIZE { + return (Err(Self::Error::BufferTooSmall), buf.len()); + } + let val = u32::from_le_bytes([buf[0], buf[1], buf[2], 0]); + if val < 0xFF || val > 0x0FFFFF { + return (Err(Self::Error::OutOfRange), Self::BYTE_SIZE); + } + (Ok(SduInterval(val)), Self::BYTE_SIZE) + } +} + +decodable_enum! { +pub enum Framing<u8, bt_common::packet_encoding::Error, OutOfRange> { + Unframed = 0x00, + Framed = 0x01, +} +} + +impl Framing { + const BYTE_SIZE: usize = 1; +} + +/// Max SDU parameter value. +/// Valid range is 0x0000-0x0FFF +/// Transmitted in little-endian. Stored here in native-endian. +#[derive(Debug, Clone, PartialEq)] +pub struct MaxSdu(u16); + +impl TryFrom<[u8; 2]> for MaxSdu { + type Error = bt_common::packet_encoding::Error; + + fn try_from(value: [u8; 2]) -> Result<Self, Self::Error> { + let value = u16::from_le_bytes([value[0], value[1]]); + if value > 0xFFF { + return Err(Self::Error::OutOfRange); + } + Ok(MaxSdu(value)) + } +} + +impl MaxSdu { + const BYTE_SIZE: usize = 2; +} + +/// Max Transport Latency +/// Valid range is [0x0005, 0x0FA0]. +/// Transmitted in little-endian, Stored in native-endian. +#[derive(Debug, Clone, PartialEq)] +pub struct MaxTransportLatency(u16); + +impl Decodable for MaxTransportLatency { + type Error = bt_common::packet_encoding::Error; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < Self::BYTE_SIZE { + return (Err(Self::Error::BufferTooSmall), buf.len()); + } + let val = u16::from_le_bytes([buf[0], buf[1]]); + if val < 0x0005 || val > 0x0FA0 { + return (Err(Self::Error::OutOfRange), Self::BYTE_SIZE); + } + (Ok(MaxTransportLatency(val)), Self::BYTE_SIZE) + } +} + +impl TryFrom<std::time::Duration> for MaxTransportLatency { + type Error = bt_common::packet_encoding::Error; + + fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> { + let Ok(milliseconds) = u16::try_from(value.as_millis()) else { + return Err(Self::Error::OutOfRange); + }; + if !(0x0005..=0x0FA0).contains(&milliseconds) { + return Err(Self::Error::OutOfRange); + } + Ok(Self(milliseconds)) + } +} + +impl MaxTransportLatency { + const BYTE_SIZE: usize = 2; +} + +/// Presentation delay parameter value being requested by the client for an ASE. +/// This value is 24 bits long (0x00FFFFFF max) +/// Transmitted in little-endian, Stored in native-endian. +#[derive(Debug, Clone, PartialEq)] +pub struct PresentationDelay { + pub microseconds: u32, +} + +impl PresentationDelay { + const BYTE_SIZE: usize = 3; +} + +impl Decodable for PresentationDelay { + type Error = ResponseCode; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < Self::BYTE_SIZE { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let microseconds = u32::from_le_bytes([buf[0], buf[1], buf[2], 0]); + (Ok(PresentationDelay { microseconds }), Self::BYTE_SIZE) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct AseIdWithMetadata { + pub ase_id: AseId, + pub metadata: Vec<Metadata>, +} + +impl AseIdWithMetadata { + const MIN_BYTE_SIZE: usize = 2; +} + +impl Decodable for AseIdWithMetadata { + type Error = ResponseCode; + + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + if buf.len() < Self::MIN_BYTE_SIZE { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let ase_id = match AseId::try_from(buf[0]) { + Ok(ase_id) => ase_id, + Err(e) => return (Err(e), buf.len()), + }; + let metadata_length = buf[1] as usize; + let total_length = Self::MIN_BYTE_SIZE + metadata_length; + if buf.len() < total_length { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + use bt_common::core::ltv::Error as LtvError; + use bt_common::core::ltv::LtValue; + let (metadata_results, consumed) = Metadata::decode_all(&buf[2..2 + metadata_length]); + if consumed != metadata_length { + return (Err(ResponseCode::InvalidLength), buf.len()); + } + let metadata_result: Result<Vec<Metadata>, LtvError<<Metadata as LtValue>::Type>> = + metadata_results.into_iter().collect(); + let Ok(metadata) = metadata_result else { + match metadata_result.unwrap_err() { + LtvError::MissingType => return (Err(ResponseCode::InvalidLength), buf.len()), + LtvError::MissingData(_) => return (Err(ResponseCode::InvalidLength), buf.len()), + LtvError::UnrecognizedType(_, type_value) => { + return ( + Err(ResponseCode::Metadata { + ase_id, + issue: ResponseIssue::Unsupported, + type_value, + }), + total_length, + ); + } + LtvError::LengthOutOfRange(_, t, _) | LtvError::TypeFailedToDecode(t, _) => { + return ( + Err(ResponseCode::Metadata { + ase_id, + issue: ResponseIssue::Invalid, + type_value: t.into(), + }), + total_length, + ); + } + } + }; + (Ok(Self { ase_id, metadata }), total_length) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use bt_common::packet_encoding::Encodable; + + use bt_common::core::ltv::LtValue; + use bt_common::generic_audio::{AudioLocation, codec_configuration}; + + #[test] + fn codec_configuration_roundtrip() { + let codec_specific_configuration = vec![ + codec_configuration::CodecConfiguration::SamplingFrequency( + codec_configuration::SamplingFrequency::F48000Hz, + ), + codec_configuration::CodecConfiguration::FrameDuration( + codec_configuration::FrameDuration::TenMs, + ), + codec_configuration::CodecConfiguration::AudioChannelAllocation( + [AudioLocation::FrontLeft].into_iter().collect(), + ), + codec_configuration::CodecConfiguration::CodecFramesPerSdu(1), + ]; + let codec_config_len = + codec_specific_configuration.iter().fold(0, |a, x| a + x.encoded_len()); + let mut vec = Vec::with_capacity(codec_config_len); + vec.resize(codec_config_len, 0); + LtValue::encode_all(codec_specific_configuration.clone().into_iter(), &mut vec[..]) + .unwrap(); + + let _codec_config = CodecConfiguration { + ase_id: AseId(5), + target_latency: TargetLatency::TargetLowLatency, + target_phy: TargetPhy::Le1MPhy, + codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Lc3), + codec_specific_configuration: vec, + }; + } + + #[test] + fn codec_configuration_decode() { + let encoded = &[ + 0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08, 0x02, 0x02, + 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00, + ]; + + let (codec_config, size) = CodecConfiguration::decode(&encoded[..]); + let codec_config = codec_config.unwrap(); + assert_eq!(size, encoded.len()); + assert_eq!(codec_config.ase_id, AseId(4)); + assert_eq!(codec_config.target_latency, TargetLatency::TargetBalanced); + assert_eq!(codec_config.target_phy, TargetPhy::Le2MPhy); + assert_eq!(codec_config.codec_id, CodecId::Assigned(bt_common::core::CodingFormat::Lc3)); + assert_eq!(codec_config.codec_specific_configuration.len(), 0x10); + } + + #[test] + fn ase_control_operation_decode_config_codec() { + let encoded = &[ + 0x01, 0x01, 0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08, + 0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00, + ]; + + let codec_config = CodecConfiguration::decode(&encoded[2..]).0.unwrap(); + let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect(); + let operation = AseControlOperation::try_from(encoded_vec); + + let Ok(operation) = operation else { + panic!("Expected decode to work correctly, got {operation:?}"); + }; + + assert_eq!( + operation, + AseControlOperation::ConfigCodec { + codec_configurations: vec![codec_config], + responses: Vec::new() + } + ); + } + + #[test] + fn ase_control_operation_decode_config_codec_some_failures() { + #[rustfmt::skip] + let encoded = &[ + 0x01, 0x02, // Config Codec, Two ASE_IDs + // First ASE_ID config + 0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08, + 0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00, + // Second ASE_ID config, fails based on invalid parameter value + 0x05, 0x05, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let codec_config = CodecConfiguration::decode(&encoded[2..]).0.unwrap(); + let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect(); + let operation = AseControlOperation::try_from(encoded_vec); + + let Ok(operation) = operation else { + panic!("Expected decode to work correctly, got {operation:?}"); + }; + + match operation { + AseControlOperation::ConfigCodec { codec_configurations, responses } => { + assert_eq!(codec_configurations, vec![codec_config]); + assert_eq!(responses.len(), 1); + assert!(matches!(responses[0], ResponseCode::ConfigurationParameterValue { .. })); + } + x => panic!("Expected ConfigCodec, got {x:?}"), + }; + } + + #[test] + fn ase_control_operation_decode_invalid_length() { + #[rustfmt::skip] + let encoded = &[ + 0x01, 0x02, // Config Codec, Two ASE_IDs + // First ASE_ID config + 0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08, + 0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00, + // Second ASE_ID config, fails based on invalid length + 0x05, 0x05, 0x02, 0x06, 0x00, 0x00, 0x00, + ]; + + let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect(); + let operation = AseControlOperation::try_from(encoded_vec); + + assert_eq!(operation, Err(ResponseCode::InvalidLength)); + } + + #[test] + fn qos_configuration_decode() { + let encoded = &[ + 0x03, // ASE_ID, + 0x00, // CIG_ID, + 0x00, // CIS_ID, + 0x10, 0x27, 0x00, 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, 0x01, 0x18, 0x00, 0x80, + ]; + let (qos_config, consumed) = QosConfiguration::decode(&encoded[..]); + + let qos_config = qos_config.unwrap(); + + assert_eq!(consumed, encoded.len()); + assert_eq!(qos_config.ase_id, AseId(3)); + assert_eq!(qos_config.cig_id, CigId(0)); + assert_eq!(qos_config.cis_id, CisId(0)); + assert_eq!(qos_config.sdu_interval, SduInterval(10000)); + assert_eq!(qos_config.framing, Framing::Framed); + assert_eq!(qos_config.phy, vec![Phy::Le2MPhy]); + assert_eq!(qos_config.max_sdu, MaxSdu(40)); + assert_eq!(qos_config.retransmission_number, 2); + assert_eq!(qos_config.max_transport_latency, MaxTransportLatency(500)); + assert_eq!(qos_config.presentation_delay, PresentationDelay { microseconds: 8388632 }); + } + + #[test] + fn aseid_with_metadata_decode() { + let encoded = &[ + 0x03, // ASE_ID + 0x08, // metadata length + 0x04, 0x04, 0x65, 0x6E, 0x67, // Language code + 0x02, 0x03, 0x61, // Program Info + ]; + + let (ase_with_metadata, consumed) = AseIdWithMetadata::decode(&encoded[..]); + let ase_with_metadata = ase_with_metadata.unwrap(); + assert_eq!(consumed, encoded.len()); + assert_eq!(ase_with_metadata.ase_id, AseId(3)); + assert_eq!(ase_with_metadata.metadata.len(), 2); + } + + #[test] + fn ase_control_operation_decode_qos() { + #[rustfmt::skip] + let encoded_qos = &[ + 0x02, 0x01, // Qos OpCode, 1 number_of_ases + 0x03, // ASE_ID, + 0x00, // CIG_ID, + 0x00, // CIS_ID, + 0x10, 0x27, 0x00, // SduInterval + 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, + 0x01, 0x18, 0x00, 0x80, + ]; + assert_eq!(QosConfiguration::BYTE_SIZE, encoded_qos.len() - 2); + let (qos_config, consumed) = QosConfiguration::decode(&encoded_qos[2..]); + assert!(qos_config.is_ok()); + assert_eq!(consumed, encoded_qos.len() - 2); + let encoded_qos: Vec<u8> = encoded_qos.into_iter().copied().collect(); + let operation = AseControlOperation::try_from(encoded_qos); + let Ok(_operation) = operation else { + panic!("Expected decode to work correctly, for {operation:?}"); + }; + + #[rustfmt::skip] + let encoded_qos_one_fails = &[ + 0x02, 0x03, // Qos OpCode, 3 number_of_ases + 0x03, // ASE_ID, + 0x00, // CIG_ID, + 0x00, // CIS_ID, + 0x10, 0x27, 0x00, // SduInterval + 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, + 0x01, 0x18, 0x00, 0x80, + 0x00, // ASE_ID, (AseId 0 is not allowed) + 0x00, // CIG_ID, + 0x00, // CIS_ID, + 0x10, 0x27, 0x00, // SduInterval + 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, + 0x01, 0x18, 0x00, 0x80, + 0x04, // ASE_ID + 0x00, // CIG_ID, + 0x00, // CIS_ID, + 0x10, 0x17, 0x00, // SduInterval + 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, + 0x01, 0x18, 0x00, 0x80, + ]; + + let encoded_qos_one_fails: Vec<u8> = encoded_qos_one_fails.into_iter().copied().collect(); + + match AseControlOperation::try_from(encoded_qos_one_fails) { + Ok(AseControlOperation::ConfigQos { qos_configurations, responses }) => { + assert_eq!(qos_configurations.len(), 2); + assert_eq!(responses.len(), 1); + assert_eq!(responses[0], ResponseCode::InvalidAseId { value: 0x00 }); + } + x => panic!("Expected ConfigQos to succeed, got {x:?}"), + }; + } +}
diff --git a/rust/bt-bap/src/types.rs b/rust/bt-bap/src/types.rs index cdb3c7e..a97a657 100644 --- a/rust/bt-bap/src/types.rs +++ b/rust/bt-bap/src/types.rs
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use bt_common::core::ltv::LtValue; use bt_common::core::CodecId; +use bt_common::core::ltv::LtValue; use bt_common::generic_audio::codec_configuration::CodecConfiguration; use bt_common::generic_audio::metadata_ltv::Metadata; use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError}; @@ -51,13 +51,13 @@ impl Decodable for BroadcastId { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() != Self::BYTE_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } let padded_bytes = [buf[0], buf[1], buf[2], 0x00]; - Ok((BroadcastId(u32::from_le_bytes(padded_bytes)), Self::BYTE_SIZE)) + (Ok(BroadcastId(u32::from_le_bytes(padded_bytes))), Self::BYTE_SIZE) } } @@ -96,17 +96,21 @@ impl Decodable for BroadcastAudioAnnouncement { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - let (broadcast_id, _) = BroadcastId::decode(&buf[0..3])?; - // According to the spec, broadcast audio announcement service data inlcudes - // broadcast id and any additional service data. We don't store any - // additional parameters, so for now we just "consume" all of data buffer - // without doing anything. - Ok((Self { broadcast_id }, buf.len())) + match BroadcastId::decode(&buf[0..3]) { + (Ok(broadcast_id), _) => { + // According to the spec, broadcast audio announcement service data inlcudes + // broadcast id and any additional service data. We don't store any + // additional parameters, so for now we just "consume" all of data buffer + // without doing anything. + (Ok(Self { broadcast_id }), buf.len()) + } + (Err(e), _) => (Err(e), buf.len()), + } } } @@ -128,9 +132,9 @@ impl Decodable for BroadcastAudioSourceEndpoint { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } let mut idx = 0 as usize; @@ -140,20 +144,27 @@ let num_big: usize = buf[idx] as usize; idx += 1; if num_big < 1 { - return Err(PacketError::InvalidParameter(format!( - "num of subgroups shall be at least 1 got {num_big}" - ))); + return ( + Err(PacketError::InvalidParameter(format!( + "num of subgroups shall be at least 1 got {num_big}" + ))), + buf.len(), + ); } - let mut big = Vec::new(); while big.len() < num_big { - let (group, len) = BroadcastIsochronousGroup::decode(&buf[idx..]) - .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?; - big.push(group); - idx += len; + match BroadcastIsochronousGroup::decode(&buf[idx..]) { + (Ok(group), consumed) => { + big.push(group); + idx += consumed; + } + (Err(e), _) => { + return (Err(PacketError::InvalidParameter(e.to_string())), buf.len()); + } + }; } - Ok((Self { presentation_delay_ms: presentation_delay, big }, idx)) + (Ok(Self { presentation_delay_ms: presentation_delay, big }), idx) } } @@ -178,59 +189,83 @@ impl Decodable for BroadcastIsochronousGroup { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < BroadcastIsochronousGroup::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } let mut idx = 0; let num_bis = buf[idx] as usize; idx += 1; if num_bis < 1 { - return Err(PacketError::InvalidParameter(format!( - "num of BIS shall be at least 1 got {num_bis}" - ))); + return ( + Err(PacketError::InvalidParameter(format!( + "num of BIS shall be at least 1 got {num_bis}" + ))), + buf.len(), + ); } - let (codec_id, read_bytes) = CodecId::decode(&buf[idx..])?; - idx += read_bytes; + let mut decode_fn = || { + let codec_id; + match CodecId::decode(&buf[idx..]) { + (Ok(id), consumed) => { + codec_id = id; + idx += consumed; + } + (Err(e), _) => { + return Err(e); + } + }; - let codec_config_len = buf[idx] as usize; - idx += 1; - if idx + codec_config_len > buf.len() { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + let codec_config_len = buf[idx] as usize; + idx += 1; + if idx + codec_config_len > buf.len() { + return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + } + let (results, consumed) = + CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]); + if consumed != codec_config_len { + return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + } + + let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect(); + idx += codec_config_len; + + let metadata_len = buf[idx] as usize; + idx += 1; + if idx + metadata_len > buf.len() { + return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + } + + let (results_metadata, consumed_len) = + Metadata::decode_all(&buf[idx..idx + metadata_len]); + if consumed_len != metadata_len { + return Err(PacketError::UnexpectedDataLength); + } + // Ignore any undecodable metadata types + let metadata = results_metadata.into_iter().filter_map(Result::ok).collect(); + idx += consumed_len; + + let mut bis = Vec::new(); + while bis.len() < num_bis { + match BroadcastIsochronousStream::decode(&buf[idx..]) { + (Ok(stream), consumed) => { + bis.push(stream); + idx += consumed; + } + (Err(e), _consumed) => { + return Err(PacketError::InvalidParameter(e.to_string())); + } + } + } + + Ok((BroadcastIsochronousGroup { codec_id, codec_specific_configs, metadata, bis }, idx)) + }; + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), } - let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]); - if consumed != codec_config_len { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); - } - - let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect(); - idx += codec_config_len; - - let metadata_len = buf[idx] as usize; - idx += 1; - if idx + metadata_len > buf.len() { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); - } - - let (results_metadata, consumed_len) = Metadata::decode_all(&buf[idx..idx + metadata_len]); - if consumed_len != metadata_len { - return Err(PacketError::UnexpectedDataLength); - } - // Ignore any undecodable metadata types - let metadata = results_metadata.into_iter().filter_map(Result::ok).collect(); - idx += consumed_len; - - let mut bis = Vec::new(); - while bis.len() < num_bis { - let (stream, len) = BroadcastIsochronousStream::decode(&buf[idx..]) - .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?; - bis.push(stream); - idx += len; - } - - Ok((BroadcastIsochronousGroup { codec_id, codec_specific_configs, metadata, bis }, idx)) } } @@ -247,9 +282,9 @@ impl Decodable for BroadcastIsochronousStream { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < BroadcastIsochronousStream::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } let mut idx = 0; @@ -262,15 +297,18 @@ let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]); if consumed != codec_config_len { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect(); idx += codec_config_len; - Ok(( - BroadcastIsochronousStream { bis_index, codec_specific_config: codec_specific_configs }, + ( + Ok(BroadcastIsochronousStream { + bis_index, + codec_specific_config: codec_specific_configs, + }), idx, - )) + ) } } @@ -280,8 +318,8 @@ use std::collections::HashSet; - use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency}; use bt_common::generic_audio::AudioLocation; + use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency}; #[test] fn broadcast_id() { @@ -297,8 +335,8 @@ let bytes = vec![0x0C, 0x0B, 0x0A]; assert_eq!(buf, bytes); - let (got, bytes) = BroadcastId::decode(&bytes).expect("should succeed"); - assert_eq!(got, id); + let (got, bytes) = BroadcastId::decode(&bytes); + assert_eq!(got, Ok(id)); assert_eq!(bytes, BroadcastId::BYTE_SIZE); let got = BroadcastId::try_from(u32::from_le_bytes([0x0C, 0x0B, 0x0A, 0x00])) .expect("should succeed"); @@ -310,15 +348,15 @@ let bytes = vec![0x0C, 0x0B, 0x0A]; let broadcast_id = BroadcastId::try_from(0x000A0B0C).unwrap(); - let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes).expect("should succeed"); - assert_eq!(got, BroadcastAudioAnnouncement { broadcast_id }); + let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes); + assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id })); assert_eq!(consumed, 3); let bytes = vec![ 0x0C, 0x0B, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, /* some other additional data */ ]; - let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes).expect("should succeed"); - assert_eq!(got, BroadcastAudioAnnouncement { broadcast_id }); + let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes); + assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id })); assert_eq!(consumed, 8); } @@ -331,11 +369,10 @@ 0x05, 0x03, 0x03, 0x00, 0x00, 0x0C, // audio location LTV ]; - let (bis, _read_bytes) = - BroadcastIsochronousStream::decode(&buf[..]).expect("should not fail"); + let (bis, _read_bytes) = BroadcastIsochronousStream::decode(&buf[..]); assert_eq!( bis, - BroadcastIsochronousStream { + Ok(BroadcastIsochronousStream { bis_index: 0x01, codec_specific_config: vec![ CodecConfiguration::SamplingFrequency(SamplingFrequency::F32000Hz), @@ -346,7 +383,7 @@ AudioLocation::RightSurround ])), ], - } + }) ); } @@ -361,8 +398,7 @@ 0x02, 0x03, 0x02, 0x02, 0x01, // bis index, codec specific config len, frame duration LTV (bis #2) ]; - let (big, _read_bytes) = - BroadcastIsochronousGroup::decode(&buf[..]).expect("should not fail"); + let big = BroadcastIsochronousGroup::decode(&buf[..]).0.expect("should not fail"); assert_eq!( big, BroadcastIsochronousGroup { @@ -397,8 +433,7 @@ 0x01, 0x03, 0x02, 0x05, 0x08, // bis index, codec specific config len, codec frame blocks LTV (big #2 / bis #2) ]; - let (base, _read_bytes) = - BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail"); + let base = BroadcastAudioSourceEndpoint::decode(&buf[..]).0.expect("should not fail"); assert_eq!(base.presentation_delay_ms, 0x00302010); assert_eq!(base.big.len(), 2); assert_eq!( @@ -449,8 +484,8 @@ 0x06, // codec_specific_configuration_length of the 2nd BIS 0x05, 0x03, 0x02, 0x00, 0x00, 0x00, // front right audio channel ]; - let (base, read_bytes) = - BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail"); + let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]); + let base = base.expect("should not fail"); assert_eq!(read_bytes, buf.len()); assert_eq!(base.presentation_delay_ms, 20000); assert_eq!(base.big.len(), 1); @@ -473,8 +508,8 @@ 0x03, // codec_specific_configuration_length of the 1st BIS 0x02, 0x01, 0x05, // sampling frequency 24 kHz ]; - let (base, read_bytes) = - BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail"); + let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]); + let base = base.expect("should not fail"); assert_eq!(read_bytes, buf.len()); assert_eq!(base.presentation_delay_ms, 20000); assert_eq!(base.big.len(), 1);
diff --git a/rust/bt-bass/src/client.rs b/rust/bt-bass/src/client.rs index b48d8ed..f7b3f5b 100644 --- a/rust/bt-bass/src/client.rs +++ b/rust/bt-bass/src/client.rs
@@ -8,8 +8,8 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt}; use futures::Future; +use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt}; use log::warn; use parking_lot::Mutex; @@ -177,8 +177,8 @@ // record. let mut buf = vec![0; READ_CHARACTERISTIC_BUFFER_SIZE]; match c.read(&mut buf[..]).await { - Ok(read_bytes) => match BroadcastReceiveState::decode(&buf[0..read_bytes]) { - Ok((decoded, _decoded_bytes)) => { + Ok(read_bytes) => match BroadcastReceiveState::decode(&buf[0..read_bytes]).0 { + Ok(decoded) => { brs_map.insert(*c.handle(), decoded); continue; } @@ -420,15 +420,15 @@ use assert_matches::assert_matches; use futures::executor::block_on; - use futures::{pin_mut, FutureExt}; + use futures::{FutureExt, pin_mut}; - use bt_common::core::AdvertisingSetId; use bt_common::Uuid; + use bt_common::core::AdvertisingSetId; + use bt_gatt::Characteristic; use bt_gatt::test_utils::*; use bt_gatt::types::{ AttributePermissions, CharacteristicProperties, CharacteristicProperty, Handle, }; - use bt_gatt::Characteristic; const RECEIVE_STATE_1_HANDLE: Handle = Handle(1); const RECEIVE_STATE_2_HANDLE: Handle = Handle(2);
diff --git a/rust/bt-bass/src/client/event.rs b/rust/bt-bass/src/client/event.rs index ca48fa8..0ff2768 100644 --- a/rust/bt-bass/src/client/event.rs +++ b/rust/bt-bass/src/client/event.rs
@@ -15,9 +15,9 @@ use bt_gatt::client::CharacteristicNotification; use bt_gatt::types::Error as BtGattError; +use crate::client::KnownBroadcastSources; use crate::client::error::Error; use crate::client::error::ServiceError; -use crate::client::KnownBroadcastSources; use crate::types::*; #[derive(Clone, Debug, PartialEq)] @@ -150,7 +150,7 @@ } Poll::Ready(Some(Ok(notification))) => { let char_handle = notification.handle; - let Ok((new_state, _)) = + let (Ok(new_state), _) = BroadcastReceiveState::decode(notification.value.as_slice()) else { self.event_queue.push_back(Ok(Event::UnknownPacket)); @@ -190,7 +190,7 @@ receive_state.big_encryption, ))); } else { - let other_events = Event::from_broadcast_receive_state(receive_state); + let other_events = Event::from_broadcast_receive_state(&receive_state); for e in other_events.into_iter() { multi_events.push_back(Ok(e)); }
diff --git a/rust/bt-bass/src/types.rs b/rust/bt-bass/src/types.rs index 0ca916a..22baa25 100644 --- a/rust/bt-bass/src/types.rs +++ b/rust/bt-bass/src/types.rs
@@ -7,7 +7,7 @@ use bt_common::core::{AddressType, AdvertisingSetId, PaInterval}; use bt_common::generic_audio::metadata_ltv::*; use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError}; -use bt_common::{decodable_enum, Uuid}; +use bt_common::{Uuid, decodable_enum}; pub const ADDRESS_BYTE_SIZE: usize = 6; const NUM_SUBGROUPS_BYTE_SIZE: usize = 1; @@ -78,12 +78,12 @@ 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); + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + const BYTE_SIZE: usize = ControlPointOpcode::BYTE_SIZE; + if buf.len() < BYTE_SIZE { + return (Err(PacketError::BufferTooSmall), buf.len()); } - let _ = Self::check_opcode(buf[0])?; - Ok((RemoteScanStoppedOperation, ControlPointOpcode::BYTE_SIZE)) + (Self::check_opcode(buf[0]).map(|_| RemoteScanStoppedOperation), BYTE_SIZE) } } @@ -116,12 +116,12 @@ 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); + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { + const BYTE_SIZE: usize = ControlPointOpcode::BYTE_SIZE; + if buf.len() < BYTE_SIZE { + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - let _ = Self::check_opcode(buf[0])?; - Ok((RemoteScanStartedOperation, ControlPointOpcode::BYTE_SIZE)) + (Self::check_opcode(buf[0]).map(|_| RemoteScanStartedOperation), BYTE_SIZE) } } @@ -194,43 +194,50 @@ impl Decodable for AddSourceOperation { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - 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 decode_fn = || { + 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 mut idx: usize = 16; + for _i in 0..num_subgroups { + if buf.len() <= idx { + return Err(PacketError::UnexpectedDataLength); + } + let (decoded, consumed) = BigSubgroup::decode(&buf[idx..]); + subgroups.push(decoded?); + idx += consumed; } - 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, + )) + }; + + match decode_fn() { + Ok((result, consumed)) => (Ok(result), consumed), + Err(e) => (Err(e), buf.len()), } - Ok(( - Self { - advertiser_address_type, - advertiser_address, - advertising_sid, - broadcast_id, - pa_sync, - pa_interval, - subgroups, - }, - idx, - )) } } @@ -303,27 +310,34 @@ 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> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - 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 decode_fn = || { + 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 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; } - let decoded = BigSubgroup::decode(&buf[idx..])?; - subgroups.push(decoded.0); - idx += decoded.1; + Ok((Self { source_id, pa_sync, pa_interval, subgroups }, idx)) + }; + + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), } - Ok((Self { source_id, pa_sync, pa_interval, subgroups }, idx)) } } @@ -384,15 +398,22 @@ 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> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - 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)) + let decode_fn = || { + 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)) + }; + + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), + } } } @@ -437,13 +458,19 @@ 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> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - let _ = Self::check_opcode(buf[0])?; - let source_id = buf[1]; - Ok((RemoveSourceOperation(source_id), Self::PACKET_SIZE)) + let decode_fn = || { + let _ = Self::check_opcode(buf[0])?; + let source_id = buf[1]; + Ok((RemoveSourceOperation(source_id), Self::PACKET_SIZE)) + }; + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), + } } } @@ -555,27 +582,33 @@ impl Decodable for BigSubgroup { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < BigSubgroup::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap()); - let metadata_len = buf[4] as usize; + let decode_fn = || { + let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap()); + let metadata_len = buf[4] as usize; - let mut start_idx = 5; - if buf.len() < start_idx + metadata_len { - return Err(PacketError::UnexpectedDataLength); - } + let mut start_idx = 5; + if buf.len() < start_idx + metadata_len { + return Err(PacketError::UnexpectedDataLength); + } - let (results_metadata, consumed_len) = - Metadata::decode_all(&buf[start_idx..start_idx + metadata_len]); - start_idx += consumed_len; - if start_idx != 5 + metadata_len { - return Err(PacketError::UnexpectedDataLength); + let (results_metadata, consumed_len) = + Metadata::decode_all(&buf[start_idx..start_idx + metadata_len]); + start_idx += consumed_len; + if start_idx != 5 + metadata_len { + return Err(PacketError::UnexpectedDataLength); + } + // Ignore any undecodable metadata types + let metadata = results_metadata.into_iter().filter_map(Result::ok).collect(); + Ok((BigSubgroup { bis_sync: BisSync(bis_sync), metadata }, start_idx)) + }; + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), } - // Ignore any undecodable metadata types - let metadata = results_metadata.into_iter().filter_map(Result::ok).collect(); - Ok((BigSubgroup { bis_sync: BisSync(bis_sync), metadata }, start_idx)) } } @@ -647,12 +680,14 @@ impl Decodable for BroadcastReceiveState { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() == 0 { - return Ok((Self::Empty, 0)); + return (Ok(Self::Empty), 0); } - let res = ReceiveState::decode(&buf[..])?; - Ok((Self::NonEmpty(res.0), res.1)) + match ReceiveState::decode(&buf[..]) { + (Ok(state), consumed) => (Ok(Self::NonEmpty(state)), consumed), + (Err(e), consumed) => (Err(e), consumed), + } } } @@ -741,49 +776,63 @@ impl Decodable for ReceiveState { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < Self::MIN_PACKET_SIZE { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - 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 decode_fn = || { + 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 { + let big_encryption; + let mut idx = 13; + match EncryptionStatus::decode(&buf[13..]) { + (Ok(encryption), consumed) => { + big_encryption = encryption; + idx += consumed; + } + (Err(e), _) => { + return Err(e); + } + } if buf.len() <= idx { return Err(PacketError::UnexpectedDataLength); } - let (subgroup, consumed) = BigSubgroup::decode(&buf[idx..])?; - subgroups.push(subgroup); - idx += consumed; + 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, + )) + }; + match decode_fn() { + Ok((obj, consumed)) => (Ok(obj), consumed), + Err(e) => (Err(e), buf.len()), } - Ok(( - ReceiveState { - source_id, - source_address_type, - source_address, - source_adv_sid, - broadcast_id, - pa_sync_state, - big_encryption, - subgroups, - }, - idx, - )) } } @@ -871,21 +920,21 @@ impl Decodable for EncryptionStatus { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < 1 { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } match buf[0] { - 0x00 => Ok((Self::NotEncrypted, 1)), - 0x01 => Ok((Self::BroadcastCodeRequired, 1)), - 0x02 => Ok((Self::Decrypting, 1)), + 0x00 => (Ok(Self::NotEncrypted), 1), + 0x01 => (Ok(Self::BroadcastCodeRequired), 1), + 0x02 => (Ok(Self::Decrypting), 1), 0x03 => { if buf.len() < 17 { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } - Ok((Self::BadCode(buf[1..17].try_into().unwrap()), 17)) + (Ok(Self::BadCode(buf[1..17].try_into().unwrap())), 17) } - _ => Err(PacketError::OutOfRange), + _ => (Err(PacketError::OutOfRange), buf.len()), } } } @@ -950,9 +999,9 @@ 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); + let (decoded, len) = EncryptionStatus::decode(&bytes); + assert_eq!(decoded, Ok(not_encrypted)); + assert_eq!(len, 1); // Encoding bad code status with code. let bad_code = EncryptionStatus::BadCode([ @@ -969,10 +1018,10 @@ ]; 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); + // Decoding bad code statsu with code. + let (decoded, len) = EncryptionStatus::decode(&bytes); + assert_eq!(decoded, Ok(bad_code)); + assert_eq!(len, 17); } #[test] @@ -992,11 +1041,11 @@ // Cannot decode empty buffer. let buf = vec![]; - let _ = EncryptionStatus::decode(&buf).expect_err("should fail"); + let _ = EncryptionStatus::decode(&buf).0.expect_err("should fail"); // Bad code status with no code. let buf = vec![0x03]; - let _ = EncryptionStatus::decode(&buf).expect_err("should fail"); + let _ = EncryptionStatus::decode(&buf).0.expect_err("should fail"); } #[test] @@ -1032,9 +1081,9 @@ 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); + let (decoded, len) = RemoteScanStoppedOperation::decode(&bytes); + assert_eq!(decoded, Ok(stopped)); + assert_eq!(len, 1); } #[test] @@ -1049,9 +1098,9 @@ 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); + let (decoded, len) = RemoteScanStartedOperation::decode(&bytes); + assert_eq!(decoded, Ok(started)); + assert_eq!(len, 1); } #[test] @@ -1077,9 +1126,9 @@ 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); + let (decoded, len) = AddSourceOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 16); } #[test] @@ -1111,9 +1160,9 @@ 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); + let (decoded, len) = AddSourceOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 31); } #[test] @@ -1129,9 +1178,9 @@ 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); + let (decoded, len) = ModifySourceOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 6); } #[test] @@ -1156,9 +1205,9 @@ 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); + let (decoded, len) = ModifySourceOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 21); } #[test] @@ -1182,9 +1231,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = SetBroadcastCodeOperation::decode(&bytes).expect("should succeed"); - assert_eq!(decoded.0, op); - assert_eq!(decoded.1, 18); + let (decoded, len) = SetBroadcastCodeOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 18); } #[test] @@ -1199,9 +1248,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = RemoveSourceOperation::decode(&bytes).expect("should succeed"); - assert_eq!(decoded.0, op); - assert_eq!(decoded.1, 2); + let (decoded, len) = RemoveSourceOperation::decode(&bytes); + assert_eq!(decoded, Ok(op)); + assert_eq!(len, 2); } #[test] @@ -1233,9 +1282,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed"); - assert_eq!(decoded.0, state); - assert_eq!(decoded.1, 31); + let (decoded, len) = BroadcastReceiveState::decode(&bytes); + assert_eq!(decoded, Ok(state)); + assert_eq!(len, 31); } #[test] @@ -1266,8 +1315,8 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed"); - assert_eq!(decoded.0, state); - assert_eq!(decoded.1, 23); + let (decoded, len) = BroadcastReceiveState::decode(&bytes); + assert_eq!(decoded, Ok(state)); + assert_eq!(len, 23); } }
diff --git a/rust/bt-battery/src/monitor/client.rs b/rust/bt-battery/src/monitor/client.rs index 516cb72..d1ae91e 100644 --- a/rust/bt-battery/src/monitor/client.rs +++ b/rust/bt-battery/src/monitor/client.rs
@@ -3,14 +3,14 @@ // found in the LICENSE file. use bt_common::packet_encoding::Decodable; +use bt_gatt::GattTypes; use bt_gatt::client::{CharacteristicNotification, PeerService, ServiceCharacteristic}; use bt_gatt::types::{CharacteristicProperty, Error as GattLibraryError, Handle}; -use bt_gatt::GattTypes; use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt}; use std::task::Poll; use crate::error::{Error, ServiceError}; -use crate::types::{BatteryLevel, BATTERY_LEVEL_UUID, READ_CHARACTERISTIC_BUFFER_SIZE}; +use crate::types::{BATTERY_LEVEL_UUID, BatteryLevel, READ_CHARACTERISTIC_BUFFER_SIZE}; /// Represents the termination status of a Stream. #[derive(Clone, Copy, Debug, PartialEq, Default)] @@ -70,9 +70,8 @@ let result = futures::ready!(self.notification_streams.poll_next_unpin(cx)); match result { Some(Ok(notification)) => { - let battery_level_result = - BatteryLevel::decode(¬ification.value[..]).map(|r| r.0).map_err(Into::into); - Poll::Ready(Some(battery_level_result)) + let battery_level_result = BatteryLevel::decode(¬ification.value[..]).0?; + Poll::Ready(Some(Ok(battery_level_result))) } Some(Err(e)) => { // GATT Errors are not fatal and will be relayed to the stream. @@ -151,8 +150,9 @@ let (battery_level, _decoded_bytes) = { let mut buf = vec![0; READ_CHARACTERISTIC_BUFFER_SIZE]; let read_bytes = primary_battery_level_characteristic.read(&mut buf[..]).await?; - BatteryLevel::decode(&buf[0..read_bytes])? + BatteryLevel::decode(&buf[0..read_bytes]) }; + let battery_level = battery_level?; // Subscribe to notifications on the battery level characteristic if it // is supported. @@ -199,13 +199,13 @@ pub(crate) mod tests { use super::*; - use bt_common::packet_encoding::Error as PacketError; use bt_common::Uuid; + use bt_common::packet_encoding::Error as PacketError; use bt_gatt::test_utils::{FakeClient, FakePeerService, FakeTypes}; use bt_gatt::types::{ AttributePermissions, Characteristic, CharacteristicProperties, GattError, }; - use futures::{pin_mut, FutureExt}; + use futures::{FutureExt, pin_mut}; pub(crate) const BATTERY_LEVEL_HANDLE: Handle = Handle(0x1); pub(crate) fn fake_battery_service(battery_level: u8) -> FakePeerService {
diff --git a/rust/bt-battery/src/types.rs b/rust/bt-battery/src/types.rs index b7980c5..ccc4428 100644 --- a/rust/bt-battery/src/types.rs +++ b/rust/bt-battery/src/types.rs
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use bt_common::packet_encoding::{Decodable, Error as PacketError}; use bt_common::Uuid; +use bt_common::packet_encoding::{Decodable, Error as PacketError}; /// The UUID of the GATT battery service. /// Defined in Assigned Numbers Section 3.4.2. @@ -21,13 +21,13 @@ impl Decodable for BatteryLevel { type Error = PacketError; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < 1 { - return Err(PacketError::UnexpectedDataLength); + return (Err(PacketError::UnexpectedDataLength), buf.len()); } let level_percent = buf[0].clamp(0, 100); - Ok((BatteryLevel(level_percent), 1)) + (Ok(BatteryLevel(level_percent)), 1) } } @@ -38,31 +38,32 @@ #[test] fn decode_battery_level_success() { let buf = [55]; - let (parsed, parsed_size) = BatteryLevel::decode(&buf).expect("valid battery level"); - assert_eq!(parsed, BatteryLevel(55)); + let (parsed, parsed_size) = BatteryLevel::decode(&buf); + assert_eq!(parsed, Ok(BatteryLevel(55))); assert_eq!(parsed_size, 1); } #[test] fn decode_large_battery_level_clamped() { let buf = [125]; // Too large, expected to be a percentage value. - let (parsed, parsed_size) = BatteryLevel::decode(&buf).expect("valid battery level"); - assert_eq!(parsed, BatteryLevel(100)); + let (parsed, parsed_size) = BatteryLevel::decode(&buf); + assert_eq!(parsed, Ok(BatteryLevel(100))); assert_eq!(parsed_size, 1); } #[test] fn decode_large_buf_success() { let large_buf = [19, 0]; // Only expect a single u8 for the level. - let (parsed, parsed_size) = BatteryLevel::decode(&large_buf).expect("valid battery level"); - assert_eq!(parsed, BatteryLevel(19)); // Only the first byte should be read. + let (parsed, parsed_size) = BatteryLevel::decode(&large_buf); + assert_eq!(parsed, Ok(BatteryLevel(19))); // Only the first byte should be read. assert_eq!(parsed_size, 1); } #[test] fn decode_invalid_battery_level_buf_is_error() { let buf = []; - let result = BatteryLevel::decode(&buf); + let (result, parsed_size) = BatteryLevel::decode(&buf); assert_eq!(result, Err(PacketError::UnexpectedDataLength)); + assert_eq!(parsed_size, 0); } }
diff --git a/rust/bt-broadcast-assistant/src/assistant/event.rs b/rust/bt-broadcast-assistant/src/assistant/event.rs index e1e444c..7290e47 100644 --- a/rust/bt-broadcast-assistant/src/assistant/event.rs +++ b/rust/bt-broadcast-assistant/src/assistant/event.rs
@@ -8,14 +8,14 @@ use std::task::Poll; use bt_bap::types::{BroadcastAudioSourceEndpoint, BroadcastId}; +use bt_common::PeerId; use bt_common::packet_encoding::Decodable; use bt_common::packet_encoding::Error as PacketError; -use bt_common::PeerId; use bt_gatt::central::{AdvertisingDatum, ScanResult}; use crate::assistant::{ - DiscoveredBroadcastSources, Error, BASIC_AUDIO_ANNOUNCEMENT_SERVICE, - BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE, + BASIC_AUDIO_ANNOUNCEMENT_SERVICE, BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE, + DiscoveredBroadcastSources, Error, }; use crate::types::BroadcastSource; @@ -59,11 +59,11 @@ continue; }; if *uuid == BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE { - let (bid, _) = BroadcastId::decode(data.as_slice())?; + let bid = BroadcastId::decode(data.as_slice()).0?; source.get_or_insert(BroadcastSource::default()).with_broadcast_id(bid); } else if *uuid == BASIC_AUDIO_ANNOUNCEMENT_SERVICE { // TODO(dayeonglee): revisit when we implement periodic advertisement. - let (base, _) = BroadcastAudioSourceEndpoint::decode(data.as_slice())?; + let base = BroadcastAudioSourceEndpoint::decode(data.as_slice()).0?; source.get_or_insert(BroadcastSource::default()).with_endpoint(base); } }
diff --git a/rust/bt-common/src/core.rs b/rust/bt-common/src/core.rs index f7d94e5..db4a1d9 100644 --- a/rust/bt-common/src/core.rs +++ b/rust/bt-common/src/core.rs
@@ -162,18 +162,18 @@ impl crate::packet_encoding::Decodable for CodecId { type Error = crate::packet_encoding::Error; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < 5 { - return Err(crate::packet_encoding::Error::UnexpectedDataLength); + return (Err(crate::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let format = buf[0].into(); if format != CodingFormat::VendorSpecific { // Maybe don't ignore the company and vendor id, and check if they are wrong. - return Ok((Self::Assigned(format), 5)); + return (Ok(Self::Assigned(format)), 5); } let company_id = u16::from_le_bytes([buf[1], buf[2]]).into(); let vendor_specific_codec_id = u16::from_le_bytes([buf[3], buf[4]]); - Ok((Self::VendorSpecific { company_id, vendor_specific_codec_id }, 5)) + (Ok(Self::VendorSpecific { company_id, vendor_specific_codec_id }), 5) } } @@ -239,34 +239,34 @@ #[test] fn decode_codec_id() { let assigned = [0x01, 0x00, 0x00, 0x00, 0x00]; - let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed"); - assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog)); + let (codec_id, _) = CodecId::decode(&assigned[..]); + assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog))); let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22]; - let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed"); + let (codec_id, _) = CodecId::decode(&vendor_specific[..]); assert_eq!( codec_id, - CodecId::VendorSpecific { + Ok(CodecId::VendorSpecific { company_id: (0xFD36 as u16).into(), vendor_specific_codec_id: 0x2211 - } + }) ); } #[test] fn encode_codec_id() { let assigned = [0x01, 0x00, 0x00, 0x00, 0x00]; - let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed"); - assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog)); + let (codec_id, _) = CodecId::decode(&assigned[..]); + assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog))); let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22]; - let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed"); + let (codec_id, _) = CodecId::decode(&vendor_specific[..]); assert_eq!( codec_id, - CodecId::VendorSpecific { + Ok(CodecId::VendorSpecific { company_id: (0xFD36 as u16).into(), vendor_specific_codec_id: 0x2211 - } + }) ); } }
diff --git a/rust/bt-common/src/core/ltv.rs b/rust/bt-common/src/core/ltv.rs index 6a1083a..e8c7498 100644 --- a/rust/bt-common/src/core/ltv.rs +++ b/rust/bt-common/src/core/ltv.rs
@@ -54,21 +54,15 @@ return (results, std::cmp::min(buf.len(), total_consumed)); } let indicated_len = buf[total_consumed] as usize; - match Self::decode(&buf[total_consumed..=total_consumed + indicated_len]) { - Ok((item, consumed)) => { + let range_end = std::cmp::min(buf.len() - 1, total_consumed + indicated_len); + match Self::decode(&buf[total_consumed..=range_end]) { + (Ok(item), consumed) => { results.push(Ok(item)); total_consumed += consumed; } - // If we are missing the type / length completely, or missing some of the data, we - // can't continue - Err(e @ Error::MissingType) | Err(e @ Error::MissingData(_)) => { + (Err(e), consumed) => { results.push(Err(e)); - return (results, total_consumed); - } - Err(e) => { - results.push(Err(e)); - // Consume the bytes - total_consumed += indicated_len + 1; + total_consumed += consumed; } } } @@ -134,28 +128,36 @@ } } -impl<T: LtValue> Decodable for T { +impl<T> Decodable for T +where + T: LtValue, +{ type Error = Error<T::Type>; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < 2 { - return Err(Error::MissingType); + return (Err(Error::MissingType), buf.len()); } let indicated_len = buf[0] as usize; + let too_short = buf.len() < indicated_len + 1; + let Some(ty) = Self::type_from_octet(buf[1]) else { - return Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1])); + return ( + Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1])), + if too_short { buf.len() } else { indicated_len + 1 }, + ); }; - if buf.len() < indicated_len + 1 { - return Err(Error::MissingData(ty)); + if too_short { + return (Err(Error::MissingData(ty)), buf.len()); } let size_range = Self::length_range_from_type(ty); let remaining_len = (buf.len() - 1) as u8; if !size_range.contains(&remaining_len) { - return Err(Error::LengthOutOfRange(remaining_len, ty, size_range)); + return (Err(Error::LengthOutOfRange(remaining_len, ty, size_range)), buf.len()); } match Self::decode_value(&ty, &buf[2..=indicated_len]) { - Err(e) => Err(Error::TypeFailedToDecode(ty, e)), - Ok(s) => Ok((s, indicated_len + 1)), + Err(e) => (Err(Error::TypeFailedToDecode(ty, e)), indicated_len + 1), + Ok(s) => (Ok(s), indicated_len + 1), } } } @@ -304,6 +306,15 @@ } #[test] + fn decode_wronglength() { + let encoded = [0x03, 0x02, 0x10, 0x01, 0x04, 0x03, 0x10, 0x01]; + let (decoded, consumed) = TestValues::decode_all(&encoded); + assert_eq!(consumed, encoded.len()); + assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097))); + assert_eq!(decoded[1], Err(Error::MissingData(TestType::TwoBytesLittleEndian))); + } + + #[test] fn encode_twobytes() { let value = TestValues::TwoBytes(0x0A0B); let mut buf = [0; 4];
diff --git a/rust/bt-common/src/generic_audio/metadata_ltv.rs b/rust/bt-common/src/generic_audio/metadata_ltv.rs index 5e95215..75b4178 100644 --- a/rust/bt-common/src/generic_audio/metadata_ltv.rs +++ b/rust/bt-common/src/generic_audio/metadata_ltv.rs
@@ -4,7 +4,7 @@ use crate::core::ltv::LtValue; use crate::packet_encoding::Error as PacketError; -use crate::{decodable_enum, CompanyId}; +use crate::{CompanyId, decodable_enum}; use crate::generic_audio::ContextType; @@ -286,9 +286,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 4); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 4); } #[test] @@ -304,9 +304,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 4); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 4); } #[test] @@ -321,9 +321,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 3); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 3); } #[test] @@ -338,9 +338,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 5); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 5); } #[test] @@ -355,9 +355,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 4); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 4); } #[test] @@ -372,9 +372,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 3); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 3); } #[test] @@ -389,9 +389,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 6); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 6); } #[test] @@ -406,9 +406,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 3); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 3); } #[test] @@ -423,9 +423,9 @@ assert_eq!(buf, bytes); // Decoding. - let decoded = Metadata::decode(&buf).expect("should succeed"); - assert_eq!(decoded.0, test); - assert_eq!(decoded.1, 2); + let (decoded, len) = Metadata::decode(&buf); + assert_eq!(decoded, Ok(test)); + assert_eq!(len, 2); } #[test] @@ -437,15 +437,15 @@ // Not enough length for Length and Type for decoding. let buf = vec![0x03]; - let _ = Metadata::decode(&buf).expect_err("should fail"); + let _ = Metadata::decode(&buf).0.expect_err("should fail"); // Not enough length for Value field for decoding. let buf = vec![0x02, 0x01, 0x02]; - let _ = Metadata::decode(&buf).expect_err("should fail"); + let _ = Metadata::decode(&buf).0.expect_err("should fail"); // Buffer length does not match Length value for decoding. let buf = vec![0x03, 0x03, 0x61]; - let _ = Metadata::decode(&buf).expect_err("should fail"); + let _ = Metadata::decode(&buf).0.expect_err("should fail"); } #[test]
diff --git a/rust/bt-common/src/packet_encoding.rs b/rust/bt-common/src/packet_encoding.rs index 9ff05d4..49f3e63 100644 --- a/rust/bt-common/src/packet_encoding.rs +++ b/rust/bt-common/src/packet_encoding.rs
@@ -9,34 +9,43 @@ pub trait Decodable: ::core::marker::Sized { type Error; - /// Decodes into a new object with the number of bytes that were decoded, or - /// returns an error. - fn decode(buf: &[u8]) -> ::core::result::Result<(Self, usize), Self::Error>; + /// Decodes into a new object or an error, and the number of bytes that + /// the decoding consumed. Should attempt to consume the entire item from + /// the buffer in the case of an error. If the item end cannot be determined, + /// return an error and consume the entirety of the bufer (`buf.len()`) + fn decode(buf: &[u8]) -> (::core::result::Result<Self, Self::Error>, usize); - /// Tries to decode a collection of a decodable item from a buffer. - /// If any item fails to decode, fails with an Error and the previously - /// decoded items. - /// Will only decode up to max items. If None, will decode the entire - /// buffer. + /// Tries to decode a collection of this object concatenated in a buffer. + /// Returns a vector of items (or errors) and the number of bytes consumed to + /// decode them. + /// Continues to decode items until the buffer is consumed or the max items. + /// If None, will decode the entire buffer. fn decode_multiple( buf: &[u8], max: Option<usize>, - ) -> ::core::result::Result<(Vec<Self>, usize), (Vec<Self>, usize, Self::Error)> { + ) -> (Vec<::core::result::Result<Self, Self::Error>>, usize) { let mut idx = 0; let mut result = Vec::new(); while idx < buf.len() && Some(result.len()) != max { - match Self::decode(&buf[idx..]) { - Err(e) => return Err((result, idx, e)), - Ok((item, consumed)) => { - result.push(item); - idx += consumed; - } - } + let (one_result, consumed) = Self::decode(&buf[idx..]); + result.push(one_result); + idx += consumed; } - Ok((result, idx)) + (result, idx) } } +/// A decodable type that has an exact size in bytes. +pub trait FixedSizeDecodable: ::core::marker::Sized { + type Error; + const WRONG_SIZE_ERROR: Self::Error; + const BYTE_SIZE: usize; + + /// Decodes the value. This function assumes that buf is of at least + /// BYTE_SIZE, and assumes that BYTE_SIZE bytes are consumed to decode. + fn decode_checked(buf: &[u8]) -> core::result::Result<Self, Self::Error>; +} + /// An encodable type can write itself into a byte buffer. pub trait Encodable { type Error;
diff --git a/rust/bt-gatt/src/server.rs b/rust/bt-gatt/src/server.rs index 8a6f3b3..28efd8e 100644 --- a/rust/bt-gatt/src/server.rs +++ b/rust/bt-gatt/src/server.rs
@@ -13,7 +13,7 @@ pub struct ServiceId(u64); impl ServiceId { - pub fn new(id: u64) -> Self { + pub const fn new(id: u64) -> Self { Self(id) } }
diff --git a/rust/bt-gatt/src/test_utils.rs b/rust/bt-gatt/src/test_utils.rs index 873689f..46aa379 100644 --- a/rust/bt-gatt/src/test_utils.rs +++ b/rust/bt-gatt/src/test_utils.rs
@@ -3,9 +3,9 @@ // found in the LICENSE file. use bt_common::core::{Address, AddressType}; -use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures::future::{ready, Ready}; use futures::Stream; +use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded}; +use futures::future::{Ready, ready}; use parking_lot::Mutex; use std::collections::HashMap; use std::sync::Arc; @@ -18,7 +18,7 @@ use crate::periodic_advertising::{PeriodicAdvertising, SyncReport}; use crate::pii::GetPeerAddr; use crate::server::{self, LocalService, ReadResponder, ServiceDefinition, WriteResponder}; -use crate::{types::*, GattTypes, ServerTypes}; +use crate::{GattTypes, ServerTypes, types::*}; #[derive(Default)] struct FakePeerServiceInner { @@ -362,6 +362,7 @@ } } +#[derive(Debug)] pub enum FakeServerEvent { ReadResponded { service_id: server::ServiceId,
diff --git a/rust/bt-gatt/src/types.rs b/rust/bt-gatt/src/types.rs index aefbb78..c9b72d3 100644 --- a/rust/bt-gatt/src/types.rs +++ b/rust/bt-gatt/src/types.rs
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use std::collections::HashSet; - use bt_common::{PeerId, Uuid}; use thiserror::Error; @@ -264,9 +262,12 @@ impl CharacteristicProperties { pub fn is_disjoint(&self, other: &Self) -> bool { - let this_properties: HashSet<_> = HashSet::from_iter(self.0.iter()); - let other_properties: HashSet<_> = HashSet::from_iter(other.0.iter()); - this_properties.is_disjoint(&other_properties) + for property in self.0.iter() { + if other.0.contains(&property) { + return false; + } + } + true } pub fn contains(&self, property: CharacteristicProperty) -> bool {
diff --git a/rust/bt-pacs/src/lib.rs b/rust/bt-pacs/src/lib.rs index 3e34f35..412e574 100644 --- a/rust/bt-pacs/src/lib.rs +++ b/rust/bt-pacs/src/lib.rs
@@ -37,19 +37,24 @@ impl Decodable for PacRecord { type Error = bt_common::packet_encoding::Error; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { let mut idx = 0; - let (codec_id, consumed) = CodecId::decode(&buf[idx..])?; - idx += consumed; + let codec_id = match CodecId::decode(&buf[idx..]) { + (Ok(codec_id), consumed) => { + idx += consumed; + codec_id + } + (Err(e), _) => return (Err(e), buf.len()), + }; let codec_specific_capabilites_length = buf[idx] as usize; idx += 1; if idx + codec_specific_capabilites_length > buf.len() { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let (results, consumed) = CodecCapability::decode_all(&buf[idx..idx + codec_specific_capabilites_length]); if consumed != codec_specific_capabilites_length { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let codec_specific_capabilities = results.into_iter().filter_map(Result::ok).collect(); idx += consumed; @@ -57,16 +62,16 @@ let metadata_length = buf[idx] as usize; idx += 1; if idx + metadata_length > buf.len() { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let (results, consumed) = Metadata::decode_all(&buf[idx..idx + metadata_length]); if consumed != metadata_length { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let metadata = results.into_iter().filter_map(Result::ok).collect(); idx += consumed; - Ok((Self { codec_id, codec_specific_capabilities, metadata }, idx)) + (Ok(Self { codec_id, codec_specific_capabilities, metadata }), idx) } } @@ -124,8 +129,8 @@ let mut next_idx = 1; let mut capabilities = Vec::with_capacity(num_of_pac_records); for _ in 0..num_of_pac_records { - let (cap, consumed) = PacRecord::decode(&value[next_idx..])?; - capabilities.push(cap); + let (cap, consumed) = PacRecord::decode(&value[next_idx..]); + capabilities.push(cap?); next_idx += consumed; } Ok(capabilities) @@ -197,14 +202,14 @@ impl Decodable for AudioLocations { type Error = bt_common::packet_encoding::Error; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() != 4 { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len()); } let locations = AudioLocation::from_bits(u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]])) .collect(); - Ok((AudioLocations { locations }, 4)) + (Ok(AudioLocations { locations }), 4) } } @@ -249,12 +254,12 @@ value: &[u8], ) -> Result<Self, bt_common::packet_encoding::Error> { let handle = characteristic.handle; - let (locations, _) = AudioLocations::decode(value)?; + let locations = AudioLocations::decode(value).0?; Ok(Self { handle, locations }) } fn update(&mut self, new_value: &[u8]) -> Result<&mut Self, bt_common::packet_encoding::Error> { - self.locations = AudioLocations::decode(new_value)?.0; + self.locations = AudioLocations::decode(new_value).0?; Ok(self) } } @@ -282,12 +287,12 @@ value: &[u8], ) -> Result<Self, bt_common::packet_encoding::Error> { let handle = characteristic.handle; - let (locations, _) = AudioLocations::decode(value)?; + let locations = AudioLocations::decode(value).0?; Ok(Self { handle, locations }) } fn update(&mut self, new_value: &[u8]) -> Result<&mut Self, bt_common::packet_encoding::Error> { - self.locations = AudioLocations::decode(new_value)?.0; + self.locations = AudioLocations::decode(new_value).0?; Ok(self) } } @@ -301,15 +306,15 @@ impl Decodable for AvailableContexts { type Error = bt_common::packet_encoding::Error; - fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> { + fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) { if buf.len() < 2 { - return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); + return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), 2); } let encoded = u16::from_le_bytes([buf[0], buf[1]]); if encoded == 0 { - Ok((Self::NotAvailable, 2)) + (Ok(Self::NotAvailable), 2) } else { - Ok((Self::Available(ContextType::from_bits(encoded).collect()), 2)) + (Ok(Self::Available(ContextType::from_bits(encoded).collect())), 2) } } } @@ -373,8 +378,8 @@ if value.len() < 4 { return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); } - let sink = AvailableContexts::decode(&value[0..2])?.0; - let source = AvailableContexts::decode(&value[2..4])?.0; + let sink = AvailableContexts::decode(&value[0..2]).0?; + let source = AvailableContexts::decode(&value[2..4]).0?; Ok(Self { handle, sink, source }) } @@ -385,8 +390,8 @@ if new_value.len() != 4 { return Err(bt_common::packet_encoding::Error::UnexpectedDataLength); } - let sink = AvailableContexts::decode(&new_value[0..2])?.0; - let source = AvailableContexts::decode(&new_value[2..4])?.0; + let sink = AvailableContexts::decode(&new_value[0..2]).0?; + let source = AvailableContexts::decode(&new_value[2..4]).0?; self.sink = sink; self.source = source; Ok(self)