blob: b571889458e1a506aae3af55f65ed16a350f698c [file] [log] [blame]
// Copyright 2020 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 {
anyhow::{format_err, Error},
bt_rfcomm::{
profile::{build_rfcomm_protocol, is_rfcomm_protocol},
ServerChannel,
},
fidl_fuchsia_bluetooth_bredr as bredr,
fuchsia_bluetooth::profile::{DataElement, Psm, ServiceDefinition},
std::{collections::HashSet, convert::TryFrom},
};
/// Updates the provided `service` with the assigned `server_channel` if
/// the service is requesting RFCOMM.
/// Updates the primary protocol descriptor only. SDP records for profiles
/// usually have the RFCOMM descriptor in the primary protocol.
///
/// Returns Ok() if the `service` was updated.
pub fn update_svc_def_with_server_channel(
service: &mut ServiceDefinition,
server_channel: ServerChannel,
) -> Result<(), Error> {
// If the service definition is not requesting RFCOMM, there is no need to update
// with the server channel.
if !is_rfcomm_service_definition(&service) {
return Err(format_err!("Non-RFCOMM service definition provided"));
}
service.protocol_descriptor_list = build_rfcomm_protocol(server_channel);
Ok(())
}
/// Returns true if the provided `service` is requesting RFCOMM.
pub fn is_rfcomm_service_definition(service: &ServiceDefinition) -> bool {
is_rfcomm_protocol(&service.protocol_descriptor_list)
}
/// Returns true if any of the `services` request RFCOMM.
pub fn service_definitions_request_rfcomm(services: &Vec<ServiceDefinition>) -> bool {
services.iter().map(is_rfcomm_service_definition).fold(false, |acc, is_rfcomm| acc || is_rfcomm)
}
/// Returns the Server Channel from the provided `service` or None if the service
/// is not RFCOMM or is invalidly formatted.
pub fn server_channel_from_service_definition(
service: &ServiceDefinition,
) -> Option<ServerChannel> {
for descriptor in &service.protocol_descriptor_list {
if descriptor.protocol == bredr::ProtocolIdentifier::Rfcomm {
// If the Protocol is RFCOMM, there should be one element with the Server Channel.
if descriptor.params.len() != 1 {
return None;
}
if let DataElement::Uint8(sc) = descriptor.params[0] {
return ServerChannel::try_from(sc).ok();
}
return None;
}
}
None
}
/// Returns the server channels specified in `services`. It's possible that
/// none of the `services` request a ServerChannel in which case the returned set
/// will be empty.
pub fn server_channels_from_service_definitions(
services: &Vec<ServiceDefinition>,
) -> HashSet<ServerChannel> {
services.iter().filter_map(server_channel_from_service_definition).collect()
}
/// Returns a set of PSMs specified by a list of `services`.
pub fn psms_from_service_definitions(services: &Vec<ServiceDefinition>) -> HashSet<Psm> {
services.iter().fold(HashSet::new(), |mut psms, service| {
psms.extend(&service.psm_set());
psms
})
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_bluetooth::profile::ProtocolDescriptor;
use crate::types::tests::rfcomm_protocol_descriptor_list;
#[test]
fn test_is_rfcomm_service_definition() {
let mut def = ServiceDefinition::default();
let empty_res = is_rfcomm_service_definition(&def);
assert!(!empty_res);
// Updated definition with L2CAP.
def.protocol_descriptor_list =
vec![ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] }];
let l2cap_res = is_rfcomm_service_definition(&def);
assert!(!l2cap_res);
// Updated definition with L2CAP and RFCOMM.
def.protocol_descriptor_list = rfcomm_protocol_descriptor_list(None);
let rfcomm_res = is_rfcomm_service_definition(&def);
assert!(rfcomm_res);
}
#[test]
fn test_update_service_definition_with_rfcomm() {
let server_channel = ServerChannel::try_from(10).unwrap();
let mut def = ServiceDefinition::default();
let mut expected = def.clone();
// Empty definition doesn't request RFCOMM - shouldn't be updated.
let updated = update_svc_def_with_server_channel(&mut def, server_channel);
assert!(updated.is_err());
assert_eq!(expected, def);
// Normal case - definition is requesting RFCOMM. It should be updated with the
// server channel.
def.protocol_descriptor_list = vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: vec![] },
];
expected.protocol_descriptor_list = rfcomm_protocol_descriptor_list(Some(server_channel));
let updated = update_svc_def_with_server_channel(&mut def, server_channel);
assert!(updated.is_ok());
assert_eq!(expected, def);
}
#[test]
fn test_server_channel_from_empty_service_definition_is_none() {
assert_eq!(None, server_channel_from_service_definition(&ServiceDefinition::default()));
}
#[test]
fn test_server_channel_from_l2cap_is_none() {
let mut def = ServiceDefinition::default();
// Just L2CAP - should be no server channel.
def.protocol_descriptor_list = vec![ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::L2Cap,
params: vec![DataElement::Uint16(bredr::PSM_AVDTP)],
}];
assert_eq!(None, server_channel_from_service_definition(&def));
}
#[test]
fn test_server_channel_from_empty_rfcomm_is_none() {
let mut def = ServiceDefinition::default();
// RFCOMM but un-allocated Server Channel.
def.protocol_descriptor_list = vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: vec![] },
];
assert_eq!(None, server_channel_from_service_definition(&def));
}
#[test]
fn test_server_channel_from_invalid_rfcomm_is_none() {
let mut def = ServiceDefinition::default();
// RFCOMM but invalidly formatted.
def.protocol_descriptor_list = vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::Rfcomm,
params: vec![DataElement::Uint16(100)],
},
];
assert_eq!(None, server_channel_from_service_definition(&def));
}
#[test]
fn test_server_channel_from_rfcomm_is_present() {
let mut def = ServiceDefinition::default();
// RFCOMM service with assigned server channel.
let sc = 10;
def.protocol_descriptor_list = vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::Rfcomm,
params: vec![DataElement::Uint8(sc)],
},
];
let expected = ServerChannel::try_from(sc).ok();
assert_eq!(server_channel_from_service_definition(&def), expected);
}
}