blob: 32e8700474a82ea7ab1371dd79b0c5bb4d0ce346 [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,
fidl::endpoints::{create_endpoints, create_proxy},
fidl_fuchsia_wlan_common::RequestStatus,
fidl_fuchsia_wlan_policy::{
AccessPointControllerMarker, AccessPointControllerProxy, AccessPointListenerMarker,
AccessPointProviderMarker, AccessPointProviderProxy, AccessPointStateUpdatesMarker,
ConnectivityMode, Credential, NetworkConfig, NetworkIdentifier, OperatingBand,
OperatingState, SecurityType,
},
fuchsia_component::client::connect_to_service,
futures::TryStreamExt,
};
#[derive(Debug)]
pub struct WlanApPolicyFacade {
policy_provider: AccessPointProviderProxy,
ap_controller: AccessPointControllerProxy,
}
impl WlanApPolicyFacade {
pub fn new() -> Result<WlanApPolicyFacade, Error> {
let policy_provider = connect_to_service::<AccessPointProviderMarker>()?;
let (ap_controller, server_end) = create_proxy::<AccessPointControllerMarker>().unwrap();
// Drop the initial client state update server end. New update request streams will be
// created for calls that rely on state changes.
let (update_client_end, _) = create_endpoints::<AccessPointStateUpdatesMarker>().unwrap();
let () = policy_provider.get_controller(server_end, update_client_end)?;
Ok(WlanApPolicyFacade { policy_provider, ap_controller })
}
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_service::<AccessPointListenerMarker>()?;
let (client_end, server_end) = create_endpoints::<AccessPointStateUpdatesMarker>().unwrap();
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),
..NetworkConfig::EMPTY
},
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),
..NetworkConfig::EMPTY
})
.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(())
}
}