use anyhow::{Context as _, Error};
use fidl_fuchsia_testing_sl4f::{
FacadeIteratorMarker, FacadeIteratorSynchronousProxy, FacadeProviderMarker, FacadeProviderProxy,
};
use fuchsia_component::client::connect_to_protocol;
use fuchsia_sync::RwLock;
use maplit::{convert_args, hashmap};
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tracing::{error, info, warn};
use crate::bluetooth::avrcp_facade::AvrcpFacade;
use crate::server::sl4f_types::{
AsyncCommandRequest, AsyncRequest, ClientData, CommandRequest, CommandResponse, Facade,
MethodId, RequestId,
};
use crate::audio::commands::AudioFacade;
use crate::modular::facade::ModularFacade;
use crate::bluetooth::a2dp_facade::A2dpFacade;
use crate::bluetooth::avdtp_facade::AvdtpFacade;
use crate::bluetooth::ble_advertise_facade::BleAdvertiseFacade;
use crate::bluetooth::bt_sys_facade::BluetoothSysFacade;
use crate::bluetooth::gatt_client_facade::GattClientFacade;
use crate::bluetooth::gatt_server_facade::GattServerFacade;
use test_call_manager::TestCallManager as HfpFacade;
use test_rfcomm_client::RfcommManager as RfcommFacade;
use crate::bluetooth::profile_server_facade::ProfileServerFacade;
use crate::common_utils::common::{read_json_from_vmo, write_json_to_vmo};
use crate::common_utils::error::Sl4fError;
use crate::component::facade::ComponentFacade;
use crate::device::facade::DeviceFacade;
use crate::diagnostics::facade::DiagnosticsFacade;
use crate::factory_reset::facade::FactoryResetFacade;
use crate::factory_store::facade::FactoryStoreFacade;
use crate::feedback_data_provider::facade::FeedbackDataProviderFacade;
use crate::file::facade::FileFacade;
use crate::hardware_power_statecontrol::facade::HardwarePowerStatecontrolFacade;
use crate::hwinfo::facade::HwinfoFacade;
use crate::input::facade::InputFacade;
use crate::location::emergency_provider_facade::EmergencyProviderFacade;
use crate::location::regulatory_region_facade::RegulatoryRegionFacade;
use crate::logging::facade::LoggingFacade;
use crate::media_session::facade::MediaSessionFacade;
use crate::netstack::facade::NetstackFacade;
use crate::paver::facade::PaverFacade;
use crate::power::facade::PowerFacade;
use crate::proxy::facade::ProxyFacade;
use crate::scenic::facade::ScenicFacade;
use crate::setui::facade::SetUiFacade;
use crate::system_metrics::facade::SystemMetricsFacade;
use crate::temperature::facade::TemperatureFacade;
use crate::time::facade::TimeFacade;
use crate::traceutil::facade::TraceutilFacade;
use crate::tracing::facade::TracingFacade;
use crate::virtual_camera::facade::VirtualCameraFacade;
use crate::weave::facade::WeaveFacade;
use crate::webdriver::facade::WebdriverFacade;
use crate::wlan::facade::WlanFacade;
use crate::wlan_deprecated::facade::WlanDeprecatedConfigurationFacade;
use crate::wlan_phy::facade::WlanPhyFacade;
use crate::wlan_policy::ap_facade::WlanApPolicyFacade;
use crate::wlan_policy::facade::WlanPolicyFacade;
use crate::wpan::facade::WpanFacade;
#[derive(Debug)]
pub struct Sl4f {
facades: HashMap<String, Arc<dyn Facade>>,
facade_provider: FacadeProviderProxy,
proxied_facades: HashSet<String>,
clients: Arc<RwLock<Sl4fClients>>,
}
impl Sl4f {
pub fn new(clients: Arc<RwLock<Sl4fClients>>) -> Result<Sl4f, Error> {
fn to_arc_trait_object<'a, T: Facade + 'a>(facade: T) -> Arc<dyn Facade + 'a> {
Arc::new(facade) as Arc<dyn Facade>
}
let facades = convert_args!(
keys = String::from,
values = to_arc_trait_object,
hashmap!(
"a2dp_facade" => A2dpFacade::new(),
"audio_facade" => AudioFacade::new()?,
"avdtp_facade" => AvdtpFacade::new(),
"avrcp_facade" => AvrcpFacade::new(),
"basemgr_facade" => ModularFacade::new(),
"modular_facade" => ModularFacade::new(),
"ble_advertise_facade" => BleAdvertiseFacade::new(),
"bt_sys_facade" => BluetoothSysFacade::new(),
"component_facade" => ComponentFacade::new(),
"diagnostics_facade" => DiagnosticsFacade::new(),
"device_facade" => DeviceFacade::new(),
"factory_reset_facade" => FactoryResetFacade::new(),
"factory_store_facade" => FactoryStoreFacade::new(),
"feedback_data_provider_facade" => FeedbackDataProviderFacade::new(),
"file_facade" => FileFacade::new(),
"gatt_client_facade" => GattClientFacade::new(),
"gatt_server_facade" => GattServerFacade::new(),
"hardware_power_statecontrol_facade" => HardwarePowerStatecontrolFacade::new(),
"hfp_facade" => HfpFacade::new(),
"hwinfo_facade" => HwinfoFacade::new(),
"input_facade" => InputFacade::new(),
"location_emergency_provider_facade" => EmergencyProviderFacade::new()?,
"location_regulatory_region_facade" => RegulatoryRegionFacade::new()?,
"logging_facade" => LoggingFacade::new(),
"media_session_facade" => MediaSessionFacade::new(),
"netstack_facade" => NetstackFacade::default(),
"rfcomm_facade" => RfcommFacade::new()?,
"paver" => PaverFacade::new(),
"power_facade" => PowerFacade::new(),
"profile_server_facade" => ProfileServerFacade::new(),
"proxy_facade" => ProxyFacade::new(),
"scenic_facade" => ScenicFacade::new(),
"setui_facade" => SetUiFacade::new(),
"system_metrics_facade" => SystemMetricsFacade::new(),
"temperature_facade" => TemperatureFacade::new(),
"time_facade" => TimeFacade::new(),
"traceutil_facade" => TraceutilFacade::new(),
"tracing_facade" => TracingFacade::new(),
"virtual_camera_facade" => VirtualCameraFacade::new(),
"weave_facade" => WeaveFacade::new(),
"webdriver_facade" => WebdriverFacade::new(),
"wlan" => WlanFacade::new()?,
"wlan_ap_policy" => WlanApPolicyFacade::new()?,
"wlan_deprecated" => WlanDeprecatedConfigurationFacade::new()?,
"wlan_phy" => WlanPhyFacade::new()?,
"wlan_policy" => WlanPolicyFacade::new()?,
"wpan_facade" => WpanFacade::new(),
)
);
let mut proxied_facades = HashSet::<String>::new();
let facade_provider = match connect_to_protocol::<FacadeProviderMarker>() {
Ok(proxy) => proxy,
Err(error) => {
error!(%error, "Failed to connect to FacadeProvider");
return Err(error.into());
}
};
let (client_end, server_end) = fidl::endpoints::create_endpoints::<FacadeIteratorMarker>();
match facade_provider.get_facades(server_end) {
Ok(_) => {
let facade_iter = FacadeIteratorSynchronousProxy::new(client_end.into_channel());
loop {
match facade_iter.get_next(zx::MonotonicInstant::INFINITE) {
Ok(facades) if facades.is_empty() => break, Ok(facades) => proxied_facades.extend(facades.into_iter()),
Err(error) if error.is_closed() && proxied_facades.is_empty() => {
break;
}
Err(error) => {
error!(%error, "Failed to get proxied facade list");
proxied_facades.clear();
break;
}
};
}
}
Err(error) if error.is_closed() => (),
Err(error) => {
error!(%error, "Failed to get FacadeIterator");
return Err(error.into());
}
};
Ok(Sl4f { facades, facade_provider, proxied_facades, clients })
}
pub fn get_facade(&self, name: &str) -> Option<Arc<dyn Facade>> {
self.facades.get(name).map(Arc::clone)
}
pub async fn cleanup(&self) {
for facade in self.facades.values() {
facade.cleanup();
}
if !self.proxied_facades.is_empty() {
if let Err(error) = self.facade_provider.cleanup().await {
error!(%error, "Failed to execute Cleanup()");
}
}
self.clients.write().cleanup_clients();
}
pub fn print_clients(&self) {
self.clients.read().print_clients();
}
pub async fn print(&self) {
for facade in self.facades.values() {
facade.print();
}
if !self.proxied_facades.is_empty() {
if let Err(error) = self.facade_provider.print().await {
error!(%error, "Failed to execute Print()");
}
}
}
pub fn has_proxy_facade(&self, name: &str) -> bool {
self.proxied_facades.contains(name)
}
pub async fn handle_proxy_request(
&self,
facade: String,
command: String,
args: Value,
) -> Result<Value, Error> {
let encode_params = async {
let params_blob = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0)?;
write_json_to_vmo(¶ms_blob, &args)?;
Ok::<zx::Vmo, Error>(params_blob)
};
let params_blob = match encode_params.await {
Ok(params_blob) => params_blob,
Err(error) => {
return Err(
Sl4fError::new(&format!("Failed to write params with: {}", error)).into()
);
}
};
match self.facade_provider.execute(&facade, &command, params_blob).await {
Ok((None, None)) => Ok(Value::Null),
Ok((Some(vmo), None)) => match read_json_from_vmo(&vmo) {
Ok(result) => Ok(result),
Err(error) => {
Err(Sl4fError::new(&format!("Failed to read result with: {}", error)).into())
}
},
Ok((_, Some(string))) => Err(Sl4fError::new(&string).into()),
Err(error) => {
Err(Sl4fError::new(&format!("Failed to send command with {}", error)).into())
}
}
}
}
#[derive(Debug)]
pub struct Sl4fClients {
clients: HashMap<String, Vec<ClientData>>,
}
impl Sl4fClients {
pub fn new() -> Self {
Self { clients: HashMap::new() }
}
fn init_client(&mut self, id: String) -> bool {
use std::collections::hash_map::Entry::*;
match self.clients.entry(id) {
Occupied(entry) => {
warn!(tag = "client_init", "Key: {:?} already exists in clients. ", entry.key());
true
}
Vacant(entry) => {
entry.insert(Vec::new());
info!(tag = "client_init", "Updated clients: {:?}", self.clients);
false
}
}
}
fn cleanup_clients(&mut self) {
self.clients.clear();
}
fn print_clients(&self) {
info!("SL4F Clients: {:?}", self.clients);
}
}
fn json<T>(content: &T) -> hyper::Response<hyper::Body>
where
T: serde::Serialize,
{
use std::convert::TryInto as _;
let application_json = "application/json".try_into().expect("json header value");
let data = serde_json::to_string(content).expect("encode json");
let mut response = hyper::Response::new(data.into());
assert_eq!(response.headers_mut().insert(hyper::header::CONTENT_TYPE, application_json), None);
response
}
pub async fn serve(
request: hyper::Request<hyper::Body>,
clients: Arc<RwLock<Sl4fClients>>,
sender: async_channel::Sender<AsyncRequest>,
) -> hyper::Response<hyper::Body> {
use hyper::Method;
match (request.method(), request.uri().path()) {
(&Method::GET, "/") => {
info!(tag = "serve", "Received command request via GET.");
client_request(request, &sender).await
}
(&Method::POST, "/") => {
info!(tag = "serve", "Received command request via POST.");
client_request(request, &sender).await
}
(&Method::GET, "/init") => {
info!(tag = "serve", "Received init request.");
client_init(request, &clients).await
}
(&Method::GET, "/print_clients") => {
info!(tag = "serve", "Received print client request.");
const PRINT_ACK: &str = "Successfully printed clients.";
json(&PRINT_ACK)
}
(&Method::GET, "/cleanup") => {
info!(tag = "serve", "Received server cleanup request.");
server_cleanup(request, &sender).await
}
_ => {
error!(tag = "serve", "Received unknown server request.");
const FAIL_REQUEST_ACK: &str = "Unknown GET request.";
let res = CommandResponse::new(json!(""), None, Some(FAIL_REQUEST_ACK.to_string()));
json(&res)
}
}
}
async fn client_request(
request: hyper::Request<hyper::Body>,
sender: &async_channel::Sender<AsyncRequest>,
) -> hyper::Response<hyper::Body> {
const FAIL_TEST_ACK: &str = "Command failed";
let (request_id, method_id, method_params) = match parse_request(request).await {
Ok(res) => res,
Err(error) => {
error!(tag = "client_request", ?error, "Failed to parse request");
return json(&FAIL_TEST_ACK);
}
};
let (async_sender, receiver) = futures::channel::oneshot::channel();
let req = AsyncCommandRequest::new(async_sender, method_id.clone(), method_params);
sender.send(AsyncRequest::Command(req)).await.expect("Failed to send request to async thread.");
let resp = receiver.await.expect("Async thread dropped responder.");
info!(
tag = "client_request",
method = ?method_id.method,
response = ?resp,
"Received async thread response"
);
match resp.result {
Some(async_res) => {
let res = CommandResponse::new(request_id.into_response_id(), Some(async_res), None);
json(&res)
}
None => {
let res = CommandResponse::new(request_id.into_response_id(), None, resp.error);
json(&res)
}
}
}
async fn client_init(
request: hyper::Request<hyper::Body>,
clients: &Arc<RwLock<Sl4fClients>>,
) -> hyper::Response<hyper::Body> {
const INIT_ACK: &str = "Recieved init request.";
const FAIL_INIT_ACK: &str = "Failed to init client.";
let (_, _, method_params) = match parse_request(request).await {
Ok(res) => res,
Err(_) => return json(&FAIL_INIT_ACK),
};
let client_id_raw = match method_params.get("client_id") {
Some(id) => Some(id).unwrap().clone(),
None => return json(&FAIL_INIT_ACK),
};
let client_id = client_id_raw.as_str().map(String::from).unwrap();
if clients.write().init_client(client_id) {
json(&FAIL_INIT_ACK)
} else {
json(&INIT_ACK)
}
}
async fn parse_request(
request: hyper::Request<hyper::Body>,
) -> Result<(RequestId, MethodId, Value), Error> {
use bytes::Buf as _;
let body = hyper::body::aggregate(request.into_body()).await.context("read request")?;
let request_data: CommandRequest = match serde_json::from_reader(body.reader()) {
Ok(tdata) => tdata,
Err(_) => return Err(Sl4fError::new("Failed to unpack request data.").into()),
};
let request_id_raw = request_data.id;
let method_id_raw = request_data.method;
let method_params = request_data.params;
info!(tag = "parse_request",
request_id = ?request_id_raw,
name = ?method_id_raw,
args = ?method_params
);
let request_id = RequestId::new(request_id_raw);
let method_id = method_id_raw.parse().unwrap_or_default();
Ok((request_id, method_id, method_params))
}
async fn server_cleanup(
request: hyper::Request<hyper::Body>,
sender: &async_channel::Sender<AsyncRequest>,
) -> hyper::Response<hyper::Body> {
const FAIL_CLEANUP_ACK: &str = "Failed to cleanup SL4F resources.";
const CLEANUP_ACK: &str = "Successful cleanup of SL4F resources.";
info!(tag = "server_cleanup", "Cleaning up server state");
let (request_id, _, _) = match parse_request(request).await {
Ok(res) => res,
Err(_) => return json(&FAIL_CLEANUP_ACK),
};
let (async_sender, receiver) = futures::channel::oneshot::channel();
sender
.send(AsyncRequest::Cleanup(async_sender))
.await
.expect("Failed to send request to async thread.");
let () = receiver.await.expect("Async thread dropped responder.");
let ack = CommandResponse::new(request_id.into_response_id(), Some(json!(CLEANUP_ACK)), None);
json(&ack)
}