blob: cbf3110fe80498a2a076605b0a01d9a9f1ce324f [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 {
crate::wlan_policy::types::AccessPointState,
anyhow::Error,
fidl::endpoints::{create_endpoints, create_proxy},
fidl_fuchsia_wlan_common::RequestStatus,
fidl_fuchsia_wlan_policy::{
AccessPointControllerMarker, AccessPointControllerProxy, AccessPointListenerMarker,
AccessPointProviderMarker, AccessPointStateUpdatesMarker,
AccessPointStateUpdatesRequestStream, ConnectivityMode, Credential, NetworkConfig,
NetworkIdentifier, OperatingBand, OperatingState, SecurityType,
},
fuchsia_component::client::connect_to_protocol,
futures::TryStreamExt,
std::{
cell::Cell,
fmt::{self, Debug},
},
};
pub struct WlanApPolicyFacade {
ap_controller: AccessPointControllerProxy,
update_listener: Cell<Option<AccessPointStateUpdatesRequestStream>>,
}
impl Debug for WlanApPolicyFacade {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let listener = self.update_listener.take();
let update_listener =
if listener.is_some() { "Some(AccessPointStateUpdatesRequestStream)" } else { "None" }
.to_string();
self.update_listener.set(listener);
f.debug_struct("WlanApPolicyFacade")
.field("controller", &self.ap_controller)
.field("update_listener", &update_listener)
.finish()
}
}
impl WlanApPolicyFacade {
pub fn new() -> Result<WlanApPolicyFacade, Error> {
let policy_provider = connect_to_protocol::<AccessPointProviderMarker>()?;
let (ap_controller, server_end) = create_proxy::<AccessPointControllerMarker>().unwrap();
let (update_client_end, update_listener) =
create_endpoints::<AccessPointStateUpdatesMarker>();
let () = policy_provider.get_controller(server_end, update_client_end)?;
let update_stream = update_listener.into_stream()?;
Ok(WlanApPolicyFacade { ap_controller, update_listener: Cell::new(Some(update_stream)) })
}
pub async fn start_access_point(
&self,
target_ssid: Vec<u8>,
type_: SecurityType,
credential: Credential,
mode: ConnectivityMode,
band: OperatingBand,
) -> Result<(), Error> {
let listener = connect_to_protocol::<AccessPointListenerMarker>()?;
let (client_end, server_end) = create_endpoints::<AccessPointStateUpdatesMarker>();
listener.get_listener(client_end)?;
let mut server_stream = server_end.into_stream()?;
match server_stream.try_next().await? {
Some(update) => {
let update = update.into_on_access_point_state_update();
let (_, responder) = match update {
Some((update, responder)) => (update, responder),
None => return Err(format_err!("AP provider produced invalid update.")),
};
let _ = responder.send();
}
None => return Err(format_err!("initial steam already busted")),
}
let network_id = NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ };
match self
.ap_controller
.start_access_point(
&NetworkConfig {
id: Some(network_id),
credential: Some(credential),
..Default::default()
},
mode,
band,
)
.await?
{
RequestStatus::Acknowledged => {}
RequestStatus::RejectedNotSupported => {
return Err(format_err!("failed to start AP (not supported)"))
}
RequestStatus::RejectedIncompatibleMode => {
return Err(format_err!("failed to start AP (incompatible mode)"))
}
RequestStatus::RejectedAlreadyInUse => {
return Err(format_err!("failed to start AP (already in use)"))
}
RequestStatus::RejectedDuplicateRequest => {
return Err(format_err!("failed to start AP (duplicate request)"))
}
}
while let Some(update_request) = server_stream.try_next().await.unwrap() {
let update = update_request.into_on_access_point_state_update();
let (updates, responder) = match update {
Some((update, responder)) => (update, responder),
None => return Err(format_err!("AP provider produced invalid update.")),
};
let _ = responder.send();
for update in updates {
match update.state {
Some(state) => match state {
OperatingState::Failed => {
return Err(format_err!("Failed to start AP."));
}
OperatingState::Starting => {
continue;
}
OperatingState::Active => return Ok(()),
},
None => continue,
}
}
}
return Err(format_err!("AP update stream failed unexpectedly"));
}
pub async fn stop_access_point(
&self,
target_ssid: Vec<u8>,
type_: SecurityType,
credential: Credential,
) -> Result<(), Error> {
let network_id = NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ };
match self
.ap_controller
.stop_access_point(&NetworkConfig {
id: Some(network_id),
credential: Some(credential),
..Default::default()
})
.await?
{
RequestStatus::Acknowledged => Ok(()),
RequestStatus::RejectedNotSupported => {
Err(format_err!("Failed to stop AP (not supported)"))
}
RequestStatus::RejectedIncompatibleMode => {
Err(format_err!("Failed to stop AP (incompatible mode)"))
}
RequestStatus::RejectedAlreadyInUse => {
Err(format_err!("Failed to stop AP (already in use)"))
}
RequestStatus::RejectedDuplicateRequest => {
Err(format_err!("Failed to stop AP (duplicate request)"))
}
}
}
pub async fn stop_all_access_points(&self) -> Result<(), Error> {
self.ap_controller.stop_all_access_points()?;
Ok(())
}
/// Creates a listener update stream for getting status updates.
fn init_listener() -> Result<AccessPointStateUpdatesRequestStream, Error> {
let listener = connect_to_protocol::<AccessPointListenerMarker>()?;
let (client_end, server_end) =
fidl::endpoints::create_endpoints::<AccessPointStateUpdatesMarker>();
listener.get_listener(client_end)?;
Ok(server_end.into_stream()?)
}
/// This function will set a new listener even if there is one because new listeners will get
/// the most recent update immediately without waiting.
pub fn set_new_listener(&self) -> Result<(), Error> {
self.update_listener.set(Some(Self::init_listener()?));
Ok(())
}
/// Wait for and return an AP update. If this is the first update gotten from the facade
/// since the AP controller or a new update listener has been created, it will get an
/// immediate status. After that, it will wait for a change and return a status when there has
/// been a change since the last call to get_update. This call will hang if there are no
/// updates.
/// This function is not thread safe, so there should not be multiple get_update calls at the
/// same time unless a new listener is set between them. There is no lock around the
/// update_listener field of the facade in order to prevent a hanging get_update from blocking
/// all future get_updates.
pub async fn get_update(&self) -> Result<Vec<AccessPointState>, Error> {
// Initialize the update listener if it has not been initialized.
let listener = self.update_listener.take();
let mut update_listener = if listener.is_none() {
Self::init_listener()
} else {
listener.ok_or(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_access_point_state_update();
let (update, responder) = match update {
Some((update, responder)) => (update, responder),
None => return Err(format_err!("Client provider produced invalid update.")).into(),
};
// Ack the update.
responder.send().map_err(|e| format_err!("failed to ack update: {}", e))?;
// Put the update listener back in the facade
self.update_listener.set(Some(update_listener));
let update = update.into_iter().map(|update| AccessPointState::from(update)).collect();
Ok(update)
} else {
self.update_listener.set(Some(update_listener));
Err(format_err!("update listener's next update is None"))
}
}
}