blob: d81ba6a68a59632b20bc292b7b081b87638e1a33 [file] [log] [blame]
// Copyright 2019 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 {
packet_encoding::{pub_decodable_enum, Decodable, Encodable},
std::convert::TryFrom,
};
use crate::packets::{
AvcCommandType, Error, PacketResult, PduId, VendorCommand, VendorDependentPdu,
};
pub_decodable_enum!(
/// AVRCP 1.6.1 section 6.4.1 Table 6.5
GetCapabilitiesCapabilityId <u8, Error, InvalidParameter> {
CompanyId => 0x02,
EventsId => 0x03,
}
);
#[derive(Debug)]
/// AVRCP 1.6.1 section 6.4 Capabilities PDUs - GetCapabilities
pub struct GetCapabilitiesCommand {
capability_id: GetCapabilitiesCapabilityId,
}
impl GetCapabilitiesCommand {
pub fn new(capability_id: GetCapabilitiesCapabilityId) -> GetCapabilitiesCommand {
GetCapabilitiesCommand { capability_id }
}
pub fn capability_id(&self) -> GetCapabilitiesCapabilityId {
self.capability_id
}
}
/// Packet PDU ID for vendor dependent packet encoding.
impl VendorDependentPdu for GetCapabilitiesCommand {
fn pdu_id(&self) -> PduId {
PduId::GetCapabilities
}
}
/// Specifies the AVC command type for this AVC command packet
impl VendorCommand for GetCapabilitiesCommand {
fn command_type(&self) -> AvcCommandType {
AvcCommandType::Status
}
}
impl Decodable for GetCapabilitiesCommand {
type Error = Error;
fn decode(buf: &[u8]) -> PacketResult<Self> {
if buf.len() < 1 {
return Err(Error::InvalidMessageLength);
}
let capability_id = GetCapabilitiesCapabilityId::try_from(buf[0])?;
Ok(Self { capability_id })
}
}
impl Encodable for GetCapabilitiesCommand {
type Error = Error;
fn encoded_len(&self) -> usize {
1
}
fn encode(&self, buf: &mut [u8]) -> PacketResult<()> {
if buf.len() < 1 {
return Err(Error::BufferLengthOutOfRange);
}
buf[0] = u8::from(&self.capability_id);
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
pub enum Capability {
CompanyId(u8, u8, u8),
EventId(u8),
}
#[derive(Debug)]
/// AVRCP 1.6.1 section 6.4 Capabilities PDUs - GetCapabilities
pub struct GetCapabilitiesResponse {
capability_id: GetCapabilitiesCapabilityId,
capabilities: Vec<Capability>,
}
const BT_SIG_COMPANY_ID: Capability = Capability::CompanyId(0x00, 0x19, 0x58);
impl GetCapabilitiesResponse {
// Generic BT SIG company ID response
pub fn new_btsig_company() -> GetCapabilitiesResponse {
GetCapabilitiesResponse {
capability_id: GetCapabilitiesCapabilityId::CompanyId,
capabilities: vec![BT_SIG_COMPANY_ID], // BT_SIG generic company ID
}
}
pub fn new_events(event_ids: &[u8]) -> GetCapabilitiesResponse {
let capabilities = event_ids.into_iter().map(|id| Capability::EventId(*id)).collect();
GetCapabilitiesResponse {
capability_id: GetCapabilitiesCapabilityId::EventsId,
capabilities,
}
}
#[allow(dead_code)]
pub fn capability_id(&self) -> GetCapabilitiesCapabilityId {
self.capability_id
}
#[allow(dead_code)]
pub fn has_bt_sig_company(&self) -> bool {
assert_eq!(self.capability_id, GetCapabilitiesCapabilityId::CompanyId);
self.capabilities.contains(&BT_SIG_COMPANY_ID)
}
pub fn event_ids(&self) -> Vec<u8> {
self.capabilities
.iter()
.map(|f| match f {
Capability::EventId(id) => *id,
_ => panic!("Capabilities contains non events"),
})
.collect()
}
}
/// Packet PDU ID for vendor dependent packet encoding.
impl VendorDependentPdu for GetCapabilitiesResponse {
fn pdu_id(&self) -> PduId {
PduId::GetCapabilities
}
}
impl Decodable for GetCapabilitiesResponse {
type Error = Error;
fn decode(buf: &[u8]) -> PacketResult<Self> {
if buf.len() < 2 {
return Err(Error::InvalidMessageLength);
}
let capability_id = GetCapabilitiesCapabilityId::try_from(buf[0])?;
let capability_count = buf[1] as usize;
if capability_count > 0 && buf.len() == 2 {
return Err(Error::InvalidMessageLength);
}
let capabilities = match capability_id {
GetCapabilitiesCapabilityId::CompanyId => {
let mut company_ids = vec![];
let mut chunks = buf[2..].chunks_exact(3);
while let Some(chunk) = chunks.next() {
company_ids.push(Capability::CompanyId(chunk[0], chunk[1], chunk[2]));
}
if chunks.remainder().len() > 0 {
return Err(Error::InvalidMessage);
}
company_ids
}
GetCapabilitiesCapabilityId::EventsId => {
let mut event_ids = vec![];
for chunk in &buf[2..] {
event_ids.push(Capability::EventId(*chunk))
}
event_ids
}
};
if capabilities.len() != capability_count {
return Err(Error::InvalidMessage);
}
Ok(Self { capability_id, capabilities })
}
}
impl Encodable for GetCapabilitiesResponse {
type Error = Error;
fn encoded_len(&self) -> usize {
const PREFIX_LENGTH: usize = 2; // capability_id + count
match self.capability_id {
GetCapabilitiesCapabilityId::CompanyId => PREFIX_LENGTH + (self.capabilities.len() * 3),
GetCapabilitiesCapabilityId::EventsId => PREFIX_LENGTH + (self.capabilities.len() * 1),
}
}
fn encode(&self, buf: &mut [u8]) -> PacketResult<()> {
if buf.len() < self.encoded_len() {
return Err(Error::BufferLengthOutOfRange);
}
buf[0] = u8::from(&self.capability_id);
buf[1] =
u8::try_from(self.capabilities.len()).map_err(|_| Error::ParameterEncodingError)?;
match self.capability_id {
GetCapabilitiesCapabilityId::CompanyId => {
let mut i = 2;
for capability in self.capabilities.iter() {
if let Capability::CompanyId(b1, b2, b3) = capability {
buf[i] = *b1;
buf[i + 1] = *b2;
buf[i + 2] = *b3;
i += 3;
} else {
return Err(Error::ParameterEncodingError);
}
}
}
GetCapabilitiesCapabilityId::EventsId => {
let mut i = 2;
for capability in self.capabilities.iter() {
if let Capability::EventId(b1) = capability {
buf[i] = *b1;
i += 1;
} else {
return Err(Error::InvalidParameter);
}
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packets::{NotificationEventId, VendorDependentRawPdu};
#[test]
/// Test GetCapabilitiesResponse company encoding
fn test_get_capabilities_response_company_encode() {
let b = GetCapabilitiesResponse::new_btsig_company();
assert!(b.has_bt_sig_company());
assert_eq!(b.raw_pdu_id(), u8::from(&PduId::GetCapabilities));
assert_eq!(b.capability_id(), GetCapabilitiesCapabilityId::CompanyId);
assert_eq!(b.encoded_len(), 5); // 1 company + len + capability id
let mut buf = vec![0; b.encoded_len()];
assert!(b.encode(&mut buf[..]).is_ok());
assert_eq!(
buf,
&[
0x02, // company_id
0x01, // len
0x00, 0x19, 0x58 // BT_SIG Company ID
]
);
}
#[test]
/// Test GetCapabilitiesResponse company decoding
fn test_get_capabilities_response_company_decode() {
assert!(GetCapabilitiesResponse::decode(&[
0x02, // company_id
0x02, // len
0x00, 0x19, 0x58, // BT_SIG Company ID
0xff, 0xff, 0xff, // random
])
.unwrap()
.has_bt_sig_company());
}
#[test]
/// Test GetCapabilitiesResponse bad company decoding
fn test_get_capabilities_response_company_decode_bad() {
assert!(GetCapabilitiesResponse::decode(&[
0x02, // company_id
0x02, // len
0x00, 0x19,
])
.is_err());
}
#[test]
/// Test GetCapabilitiesResponse decode error too short
fn test_get_capabilities_response_decode_bad_short() {
assert!(GetCapabilitiesResponse::decode(&[]).is_err());
}
#[test]
/// Test GetCapabilitiesResponse event encoding (all events)
fn test_get_capabilities_response_event_encode_all() {
let b = GetCapabilitiesResponse::new_events(NotificationEventId::VALUES);
assert_eq!(b.raw_pdu_id(), u8::from(&PduId::GetCapabilities));
assert_eq!(b.capability_id(), GetCapabilitiesCapabilityId::EventsId);
assert_eq!(b.encoded_len(), 2 + NotificationEventId::VALUES.len()); // all the events + len + capability id
let mut buf = vec![0; b.encoded_len()];
assert!(b.encode(&mut buf[..]).is_ok());
}
#[test]
/// Test GetCapabilitiesResponse event encoding (two events)
fn test_get_capabilities_response_event_encode_two() {
let b = GetCapabilitiesResponse::new_events(&[0x01, 0x02]);
let mut buf = vec![0; b.encoded_len()];
assert!(b.encode(&mut buf[..]).is_ok());
assert_eq!(
buf,
&[
0x03, // event id
0x02, // len
0x01, 0x02, // two events
]
);
}
#[test]
/// Test GetCapabilitiesResponse parsing of events
fn test_get_capabilities_response_event_decode() {
let resp = GetCapabilitiesResponse::decode(&[
0x03, // event id
0x02, // len
0x01, 0x02, // two events
]);
assert!(resp.is_ok(), "unable to decode {:?}", resp.err());
assert_eq!(resp.unwrap().event_ids(), &[0x01, 0x02]);
}
#[test]
/// Test GetCapabilitiesResponse bad decode
fn test_get_capabilities_response_decode_bad() {
assert!(GetCapabilitiesResponse::decode(&[
0x01, // bad id
0xff, // bad len
0x01, 0x02, // two params
])
.is_err());
}
#[test]
/// Test GetCapabilitiesCommand decode error too short
fn test_get_capabilities_command_decode_bad_short() {
assert!(GetCapabilitiesCommand::decode(&[]).is_err());
}
#[test]
/// Test GetCapabilitiesCommand company encoding
fn test_get_capabilities_command_company_encode() {
let b = GetCapabilitiesCommand::new(GetCapabilitiesCapabilityId::CompanyId);
assert_eq!(b.raw_pdu_id(), u8::from(&PduId::GetCapabilities));
assert_eq!(b.command_type(), AvcCommandType::Status);
assert_eq!(b.capability_id(), GetCapabilitiesCapabilityId::CompanyId);
assert_eq!(b.encoded_len(), 1); // capability id
let mut buf = vec![0; b.encoded_len()];
assert!(b.encode(&mut buf[..]).is_ok());
assert_eq!(
buf,
&[
0x02, // company id
]
);
}
#[test]
/// Test GetCapabilitiesCommand event encoding
fn test_get_capabilities_command_event_encode() {
let b = GetCapabilitiesCommand::new(GetCapabilitiesCapabilityId::EventsId);
assert_eq!(b.raw_pdu_id(), u8::from(&PduId::GetCapabilities));
assert_eq!(b.command_type(), AvcCommandType::Status);
assert_eq!(b.capability_id(), GetCapabilitiesCapabilityId::EventsId);
assert_eq!(b.encoded_len(), 1); // capability id
let mut buf = vec![0; b.encoded_len()];
assert!(b.encode(&mut buf[..]).is_ok());
assert_eq!(
buf,
&[
0x03, // event id
]
);
}
#[test]
/// Test GetCapabilitiesResponse parsing of events
fn test_get_capabilities_command_event_decode() {
let result = GetCapabilitiesCommand::decode(&[
0x03, // event id
]);
assert!(result.is_ok());
assert_eq!(result.unwrap().capability_id(), GetCapabilitiesCapabilityId::EventsId);
}
}