use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use anyhow::Error;
use fidl::endpoints::create_request_stream;
use fidl_fuchsia_bluetooth::{ChannelMode, ChannelParameters};
use fidl_fuchsia_bluetooth_bredr::{
Attribute, Channel, ConnectParameters, ConnectionReceiverRequest,
ConnectionReceiverRequestStream, DataElement, Information, L2capParameters,
ProfileAdvertiseRequest, ProfileDescriptor, ProfileMarker, ProfileProxy, ProfileSearchRequest,
ProtocolDescriptor, ProtocolIdentifier, SearchResultsRequest, SearchResultsRequestStream,
ServiceClassProfileIdentifier, ServiceDefinition,
};
use fuchsia_bluetooth::types::{PeerId, Uuid};
use fuchsia_sync::RwLock;
use futures::channel::oneshot;
use futures::stream::StreamExt;
use futures::{select, FutureExt};
use serde_json::value::Value;
use std::collections::HashMap;
use tracing::*;
use {fuchsia_async as fasync, fuchsia_component as component};
#[derive(Debug)]
struct ProfileServerFacadeInner {
profile_server_proxy: Option<ProfileProxy>,
advertisement_count: usize,
advertisement_stoppers: HashMap<usize, oneshot::Sender<()>>,
l2cap_channel_holder: Option<Channel>,
}
#[derive(Debug)]
pub struct ProfileServerFacade {
inner: RwLock<ProfileServerFacadeInner>,
}
impl ProfileServerFacade {
pub fn new() -> ProfileServerFacade {
ProfileServerFacade {
inner: RwLock::new(ProfileServerFacadeInner {
profile_server_proxy: None,
advertisement_count: 0,
advertisement_stoppers: HashMap::new(),
l2cap_channel_holder: None,
}),
}
}
pub fn create_profile_server_proxy(&self) -> Result<ProfileProxy, Error> {
let tag = "ProfileServerFacade::create_profile_server_proxy";
match self.inner.read().profile_server_proxy.clone() {
Some(profile_server_proxy) => {
info!(
tag = &with_line!(tag),
"Current profile server proxy: {:?}", profile_server_proxy
);
Ok(profile_server_proxy)
}
None => {
info!(tag = &with_line!(tag), "Setting new profile server proxy");
let profile_server_proxy =
component::client::connect_to_protocol::<ProfileMarker>();
if let Err(err) = profile_server_proxy {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to create profile server proxy: {}", err)
);
}
profile_server_proxy
}
}
}
pub async fn init_profile_server_proxy(&self) -> Result<(), Error> {
self.inner.write().profile_server_proxy = Some(self.create_profile_server_proxy()?);
Ok(())
}
pub fn generate_service_class_uuids(&self, uuid_list: &Vec<Value>) -> Result<Vec<Uuid>, Error> {
let tag = "ProfileServerFacade::generate_service_class_uuids";
let mut service_class_uuid_list = Vec::new();
for raw_uuid in uuid_list {
let uuid = if let Some(u) = raw_uuid.as_str() {
u
} else {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Unable to convert Value to String.")
)
};
let uuid: Uuid = match uuid.parse() {
Ok(uuid) => uuid,
Err(e) => {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Unable to convert to Uuid: {:?}", e)
);
}
};
service_class_uuid_list.push(uuid);
}
Ok(service_class_uuid_list)
}
pub fn generate_protocol_descriptors(
&self,
protocol_descriptors: &Vec<Value>,
) -> Result<Vec<ProtocolDescriptor>, Error> {
let tag = "ProfileServerFacade::generate_protocol_descriptors";
let mut protocol_descriptor_list = Vec::new();
for raw_protocol_descriptor in protocol_descriptors {
let protocol = match raw_protocol_descriptor["protocol"].as_u64() {
Some(p) => match p as u16 {
1 => ProtocolIdentifier::Sdp,
3 => ProtocolIdentifier::Rfcomm,
7 => ProtocolIdentifier::Att,
8 => ProtocolIdentifier::Obex,
15 => ProtocolIdentifier::Bnep,
17 => ProtocolIdentifier::Hidp,
18 => ProtocolIdentifier::HardcopyControlChannel,
20 => ProtocolIdentifier::HardcopyDataChannel,
22 => ProtocolIdentifier::HardcopyNotification,
23 => ProtocolIdentifier::Avctp,
25 => ProtocolIdentifier::Avdtp,
30 => ProtocolIdentifier::McapControlChannel,
31 => ProtocolIdentifier::McapDataChannel,
256 => ProtocolIdentifier::L2Cap,
_ => fx_err_and_bail!(
&with_line!(tag),
format!("Input protocol does not match supported protocols: {}", p)
),
},
None => fx_err_and_bail!(&with_line!(tag), "Value 'protocol' not found."),
};
let raw_params = if let Some(p) = raw_protocol_descriptor["params"].as_array() {
p
} else {
fx_err_and_bail!(&with_line!(tag), "Value 'params' not found or invalid type.")
};
let mut params = Vec::new();
for param in raw_params {
let data = if let Some(d) = param["data"].as_u64() {
d as u16
} else {
fx_err_and_bail!(&with_line!(tag), "Value 'data' not found or invalid type.")
};
params.push(DataElement::Uint16(data as u16));
}
protocol_descriptor_list.push(ProtocolDescriptor {
protocol: Some(protocol),
params: Some(params),
..Default::default()
});
}
Ok(protocol_descriptor_list)
}
pub fn generate_profile_descriptors(
&self,
profile_descriptors: &Vec<Value>,
) -> Result<Vec<ProfileDescriptor>, Error> {
let tag = "ProfileServerFacade::generate_profile_descriptors";
let mut profile_descriptor_list = Vec::new();
for raw_profile_descriptor in profile_descriptors.into_iter() {
let profile_id = if let Some(r) = raw_profile_descriptor.get("profile_id") {
match self.get_service_class_profile_identifier_from_id(r) {
Ok(id) => id,
Err(e) => fx_err_and_bail!(&with_line!(tag), e),
}
} else {
let log_err = "Invalid SDP search input. Missing 'profile_id'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let minor_version = if let Some(num) = raw_profile_descriptor["minor_version"].as_u64()
{
num as u8
} else {
let log_err = "Type of 'minor_version' incorrect or incorrect type.";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let major_version = if let Some(num) = raw_profile_descriptor["major_version"].as_u64()
{
num as u8
} else {
let log_err = "Type of 'major_version' incorrect or incorrect type.";
fx_err_and_bail!(&with_line!(tag), log_err)
};
profile_descriptor_list.push(ProfileDescriptor {
profile_id: Some(profile_id),
minor_version: Some(minor_version),
major_version: Some(major_version),
..Default::default()
});
}
Ok(profile_descriptor_list)
}
pub fn generate_information(
&self,
information_list: &Vec<Value>,
) -> Result<Vec<Information>, Error> {
let tag = "ProfileServerFacade::generate_information";
let mut info_list = Vec::new();
for raw_information in information_list {
let language = if let Some(v) = raw_information["language"].as_str() {
Some(v.to_string())
} else {
let log_err = "Type of 'language' incorrect of invalid type.";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let name = if let Some(v) = raw_information["name"].as_str() {
Some(v.to_string())
} else {
None
};
let description = if let Some(v) = raw_information["description"].as_str() {
Some(v.to_string())
} else {
None
};
let provider = if let Some(v) = raw_information["provider"].as_str() {
Some(v.to_string())
} else {
None
};
info_list.push(Information {
language,
name,
description,
provider,
..Default::default()
});
}
Ok(info_list)
}
pub fn generate_additional_attributes(
&self,
additional_attributes_list: &Vec<Value>,
) -> Result<Vec<Attribute>, Error> {
let tag = "ProfileServerFacade::generate_additional_attributes";
let mut attribute_list = Vec::new();
for raw_attribute in additional_attributes_list {
let id = if let Some(v) = raw_attribute["id"].as_u64() {
v as u16
} else {
let log_err = "Type of 'id' incorrect or invalid type.";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let raw_element = if let Some(e) = raw_attribute.get("element") {
e
} else {
let log_err = "Type of 'element' incorrect.";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let data_element = if let Some(d) = raw_element["data"].as_u64() {
DataElement::Uint8(d as u8)
} else {
fx_err_and_bail!(&with_line!(tag), "Value 'data' not found.")
};
attribute_list.push(Attribute {
id: Some(id),
element: Some(data_element),
..Default::default()
})
}
Ok(attribute_list)
}
pub async fn monitor_connection_receiver(
mut requests: ConnectionReceiverRequestStream,
end_signal: oneshot::Receiver<()>,
) -> Result<(), Error> {
let tag = "ProfileServerFacade::monitor_connection_receiver";
let mut fused_end_signal = end_signal.fuse();
loop {
select! {
_ = fused_end_signal => {
info!("Ending advertisement on signal..");
return Ok(());
},
request = requests.next() => {
let request = match request {
None => {
let log_err = format_err!("Connection request stream ended");
fx_err_and_bail!(&with_line!(tag), log_err)
}
Some(Err(e)) => {
let log_err = format_err!("Error during connection request: {}", e);
fx_err_and_bail!(&with_line!(tag), log_err)
},
Some(Ok(r)) => r,
};
let ConnectionReceiverRequest::Connected { peer_id, channel, .. } = request else {
fx_err_and_bail!(&with_line!(tag), "unknown method")
};
let peer_id: PeerId = peer_id.into();
info!(
tag = &with_line!(tag),
"Connection from {}: {:?}!",
peer_id,
channel
);
}
}
}
}
pub async fn monitor_search_results(
mut requests: SearchResultsRequestStream,
) -> Result<(), Error> {
let tag = "ProfileServerFacade::monitor_search_results";
while let Some(request) = requests.next().await {
let request = match request {
Err(e) => {
let log_err = format_err!("Error during search results request: {}", e);
fx_err_and_bail!(&with_line!(tag), log_err)
}
Ok(r) => r,
};
let SearchResultsRequest::ServiceFound { peer_id, protocol, attributes, responder } =
request
else {
fx_err_and_bail!(&with_line!(tag), "unknown method")
};
let peer_id: PeerId = peer_id.into();
info!(
tag = &with_line!(tag),
"Search Result: Peer {} with protocol {:?}: {:?}", peer_id, protocol, attributes
);
responder.send()?;
}
let log_err = format_err!("Search result request stream ended");
fx_err_and_bail!(&with_line!(tag), log_err)
}
pub async fn add_service(&self, args: Value) -> Result<usize, Error> {
let tag = "ProfileServerFacade::write_sdp_record";
info!(tag = &with_line!(tag), "Writing SDP record");
let record_description = if let Some(r) = args.get("record") {
r
} else {
let log_err = "Invalid SDP record input. Missing 'record'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let service_class_uuids = if let Some(v) = record_description.get("service_class_uuids") {
if let Some(r) = v.as_array() {
self.generate_service_class_uuids(r)?
} else {
let log_err = "Invalid type for service_class_uuids in record input.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP record input. Missing 'service_class_uuids'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let protocol_descriptors = if let Some(v) = record_description.get("protocol_descriptors") {
if let Some(r) = v.as_array() {
self.generate_protocol_descriptors(r)?
} else {
let log_err = "Invalid type for protocol_descriptors in record input.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP record input. Missing 'protocol_descriptors'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let profile_descriptors = if let Some(v) = record_description.get("profile_descriptors") {
if let Some(r) = v.as_array() {
self.generate_profile_descriptors(r)?
} else {
let log_err = "Invalid type for profile_descriptors in record input.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP record input. Missing 'profile_descriptors'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let raw_additional_protocol_descriptors =
if let Some(v) = record_description.get("additional_protocol_descriptors") {
if let Some(arr) = v.as_array() {
Some(self.generate_protocol_descriptors(arr)?)
} else if v.is_null() {
None
} else {
let log_err =
"Invalid type for 'additional_protocol_descriptors'. Expected null or array.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP record input. Missing 'additional_protocol_descriptors'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let information = if let Some(v) = record_description.get("information") {
if let Some(r) = v.as_array() {
self.generate_information(r)?
} else {
let log_err = "Invalid type for information in record input.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP record input. Missing 'information'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let additional_attributes = if let Some(v) = record_description.get("additional_attributes")
{
if let Some(r) = v.as_array() {
Some(self.generate_additional_attributes(r)?)
} else {
None
}
} else {
let log_err = "Invalid SDP record input. Missing 'additional_attributes'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let service_defs = vec![ServiceDefinition {
service_class_uuids: Some(service_class_uuids.into_iter().map(Into::into).collect()),
protocol_descriptor_list: Some(protocol_descriptors),
profile_descriptors: Some(profile_descriptors),
additional_protocol_descriptor_lists: match raw_additional_protocol_descriptors {
Some(d) => Some(vec![d]),
None => None,
},
information: Some(information),
additional_attributes,
..Default::default()
}];
let (connect_client, connect_requests) = create_request_stream();
match &self.inner.read().profile_server_proxy {
Some(server) => {
let _ = server.advertise(ProfileAdvertiseRequest {
services: Some(service_defs),
receiver: Some(connect_client),
..Default::default()
});
}
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
let (end_ad_sender, end_ad_receiver) = oneshot::channel::<()>();
let request_handler_fut =
Self::monitor_connection_receiver(connect_requests, end_ad_receiver);
fasync::Task::spawn(async move {
if let Err(err) = request_handler_fut.await {
error!(?err, "Connection receiver handler ended with error");
}
})
.detach();
let next = self.inner.write().advertisement_count + 1;
self.inner.write().advertisement_stoppers.insert(next, end_ad_sender);
self.inner.write().advertisement_count = next;
Ok(next)
}
pub async fn remove_service(&self, service_id: usize) -> Result<(), Error> {
let tag = "ProfileServerFacade::remove_service";
match self.inner.write().advertisement_stoppers.remove(&service_id) {
Some(_) => Ok(()),
None => fx_err_and_bail!(&with_line!(tag), "Service ID not found"),
}
}
pub fn get_service_class_profile_identifier_from_id(
&self,
raw_profile_id: &Value,
) -> Result<ServiceClassProfileIdentifier, Error> {
let tag = "ProfileServerFacade::get_service_class_profile_identifier_from_id";
match raw_profile_id.as_u64().map(u16::try_from) {
Some(Ok(id)) => match ServiceClassProfileIdentifier::from_primitive(id) {
Some(id) => return Ok(id),
None => {
let log_err = format!("UUID {} not supported by profile server.", id);
fx_err_and_bail!(&with_line!(tag), log_err)
}
},
_ => fx_err_and_bail!(&with_line!(tag), "Type of raw_profile_id incorrect."),
};
}
pub async fn add_search(&self, args: Value) -> Result<(), Error> {
let tag = "ProfileServerFacade::add_search";
info!(tag = &with_line!(tag), "Adding Search");
let raw_attribute_list = if let Some(v) = args.get("attribute_list") {
if let Some(r) = v.as_array() {
r
} else {
let log_err = "Expected 'attribute_list' as an array.";
fx_err_and_bail!(&with_line!(tag), log_err)
}
} else {
let log_err = "Invalid SDP search input. Missing 'attribute_list'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let mut attribute_list = Vec::new();
for item in raw_attribute_list {
match item.as_u64() {
Some(v) => attribute_list.push(v as u16),
None => fx_err_and_bail!(
&with_line!(tag),
"Failed to convert value in attribute_list to u16."
),
};
}
let profile_id = if let Some(r) = args.get("profile_id") {
self.get_service_class_profile_identifier_from_id(r)?
} else {
let log_err = "Invalid SDP search input. Missing 'profile_id'";
fx_err_and_bail!(&with_line!(tag), log_err)
};
let (search_client, result_requests) = create_request_stream();
match &self.inner.read().profile_server_proxy {
Some(server) => server.search(ProfileSearchRequest {
service_uuid: Some(profile_id),
attr_ids: Some(attribute_list),
results: Some(search_client),
..Default::default()
})?,
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
let search_fut = Self::monitor_search_results(result_requests);
fasync::Task::spawn(async move {
if let Err(err) = search_fut.await {
error!(?err, "Search result handler ended with error");
}
})
.detach();
Ok(())
}
pub async fn connect(&self, id: String, psm: u16, mode: &str) -> Result<(), Error> {
let tag = "ProfileServerFacade::connect";
let peer_id: PeerId = match id.parse() {
Ok(id) => id,
Err(_) => {
fx_err_and_bail!(
&with_line!(tag),
"Failed to convert value in attribute_list to u16."
);
}
};
let mode = match mode {
"BASIC" => ChannelMode::Basic,
"ERTM" => ChannelMode::EnhancedRetransmission,
_ => fx_err_and_bail!(&with_line!(tag), format!("Invalid mode: {:?}.", mode)),
};
let connection_result = match &self.inner.read().profile_server_proxy {
Some(server) => {
let l2cap_params = L2capParameters {
psm: Some(psm),
parameters: Some(ChannelParameters {
channel_mode: Some(mode),
..Default::default()
}),
..Default::default()
};
server.connect(&peer_id.into(), &ConnectParameters::L2cap(l2cap_params)).await?
}
None => fx_err_and_bail!(&with_line!(tag), "No Server Proxy created."),
};
match connection_result {
Ok(r) => self.inner.write().l2cap_channel_holder = Some(r),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format!("Failed to connect with error: {:?}", e))
}
};
Ok(())
}
pub async fn cleanup(&self) -> Result<(), Error> {
self.inner.write().advertisement_stoppers.clear();
self.inner.write().advertisement_count = 0;
self.inner.write().l2cap_channel_holder = None;
self.inner.write().profile_server_proxy = None;
Ok(())
}
}