blob: acc76d2fdb22f77cb1c2a4a6b2158cd9a9c29a10 [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::Error,
bt_rfcomm::ServerChannel,
fidl_fuchsia_bluetooth::PeerId,
fidl_fuchsia_bluetooth_bredr as bredr,
fuchsia_bluetooth::profile::{
combine_channel_parameters, ChannelParameters, Psm, ServiceDefinition,
},
slab::Slab,
std::collections::HashSet,
};
use crate::profile::{psms_from_service_definitions, server_channels_from_service_definitions};
/// Every group of registered services will be assigned a ServiceGroupHandle to track
/// relevant information about the advertisement. There can be multiple `ServiceGroupHandle`s
/// per profile client. A unique handle is assigned per Profile.Advertise() call.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ServiceGroupHandle(usize);
/// A collection of `ServiceGroups` which are indexed by a unique `ServiceGroupHandle`.
pub struct Services(Slab<ServiceGroup>);
impl Services {
pub fn new() -> Self {
Self(Slab::new())
}
pub fn contains(&self, handle: ServiceGroupHandle) -> bool {
self.0.contains(handle.0)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get_mut(&mut self, handle: ServiceGroupHandle) -> Option<&mut ServiceGroup> {
self.0.get_mut(handle.0)
}
pub fn remove(&mut self, handle: ServiceGroupHandle) -> ServiceGroup {
self.0.remove(handle.0)
}
pub fn insert(&mut self, service: ServiceGroup) -> ServiceGroupHandle {
ServiceGroupHandle(self.0.insert(service))
}
pub fn iter(&self) -> impl Iterator<Item = (ServiceGroupHandle, &ServiceGroup)> {
self.0.iter().map(|(id, data)| (ServiceGroupHandle(id), data))
}
/// Returns currently registered PSMs.
pub fn psms(&self) -> HashSet<Psm> {
self.iter().map(|(_, data)| data.allocated_psms()).fold(
HashSet::new(),
|mut psms, current| {
psms.extend(current);
psms
},
)
}
/// Attempts to build a set of AdvertiseParams from the services in the group.
/// Returns None if there are no services to be advertised.
pub fn build_registration(&self) -> Option<AdvertiseParams> {
if self.is_empty() {
return None;
}
let mut services = Vec::new();
let mut parameters = ChannelParameters::default();
for (_, data) in self.iter() {
services.extend(data.service_defs().clone());
parameters = combine_channel_parameters(&parameters, data.channel_parameters());
}
Some(AdvertiseParams { services, parameters })
}
}
/// Parameters needed to advertise a service.
/// This type is used to reduce verbosity of passing around service advertisements.
#[derive(Clone, Debug, PartialEq)]
pub struct AdvertiseParams {
pub services: Vec<ServiceDefinition>,
pub parameters: ChannelParameters,
}
/// Relevant information associated with a group of registered services.
#[derive(Debug)]
pub struct ServiceGroup {
/// Client associated with this group.
receiver: bredr::ConnectionReceiverProxy,
/// The ChannelParameters for this group.
channel_parameters: ChannelParameters,
/// The client's Responder for this group. When the services are
/// unregistered with the `ProfileRegistrar`, the hanging-get responder
/// will be notified.
responder: Option<bredr::ProfileAdvertiseResponder>,
/// The services definitions for this group.
service_defs: Vec<ServiceDefinition>,
/// The allocated PSMs for this group.
allocated_psms: HashSet<Psm>,
/// The allocated server channels for this group.
allocated_server_channels: HashSet<ServerChannel>,
}
impl ServiceGroup {
pub fn new(
receiver: bredr::ConnectionReceiverProxy,
channel_parameters: ChannelParameters,
) -> Self {
Self {
receiver,
channel_parameters,
responder: None,
service_defs: vec![],
allocated_psms: HashSet::new(),
allocated_server_channels: HashSet::new(),
}
}
pub fn service_defs(&self) -> &Vec<ServiceDefinition> {
&self.service_defs
}
pub fn channel_parameters(&self) -> &ChannelParameters {
&self.channel_parameters
}
pub fn allocated_psms(&self) -> &HashSet<Psm> {
&self.allocated_psms
}
/// Returns the Server Channels that were allocated to this group of services.
pub fn allocated_server_channels(&self) -> &HashSet<ServerChannel> {
&self.allocated_server_channels
}
/// Returns true if the `psm` is requested by this service group.s
pub fn contains_psm(&self, psm: Psm) -> bool {
self.allocated_psms.contains(&psm)
}
/// Relays the connection parameters to the client.
pub fn relay_connected(
&self,
mut peer_id: PeerId,
channel: bredr::Channel,
mut protocol: Vec<bredr::ProtocolDescriptor>,
) -> Result<(), Error> {
self.receiver
.connected(&mut peer_id, channel, &mut protocol.iter_mut())
.map_err(|e| e.into())
}
pub fn set_responder(&mut self, responder: bredr::ProfileAdvertiseResponder) {
self.responder = Some(responder);
}
/// Sets the ServiceDefinitions for this group.
pub fn set_service_defs(&mut self, defs: Vec<ServiceDefinition>) {
self.allocated_psms = psms_from_service_definitions(&defs);
self.allocated_server_channels = server_channels_from_service_definitions(&defs);
self.service_defs = defs;
}
}
impl Drop for ServiceGroup {
fn drop(&mut self) {
if let Some(responder) = self.responder.take() {
let _ = responder.send(&mut Ok(()));
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use {
fidl::{encoding::Decodable, endpoints::create_proxy_and_stream},
fidl_fuchsia_bluetooth_bredr::{
Channel, ProfileDescriptor, ProtocolIdentifier, ServiceClassProfileIdentifier,
},
fuchsia_async as fasync,
fuchsia_bluetooth::{
profile::{DataElement, ProtocolDescriptor},
types::{PeerId, Uuid},
},
futures::{stream::StreamExt, task::Poll},
matches::assert_matches,
std::convert::TryFrom,
};
/// Defines a Protocol requesting RFCOMM with the provided server `channel`.
pub fn rfcomm_protocol_descriptor_list(
channel: Option<ServerChannel>,
) -> Vec<ProtocolDescriptor> {
let params = channel.map(|c| vec![DataElement::Uint8(c.into())]).unwrap_or(vec![]);
vec![
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::L2Cap, params: vec![] },
ProtocolDescriptor { protocol: bredr::ProtocolIdentifier::Rfcomm, params: params },
]
}
/// Defines the SPP Service Definition, which requests RFCOMM.
/// An optional `channel` can be provided to specify the Server Channel.
pub fn rfcomm_service_definition(channel: Option<ServerChannel>) -> ServiceDefinition {
ServiceDefinition {
service_class_uuids: vec![Uuid::new16(0x1101).into()], // SPP UUID
protocol_descriptor_list: rfcomm_protocol_descriptor_list(channel),
additional_protocol_descriptor_lists: vec![],
profile_descriptors: vec![ProfileDescriptor {
profile_id: ServiceClassProfileIdentifier::SerialPort,
major_version: 1,
minor_version: 2,
}],
information: vec![],
additional_attributes: vec![],
}
}
/// Defines a sample ServiceDefinition with the provided `psm`.
pub fn other_service_definition(psm: Psm) -> ServiceDefinition {
ServiceDefinition {
service_class_uuids: vec![Uuid::new16(0x110A).into()], // A2DP
protocol_descriptor_list: vec![
ProtocolDescriptor {
protocol: ProtocolIdentifier::L2Cap,
params: vec![DataElement::Uint16(psm.into())],
},
ProtocolDescriptor {
protocol: ProtocolIdentifier::Avdtp,
params: vec![DataElement::Uint16(0x0103)], // Indicate v1.3
},
],
additional_protocol_descriptor_lists: vec![],
profile_descriptors: vec![ProfileDescriptor {
profile_id: ServiceClassProfileIdentifier::AdvancedAudioDistribution,
major_version: 1,
minor_version: 2,
}],
information: vec![],
additional_attributes: vec![],
}
}
fn build_service_group() -> (ServiceGroup, bredr::ConnectionReceiverRequestStream) {
let (client, server) =
create_proxy_and_stream::<bredr::ConnectionReceiverMarker>().unwrap();
let params = Default::default();
(ServiceGroup::new(client, params), server)
}
#[test]
fn test_services_collection() {
let _exec = fasync::Executor::new().unwrap();
let mut services = Services::new();
let mut expected_psms = HashSet::new();
let mut expected_defs = vec![];
let mut expected_adv_params =
AdvertiseParams { services: vec![], parameters: Default::default() };
// Empty collection of Services has no associated PSMs and shouldn't build
// into any registration data.
assert_eq!(services.psms(), expected_psms);
assert_eq!(services.build_registration(), None);
// Insert a new group.
let (mut group1, _server1) = build_service_group();
let defs1 = vec![rfcomm_service_definition(None)];
group1.set_service_defs(defs1.clone());
let _handle1 = services.insert(group1);
expected_defs.extend(defs1.clone());
expected_adv_params.services = expected_defs.clone();
assert_eq!(services.build_registration(), Some(expected_adv_params.clone()));
// Build a new ServiceGroup with RFCOMM and non-RFCOMM services and custom
// SecurityRequirements/ChannelParameters.
let (mut group2, _server2) = build_service_group();
let psm = Psm::new(6);
let sc2 = ServerChannel::try_from(1).ok();
let defs2 = vec![other_service_definition(psm), rfcomm_service_definition(sc2)];
group2.set_service_defs(defs2.clone());
let new_chan_params = ChannelParameters {
channel_mode: Some(bredr::ChannelMode::Basic),
max_rx_sdu_size: None,
security_requirements: None,
};
group2.channel_parameters = new_chan_params.clone();
let handle2 = services.insert(group2);
// We expect the advertisement parameters to include the stricter security
// requirements, channel parameters, and both handle1 and handle2 ServiceDefinitions.
expected_psms.insert(psm);
expected_defs.extend(defs2);
expected_adv_params.services = expected_defs.clone();
expected_adv_params.parameters = new_chan_params;
assert_eq!(services.psms(), expected_psms);
assert_eq!(services.build_registration(), Some(expected_adv_params.clone()));
// Removing group2 should result in the new registration parameters to only
// include group1's parameters.
let _ = services.remove(handle2);
expected_adv_params.services = defs1;
expected_adv_params.parameters = ChannelParameters::default();
assert_eq!(services.build_registration(), Some(expected_adv_params));
}
#[test]
fn test_service_group() {
let _exec = fasync::Executor::new().unwrap();
let (mut service_group, _server) = build_service_group();
let mut expected_server_channels = HashSet::new();
let mut expected_psms = HashSet::new();
assert_eq!(service_group.service_defs(), &vec![]);
assert_eq!(service_group.allocated_server_channels(), &expected_server_channels);
assert_eq!(service_group.allocated_psms(), &expected_psms);
let psm = Psm::new(20);
let other_def = other_service_definition(psm);
service_group.set_service_defs(vec![other_def.clone()]);
expected_psms.insert(psm);
assert_eq!(service_group.allocated_server_channels(), &expected_server_channels);
assert_eq!(service_group.allocated_psms(), &expected_psms);
let sc = ServerChannel::try_from(10).unwrap();
let rfcomm_def = rfcomm_service_definition(Some(sc));
service_group.set_service_defs(vec![rfcomm_def, other_def]);
expected_server_channels.insert(sc);
assert_eq!(service_group.allocated_server_channels(), &expected_server_channels);
assert_eq!(service_group.allocated_psms(), &expected_psms);
}
#[test]
fn test_service_group_relay_connected() {
let mut exec = fasync::Executor::new().unwrap();
let (mut service_group, mut server) = build_service_group();
let sc = ServerChannel::try_from(1).ok();
let defs = vec![other_service_definition(Psm::new(6)), rfcomm_service_definition(sc)];
service_group.set_service_defs(defs);
let id = PeerId(123);
let channel = Channel::new_empty();
let protocol = vec![];
let res = service_group.relay_connected(id.into(), channel, protocol);
assert_matches!(res, Ok(()));
// We expect the connected request to be relayed to the client.
match exec.run_until_stalled(&mut server.next()) {
Poll::Ready(Some(Ok(bredr::ConnectionReceiverRequest::Connected {
peer_id, ..
}))) => {
assert_eq!(peer_id, id.into());
}
x => panic!("Expected ready but got: {:?}", x),
}
}
}