use crate::common_utils::common::macros::with_line;
use crate::wlan_policy::types::{ClientStateSummary, NetworkConfig};
use anyhow::{format_err, Error};
use fidl::endpoints::Proxy as _;
use fidl_fuchsia_wlan_policy as fidl_policy;
use fuchsia_async::{self as fasync, DurationExt as _};
use fuchsia_component::client::connect_to_protocol;
use fuchsia_sync::RwLock;
use futures::TryStreamExt;
use std::cell::Cell;
use std::collections::HashSet;
use std::fmt::{self, Debug};
use tracing::*;
pub struct WlanPolicyFacade {
controller: RwLock<InnerController>,
update_listener: Cell<Option<fidl_policy::ClientStateUpdatesRequestStream>>,
}
#[derive(Debug)]
pub struct InnerController {
inner: Option<fidl_policy::ClientControllerProxy>,
}
impl Debug for WlanPolicyFacade {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let listener = self.update_listener.take();
let update_listener =
if listener.is_some() { "Some(ClientStateUpdatesRequestStream)" } else { "None" }
.to_string();
self.update_listener.set(listener);
f.debug_struct("InnerWlanPolicyFacade")
.field("controller", &self.controller)
.field("update_listener", &update_listener)
.finish()
}
}
impl WlanPolicyFacade {
pub fn new() -> Result<WlanPolicyFacade, Error> {
Ok(Self {
controller: RwLock::new(InnerController { inner: None }),
update_listener: Cell::new(None),
})
}
pub async fn create_client_controller(&self) -> Result<(), Error> {
let tag = "WlanPolicyFacade::create_client_controller";
let mut controller_guard = self.controller.write();
controller_guard.inner = None;
let (controller, update_stream) = Self::init_client_controller().await.map_err(|e| {
info!(tag = &with_line!(tag), "Error getting client controller: {}", e);
format_err!("Error getting client controller: {}", e)
})?;
controller_guard.inner = Some(controller);
self.update_listener.set(Some(update_stream));
Ok(())
}
async fn init_client_controller() -> Result<
(fidl_policy::ClientControllerProxy, fidl_policy::ClientStateUpdatesRequestStream),
Error,
> {
let provider = connect_to_protocol::<fidl_policy::ClientProviderMarker>()?;
let (controller, req) =
fidl::endpoints::create_proxy::<fidl_policy::ClientControllerMarker>();
let (update_sink, update_stream) =
fidl::endpoints::create_request_stream::<fidl_policy::ClientStateUpdatesMarker>();
provider.get_controller(req, update_sink)?;
let sleep_duration = zx::MonotonicDuration::from_millis(10);
fasync::Timer::new(sleep_duration.after_now()).await;
if controller.is_closed() {
return Err(format_err!(
"Policy layer closed channel, client controller is likely already in use."
));
}
Ok((controller, update_stream))
}
pub fn drop_client_controller(&self) {
let mut controller_guard = self.controller.write();
controller_guard.inner = None;
}
fn init_listener() -> Result<fidl_policy::ClientStateUpdatesRequestStream, Error> {
let listener = connect_to_protocol::<fidl_policy::ClientListenerMarker>()?;
let (client_end, server_end) =
fidl::endpoints::create_endpoints::<fidl_policy::ClientStateUpdatesMarker>();
listener.get_listener(client_end)?;
Ok(server_end.into_stream())
}
pub fn set_new_listener(&self) -> Result<(), Error> {
self.update_listener.set(Some(Self::init_listener()?));
Ok(())
}
pub async fn scan_for_networks(&self) -> Result<Vec<String>, Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let (iter, server) =
fidl::endpoints::create_proxy::<fidl_policy::ScanResultIteratorMarker>();
controller.scan_for_networks(server)?;
let mut scan_results = HashSet::new();
loop {
let results = iter.get_next().await?.map_err(|e| format_err!("{:?}", e))?;
if results.is_empty() {
break;
}
let results = Self::stringify_scan_results(results);
scan_results.extend(results);
}
Ok(scan_results.into_iter().collect())
}
pub async fn connect(
&self,
target_ssid: Vec<u8>,
type_: fidl_policy::SecurityType,
) -> Result<String, Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let network_id = fidl_policy::NetworkIdentifier { ssid: target_ssid, type_ };
let response = controller
.connect(&network_id)
.await
.map_err(|e| format_err!("Connect: failed to connect: {}", e))?;
Ok(Self::request_status_as_string(response))
}
fn request_status_as_string(response: fidl_policy::RequestStatus) -> String {
match response {
fidl_policy::RequestStatus::Acknowledged => "Acknowledged",
fidl_policy::RequestStatus::RejectedNotSupported => "RejectedNotSupported",
fidl_policy::RequestStatus::RejectedIncompatibleMode => "RejectedIncompatibleMode",
fidl_policy::RequestStatus::RejectedAlreadyInUse => "RejectedAlreadyInUse",
fidl_policy::RequestStatus::RejectedDuplicateRequest => "RejectedDuplicateRequest",
}
.to_string()
}
pub async fn remove_network(
&self,
target_ssid: Vec<u8>,
type_: fidl_policy::SecurityType,
credential: fidl_policy::Credential,
) -> Result<(), Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
info!(
tag = &with_line!("WlanPolicyFacade::remove_network"),
"Removing network: ({}{:?})",
String::from_utf8_lossy(&target_ssid),
type_
);
let config = fidl_policy::NetworkConfig {
id: Some(fidl_policy::NetworkIdentifier { ssid: target_ssid, type_ }),
credential: Some(credential),
..Default::default()
};
controller
.remove_network(&config)
.await
.map_err(|err| format_err!("{:?}", err))? .map_err(|err| format_err!("{:?}", err)) }
pub async fn remove_all_networks(&self) -> Result<(), Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let saved_networks = self.get_saved_networks().await?;
for network_config in saved_networks {
controller
.remove_network(&network_config)
.await
.map_err(|err| format_err!("{:?}", err))? .map_err(|err| format_err!("{:?}", err))?; }
Ok(())
}
pub async fn start_client_connections(&self) -> Result<(), Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let req_status = controller.start_client_connections().await?;
if fidl_policy::RequestStatus::Acknowledged == req_status {
Ok(())
} else {
bail!("{:?}", req_status);
}
}
pub async fn get_update(&self) -> Result<ClientStateSummary, Error> {
let listener = self.update_listener.take();
let mut update_listener = if listener.is_none() {
Self::init_listener()
} else {
listener.ok_or_else(|| format_err!("failed to set update listener of facade"))
}?;
if let Some(update_request) = update_listener.try_next().await? {
let update = update_request.into_on_client_state_update();
let (update, responder) = match update {
Some((update, responder)) => (update, responder),
None => return Err(format_err!("Client provider produced invalid update.")),
};
responder.send().map_err(|e| format_err!("failed to ack update: {}", e))?;
self.update_listener.set(Some(update_listener));
Ok(update.into())
} else {
self.update_listener.set(Some(update_listener));
Err(format_err!("update listener's next update is None"))
}
}
pub async fn stop_client_connections(&self) -> Result<(), Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let req_status = controller.stop_client_connections().await?;
if fidl_policy::RequestStatus::Acknowledged == req_status {
Ok(())
} else {
bail!("{:?}", req_status);
}
}
pub async fn save_network(
&self,
target_ssid: Vec<u8>,
type_: fidl_policy::SecurityType,
credential: fidl_policy::Credential,
) -> Result<(), Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let network_id = fidl_policy::NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ };
controller
.save_network(&fidl_policy::NetworkConfig {
id: Some(network_id),
credential: Some(credential),
..Default::default()
})
.await?
.map_err(|e| format_err!("{:?}", e))
}
pub async fn get_saved_networks_json(&self) -> Result<Vec<NetworkConfig>, Error> {
let saved_networks = self.get_saved_networks().await?;
Ok(saved_networks.into_iter().map(|cfg| cfg.into()).collect::<Vec<_>>())
}
async fn get_saved_networks(&self) -> Result<Vec<fidl_policy::NetworkConfig>, Error> {
let controller_guard = self.controller.read();
let controller = controller_guard
.inner
.as_ref()
.ok_or_else(|| format_err!("client controller has not been initialized"))?;
let (iter, server) =
fidl::endpoints::create_proxy::<fidl_policy::NetworkConfigIteratorMarker>();
controller
.get_saved_networks(server)
.map_err(|e| format_err!("Get saved networks: fidl error {:?}", e))?;
let mut networks = vec![];
loop {
let cfgs = iter.get_next().await?;
if cfgs.is_empty() {
break;
}
networks.extend(cfgs);
}
Ok(networks)
}
fn stringify_scan_results(results: Vec<fidl_policy::ScanResult>) -> Vec<String> {
results
.into_iter()
.filter_map(|result| result.id)
.map(|id| String::from_utf8_lossy(&id.ssid).into_owned())
.collect()
}
}