blob: f4be86d5e065114412a5c3b69553c1c3b949066b [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};
use bt_rfcomm::profile::{is_rfcomm_protocol, server_channel_from_protocol};
use bt_rfcomm::ServerChannel;
use fuchsia_bluetooth::profile::{DataElement, Psm, ServiceDefinition};
use std::collections::HashSet;
/// Updates `service` with `server_channel` if the service requests 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"));
}
for desc in service.protocol_descriptor_list.iter_mut() {
if desc.protocol == fidl_fuchsia_bluetooth_bredr::ProtocolIdentifier::Rfcomm {
desc.params = vec![DataElement::Uint8(server_channel.into())];
break;
}
}
Ok(())
}
/// Returns true if the provided `service` requests 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 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(|def| server_channel_from_protocol(&def.protocol_descriptor_list))
.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 assert_matches::assert_matches;
use fidl_fuchsia_bluetooth_bredr as bredr;
use fuchsia_bluetooth::profile::{Attribute, ProtocolDescriptor};
#[test]
fn update_empty_service_definition_is_error() {
let server_channel = ServerChannel::try_from(10).unwrap();
let mut def = ServiceDefinition::default();
// Empty definition doesn't request RFCOMM - shouldn't be updated.
let result = update_svc_def_with_server_channel(&mut def, server_channel);
assert_matches!(result, Err(_));
let expected = ServiceDefinition::default();
assert_eq!(def, expected);
}
#[test]
fn update_non_rfcomm_service_definition_is_error() {
let server_channel = ServerChannel::try_from(8).unwrap();
let mut def = ServiceDefinition {
protocol_descriptor_list: vec![ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::L2Cap,
params: vec![],
}],
..ServiceDefinition::default()
};
let expected = def.clone();
// Only L2CAP definition cannot be updated with RFCOMM.
let result = update_svc_def_with_server_channel(&mut def, server_channel);
assert_matches!(result, Err(_));
// The original `def` should be unchanged.
assert_eq!(def, expected);
}
#[test]
fn update_service_definition_with_rfcomm() {
let server_channel = ServerChannel::try_from(10).unwrap();
let mut def = ServiceDefinition {
protocol_descriptor_list: vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: vec![] },
],
..ServiceDefinition::default()
};
// Normal case - definition is requesting RFCOMM. It should be updated with the
// server channel.
let result = update_svc_def_with_server_channel(&mut def, server_channel);
assert_matches!(result, Ok(_));
let expected = ServiceDefinition {
protocol_descriptor_list: vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::Rfcomm,
params: vec![DataElement::Uint8(10)],
},
],
..ServiceDefinition::default()
};
assert_eq!(def, expected);
}
#[test]
fn update_obex_service_definition_with_rfcomm() {
let server_channel = ServerChannel::try_from(12).unwrap();
let mut def = ServiceDefinition {
protocol_descriptor_list: vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Obex, params: vec![] },
],
..ServiceDefinition::default()
};
// Definition is requesting RFCOMM, but OBEX is the "highest" protocol level, not RFCOMM.
let result = update_svc_def_with_server_channel(&mut def, server_channel);
assert_matches!(result, Ok(_));
// We expect the RFCOMM descriptor to be updated and the OBEX descriptor should still be
// preserved.
let expected = ServiceDefinition {
protocol_descriptor_list: vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::Rfcomm,
params: vec![DataElement::Uint8(12)],
},
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Obex, params: vec![] },
],
..ServiceDefinition::default()
};
assert_eq!(def, expected);
}
#[test]
fn psm_from_service_definitions() {
// Service 1 is only L2CAP.
let def1 = ServiceDefinition {
protocol_descriptor_list: vec![ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::L2Cap,
params: vec![DataElement::Uint16(21)],
}],
additional_protocol_descriptor_lists: vec![
vec![ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::L2Cap,
params: vec![DataElement::Uint16(23)],
}],
vec![ProtocolDescriptor {
protocol: bredr::ProtocolIdentifier::Avdtp,
params: vec![DataElement::Uint16(0x0103)],
}],
],
..ServiceDefinition::default()
};
// Service 2 is RFCOMM + L2CAP (OBEX).
let def2 = ServiceDefinition {
protocol_descriptor_list: vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Obex, params: vec![] },
],
additional_attributes: vec![Attribute {
id: 0x0200,
element: DataElement::Uint16(2000),
}],
..ServiceDefinition::default()
};
let psms = psms_from_service_definitions(&vec![def1, def2]);
// Expect to contain all of the PSMs that are specified in the record. Unallocated PSMs
// (e.g. RFCOMM) aren't included.
let expected_psms = HashSet::from([Psm::new(21), Psm::new(23), Psm::new(2000)]);
assert_eq!(psms, expected_psms);
}
}