blob: 5a796146f006da07ea072d18ebc0329561645b99 [file] [log] [blame]
// Copyright 2021 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.
//! Serves Client policy services.
use {
crate::{
client::types as client_types,
config_management::{
self, Credential, NetworkConfigError, NetworkIdentifier, SaveError,
SavedNetworksManagerApi,
},
mode_management::iface_manager_api::{ConnectAttemptRequest, IfaceManagerApi},
telemetry::{TelemetryEvent, TelemetrySender},
util::listener,
},
fidl::epitaph::ChannelEpitaphExt,
fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_policy as fidl_policy,
fuchsia_zircon as zx,
futures::{
lock::{Mutex, MutexGuard},
prelude::*,
select,
stream::FuturesUnordered,
},
std::sync::Arc,
tracing::{error, info, warn},
};
pub mod connection_selection;
pub mod roaming;
pub mod scan;
pub mod state_machine;
pub mod types;
/// Max number of network configs that we will send at once through the network config iterator
/// in get_saved_networks. This depends on the maximum size of a FIDL NetworkConfig, so it may
/// need to change if a FIDL NetworkConfig or FIDL Credential changes.
const MAX_CONFIGS_PER_RESPONSE: usize = 100;
// This number was chosen arbitrarily.
const MAX_CONCURRENT_LISTENERS: usize = 1000;
type ClientRequests = fidl::endpoints::ServerEnd<fidl_policy::ClientControllerMarker>;
type SavedNetworksPtr = Arc<dyn SavedNetworksManagerApi>;
/// Serves the ClientProvider protocol.
/// Only one ClientController can be active. Additional requests to register ClientControllers
/// will result in their channel being immediately closed.
pub async fn serve_provider_requests(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
update_sender: listener::ClientListenerMessageSender,
saved_networks: SavedNetworksPtr,
scan_requester: Arc<dyn scan::ScanRequestApi>,
client_provider_lock: Arc<Mutex<()>>,
mut requests: fidl_policy::ClientProviderRequestStream,
telemetry_sender: TelemetrySender,
) {
let mut controller_reqs = FuturesUnordered::new();
loop {
select! {
// Progress controller requests.
_ = controller_reqs.select_next_some() => (),
// Process provider requests.
req = requests.select_next_some() => if let Ok(req) = req {
// If another component already has a client provider, rejest the request.
if let Some(client_provider_guard) = client_provider_lock.try_lock() {
let fut = handle_provider_request(
Arc::clone(&iface_manager),
update_sender.clone(),
saved_networks.clone(),
scan_requester.clone(),
client_provider_guard,
req,
telemetry_sender.clone(),
);
controller_reqs.push(fut);
} else if let Err(e) = reject_provider_request(req) {
error!("error sending rejection epitaph: {:?}", e);
}
},
complete => break,
}
}
}
/// Serves the ClientListener protocol.
pub async fn serve_listener_requests(
update_sender: listener::ClientListenerMessageSender,
requests: fidl_policy::ClientListenerRequestStream,
) {
let serve_fut = requests
.try_for_each_concurrent(MAX_CONCURRENT_LISTENERS, |req| {
handle_listener_request(update_sender.clone(), req)
})
.unwrap_or_else(|e| error!("error serving Client Listener API: {}", e));
serve_fut.await;
}
/// Handle inbound requests to acquire a new ClientController.
async fn handle_provider_request(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
update_sender: listener::ClientListenerMessageSender,
saved_networks: SavedNetworksPtr,
scan_requester: Arc<dyn scan::ScanRequestApi>,
client_provider_guard: MutexGuard<'_, ()>,
req: fidl_policy::ClientProviderRequest,
telemetry_sender: TelemetrySender,
) -> Result<(), fidl::Error> {
match req {
fidl_policy::ClientProviderRequest::GetController { requests, updates, .. } => {
register_listener(update_sender, updates.into_proxy()?);
handle_client_requests(
iface_manager,
scan_requester,
saved_networks,
client_provider_guard,
requests,
telemetry_sender,
)
.await?;
Ok(())
}
}
}
/// Logs a message for an incoming ClientControllerRequest
fn log_client_request(request: &fidl_policy::ClientControllerRequest) {
info!(
"Received policy client request {}",
match request {
fidl_policy::ClientControllerRequest::Connect { .. } => "Connect",
fidl_policy::ClientControllerRequest::StartClientConnections { .. } =>
"StartClientConnections",
fidl_policy::ClientControllerRequest::StopClientConnections { .. } =>
"StopClientConnections",
fidl_policy::ClientControllerRequest::ScanForNetworks { .. } => "ScanForNetworks",
fidl_policy::ClientControllerRequest::SaveNetwork { .. } => "SaveNetwork",
fidl_policy::ClientControllerRequest::RemoveNetwork { .. } => "RemoveNetwork",
fidl_policy::ClientControllerRequest::GetSavedNetworks { .. } => "GetSavedNetworks",
}
);
}
/// Handles all incoming requests from a ClientController.
async fn handle_client_requests(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
scan_requester: Arc<dyn scan::ScanRequestApi>,
saved_networks: SavedNetworksPtr,
client_provider_guard: MutexGuard<'_, ()>,
requests: ClientRequests,
telemetry_sender: TelemetrySender,
) -> Result<(), fidl::Error> {
let mut request_stream = requests.into_stream()?;
while let Some(request) = request_stream.try_next().await? {
log_client_request(&request);
match request {
fidl_policy::ClientControllerRequest::Connect { id, responder, .. } => {
let response = handle_client_request_connect(
Arc::clone(&iface_manager),
saved_networks.clone(),
&id,
)
.await;
responder.send(response)?
}
fidl_policy::ClientControllerRequest::StartClientConnections { responder } => {
telemetry_sender.send(TelemetryEvent::StartClientConnectionsRequest);
let response =
handle_client_request_start_client_connections(iface_manager.clone()).await;
responder.send(response)?
}
fidl_policy::ClientControllerRequest::StopClientConnections { responder } => {
telemetry_sender.send(TelemetryEvent::StopClientConnectionsRequest);
let response =
handle_client_request_stop_client_connections(iface_manager.clone()).await;
responder.send(response)?
}
fidl_policy::ClientControllerRequest::ScanForNetworks { iterator, .. } => {
let fut = handle_client_request_scan(
scan_requester.clone(),
saved_networks.clone(),
telemetry_sender.clone(),
iterator,
);
// The scan handler is infallible and should not block handling of further request.
// Detach the future here so we continue responding to other requests.
fuchsia_async::Task::spawn(fut).detach();
}
fidl_policy::ClientControllerRequest::SaveNetwork { config, responder } => {
// If there is an error saving the network, log it and convert to a FIDL value.
let response = handle_client_request_save_network(
saved_networks.clone(),
config,
Arc::clone(&iface_manager),
)
.await
.map_err(|e| {
error!("Failed to save network: {:?}", e);
fidl_policy::NetworkConfigChangeError::from(e)
});
responder.send(response)?;
}
fidl_policy::ClientControllerRequest::RemoveNetwork { config, responder } => {
let err = handle_client_request_remove_network(
saved_networks.clone(),
config,
iface_manager.clone(),
)
.map_err(|_| SaveError::GeneralError)
.await;
responder.send(err)?;
}
fidl_policy::ClientControllerRequest::GetSavedNetworks { iterator, .. } => {
handle_client_request_get_networks(saved_networks.clone(), iterator).await?;
}
}
}
drop(client_provider_guard);
Ok(())
}
/// Attempts to issue a new connect request to the currently active Client.
/// The network's configuration must have been stored before issuing a connect request.
async fn handle_client_request_connect(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
saved_networks: SavedNetworksPtr,
network: &fidl_policy::NetworkIdentifier,
) -> fidl_common::RequestStatus {
let network_config = match saved_networks
.lookup(&NetworkIdentifier::new(
client_types::Ssid::from_bytes_unchecked(network.ssid.clone()),
network.type_.into(),
))
.await
.pop()
{
Some(config) => config,
None => {
error!("Requested network not found in saved networks");
return fidl_common::RequestStatus::RejectedNotSupported;
}
};
let network_id = fidl_policy::NetworkIdentifier {
ssid: network_config.ssid.into(),
type_: fidl_policy::SecurityType::from(network_config.security_type),
};
let connect_req = ConnectAttemptRequest::new(
network_id.into(),
network_config.credential,
client_types::ConnectReason::FidlConnectRequest,
);
let mut iface_manager = iface_manager.lock().await;
match iface_manager.connect(connect_req).await {
Ok(_) => fidl_common::RequestStatus::Acknowledged,
Err(e) => {
error!("failed to connect: {:?}", e);
fidl_common::RequestStatus::RejectedIncompatibleMode
}
}
}
async fn handle_client_request_scan(
scan_requester: Arc<dyn scan::ScanRequestApi>,
saved_networks: SavedNetworksPtr,
telemetry_sender: TelemetrySender,
output_iterator: fidl::endpoints::ServerEnd<fidl_fuchsia_wlan_policy::ScanResultIteratorMarker>,
) {
let get_scan_results = async {
let passive_scan_results =
scan_requester.perform_scan(scan::ScanReason::ClientRequest, vec![], vec![]).await?;
let requested_active_scan_ssids: Vec<types::Ssid> =
config_management::select_high_probability_hidden_networks(
saved_networks.get_networks().await,
)
.drain(..)
.map(|id| id.ssid)
.collect();
info!(
"Completed passive scan for API request, {} likely-hidden networks to scan for.",
requested_active_scan_ssids.len()
);
telemetry_sender.send(TelemetryEvent::ActiveScanRequestedViaApi {
num_ssids_requested: requested_active_scan_ssids.len(),
});
if requested_active_scan_ssids.is_empty() {
Ok(passive_scan_results)
} else {
scan_requester
.perform_scan(scan::ScanReason::ClientRequest, requested_active_scan_ssids, vec![])
.await
.map(|mut scan_results| {
scan_results.extend(passive_scan_results);
scan_results
})
}
};
match get_scan_results.await {
Ok(results) => {
// Note: for now, we must always present WPA2/3 networks as WPA2 over our external
// interfaces (i.e. to FIDL consumers of scan results). See b/182209070 for more
// information.
// TODO(b/182569380): use actual wpa3 support in this conversion rather than 'false'
let fidl_results = scan::scan_result_to_policy_scan_result(&results, false);
if let Err(e) = scan::send_scan_results_over_fidl(output_iterator, &fidl_results).await
{
warn!("Failed to send scan results to requester: {:?}", e);
}
}
Err(e) => {
if let Err(e) = scan::send_scan_error_over_fidl(output_iterator, e).await {
warn!("Failed to send scan error to requester: {:?}", e);
}
}
}
}
/// This function handles requests to save a network by saving the network and sending back to the
/// controller whether or not we successfully saved the network. There could be a FIDL error in
/// sending the response.
async fn handle_client_request_save_network(
saved_networks: SavedNetworksPtr,
network_config: fidl_policy::NetworkConfig,
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
) -> Result<(), NetworkConfigError> {
// The FIDL network config fields are defined as Options, and we consider it an error if either
// field is missing (ie None) here.
let net_id: client_types::NetworkIdentifier =
network_config.id.ok_or_else(|| NetworkConfigError::ConfigMissingId)?.into();
let credential = Credential::try_from(
network_config.credential.ok_or_else(|| NetworkConfigError::ConfigMissingCredential)?,
)?;
let evicted_config = saved_networks.store(net_id.clone(), credential.clone()).await?;
// If a config was removed, disconnect from that network.
let mut iface_manager = iface_manager.lock().await;
if let Some(config) = evicted_config {
let net_id = client_types::NetworkIdentifier {
ssid: config.ssid,
security_type: config.security_type,
};
match iface_manager
.disconnect(net_id, client_types::DisconnectReason::NetworkConfigUpdated)
.await
{
Ok(()) => {}
Err(e) => error!("failed to disconnect from network: {}", e),
}
}
// Attempt to connect to the new network if there is an idle client interface.
let connect_req = ConnectAttemptRequest::new(
net_id,
credential,
client_types::ConnectReason::NewSavedNetworkAutoconnect,
);
match iface_manager.has_idle_client().await {
Ok(true) => {
info!("Idle interface available, will attempt connection to new saved network");
let _ = iface_manager.connect(connect_req).await;
}
Ok(false) => {}
Err(e) => {
error!("Unable to query idle client state while saving network: {:?}", e);
}
}
Ok(())
}
/// Will remove the specified network and respond to the remove network request with a network
/// config change error if an error occurs while trying to remove the network.
async fn handle_client_request_remove_network(
saved_networks: SavedNetworksPtr,
network_config: fidl_policy::NetworkConfig,
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
) -> Result<(), NetworkConfigError> {
// The FIDL network config fields are defined as Options, and we consider it an error if either
// field is missing (ie None) here.
let net_id = NetworkIdentifier::from(
network_config.id.ok_or_else(|| NetworkConfigError::ConfigMissingId)?,
);
let credential = Credential::try_from(
network_config.credential.ok_or_else(|| NetworkConfigError::ConfigMissingCredential)?,
)?;
if saved_networks.remove(net_id.clone(), credential.clone()).await? {
match iface_manager
.lock()
.await
.disconnect(net_id, client_types::DisconnectReason::NetworkUnsaved)
.await
{
Ok(()) => {}
Err(e) => error!("failed to disconnect from network: {}", e),
}
}
Ok(())
}
async fn handle_client_request_get_networks(
saved_networks: SavedNetworksPtr,
iterator: fidl::endpoints::ServerEnd<fidl_policy::NetworkConfigIteratorMarker>,
) -> Result<(), fidl::Error> {
// make sufficiently small batches of networks to send and convert configs to FIDL values
let network_configs = saved_networks.get_networks().await;
let chunks = network_configs.chunks(MAX_CONFIGS_PER_RESPONSE);
let fidl_chunks = chunks.into_iter().map(|chunk| {
chunk
.iter()
.map(fidl_policy::NetworkConfig::from)
.collect::<Vec<fidl_policy::NetworkConfig>>()
});
let mut stream = iterator.into_stream()?;
for chunk in fidl_chunks {
send_next_chunk(&mut stream, chunk).await?;
}
send_next_chunk(&mut stream, vec![]).await
}
/// Send a chunk of saved networks to the specified FIDL iterator
async fn send_next_chunk(
stream: &mut fidl_policy::NetworkConfigIteratorRequestStream,
chunk: Vec<fidl_policy::NetworkConfig>,
) -> Result<(), fidl::Error> {
if let Some(req) = stream.try_next().await? {
let fidl_policy::NetworkConfigIteratorRequest::GetNext { responder } = req;
responder.send(&chunk)
} else {
// This will happen if the iterator request stream was closed and we expected to send
// another response.
// TODO(https://fxbug.dev/42121602) Test this error path
info!("Info: peer closed channel for network config results unexpectedly");
Ok(())
}
}
/// Handle inbound requests to register an additional ClientStateUpdates listener.
async fn handle_listener_request(
update_sender: listener::ClientListenerMessageSender,
req: fidl_policy::ClientListenerRequest,
) -> Result<(), fidl::Error> {
match req {
fidl_policy::ClientListenerRequest::GetListener { updates, .. } => {
register_listener(update_sender, updates.into_proxy()?);
Ok(())
}
}
}
/// Registers a new update listener.
/// The client's current state will be send to the newly added listener immediately.
fn register_listener(
update_sender: listener::ClientListenerMessageSender,
listener: fidl_policy::ClientStateUpdatesProxy,
) {
let _ignored = update_sender.unbounded_send(listener::Message::NewListener(listener));
}
/// Rejects a ClientProvider request by sending a corresponding Epitaph via the |requests| and
/// |updates| channels.
fn reject_provider_request(req: fidl_policy::ClientProviderRequest) -> Result<(), fidl::Error> {
match req {
fidl_policy::ClientProviderRequest::GetController { requests, updates, .. } => {
info!("Rejecting new client controller request because a controller is in use");
requests.into_channel().close_with_epitaph(zx::Status::ALREADY_BOUND)?;
updates.into_channel().close_with_epitaph(zx::Status::ALREADY_BOUND)?;
Ok(())
}
}
}
/// Allows client operations to be performed.
async fn handle_client_request_start_client_connections(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
) -> fidl_common::RequestStatus {
let mut iface_manager = iface_manager.lock().await;
if let Err(e) = iface_manager.start_client_connections().await {
warn!("encountered an error while starting client connections: {:?}", e);
}
fidl_common::RequestStatus::Acknowledged
}
/// Stops all active client connections and disallows future client operations.
async fn handle_client_request_stop_client_connections(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
) -> fidl_common::RequestStatus {
let mut iface_manager = iface_manager.lock().await;
if let Err(e) = iface_manager
.stop_client_connections(client_types::DisconnectReason::FidlStopClientConnectionsRequest)
.await
{
warn!("encountered an error while stopping client connections: {:?}", e);
}
fidl_common::RequestStatus::Acknowledged
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
access_point::state_machine as ap_fsm,
config_management::{NetworkConfig, SecurityType, WPA_PSK_BYTE_LEN},
mode_management::iface_manager_api::SmeForScan,
util::testing::{
fakes::{FakeSavedNetworksManager, FakeScanRequester},
generate_random_fidl_network_config, generate_random_fidl_network_config_with_ssid,
},
},
anyhow::{format_err, Error},
async_trait::async_trait,
fidl::endpoints::{create_proxy, create_request_stream, Proxy},
fuchsia_async as fasync,
futures::{
channel::{mpsc, oneshot},
task::Poll,
},
std::pin::pin,
test_case::test_case,
wlan_common::assert_variant,
};
/// Only used to tell us what disconnect request was given to the IfaceManager so that we
/// don't need to worry about the implementation logic in the FakeIfaceManager.
#[derive(Debug)]
enum IfaceManagerRequest {
Disconnect(client_types::NetworkIdentifier, client_types::DisconnectReason),
}
struct FakeIfaceManager {
pub sme_proxy: fidl_fuchsia_wlan_sme::ClientSmeProxy,
pub disconnected_ifaces: Vec<u16>,
command_sender: mpsc::Sender<IfaceManagerRequest>,
pub wpa3_capable: bool,
start_client_connections_succeeds: bool,
stop_client_connections_succeeds: bool,
}
impl FakeIfaceManager {
pub fn new(
proxy: fidl_fuchsia_wlan_sme::ClientSmeProxy,
command_sender: mpsc::Sender<IfaceManagerRequest>,
) -> Self {
FakeIfaceManager {
sme_proxy: proxy,
disconnected_ifaces: Vec::new(),
command_sender,
wpa3_capable: true,
start_client_connections_succeeds: true,
stop_client_connections_succeeds: true,
}
}
pub fn new_without_wpa3(
proxy: fidl_fuchsia_wlan_sme::ClientSmeProxy,
command_sender: mpsc::Sender<IfaceManagerRequest>,
) -> Self {
FakeIfaceManager {
sme_proxy: proxy,
disconnected_ifaces: Vec::new(),
command_sender,
wpa3_capable: false,
start_client_connections_succeeds: true,
stop_client_connections_succeeds: true,
}
}
}
#[async_trait]
impl IfaceManagerApi for FakeIfaceManager {
async fn disconnect(
&mut self,
network_id: client_types::NetworkIdentifier,
reason: client_types::DisconnectReason,
) -> Result<(), Error> {
self.command_sender
.try_send(IfaceManagerRequest::Disconnect(network_id, reason))
.map_err(|e| {
error!(
"Failed to send disconnect: commands_sender's receiver may have
been dropped. FakeIfaceManager should be created manually with a sender
assigned: {:?}",
e
);
format_err!("failed to send disconnect: {:?}", e)
})
}
async fn connect(&mut self, _connect_req: ConnectAttemptRequest) -> Result<(), Error> {
let _ = self.disconnected_ifaces.pop();
Ok(())
}
async fn record_idle_client(&mut self, iface_id: u16) -> Result<(), Error> {
self.disconnected_ifaces.push(iface_id);
Ok(())
}
async fn has_idle_client(&mut self) -> Result<bool, Error> {
Ok(!self.disconnected_ifaces.is_empty())
}
async fn handle_added_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
unimplemented!()
}
async fn handle_removed_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
unimplemented!()
}
async fn get_sme_proxy_for_scan(&mut self) -> Result<SmeForScan, Error> {
let (defect_sender, _) = mpsc::unbounded();
Ok(SmeForScan::new(self.sme_proxy.clone(), 0, defect_sender))
}
async fn stop_client_connections(
&mut self,
_reason: client_types::DisconnectReason,
) -> Result<(), Error> {
if self.stop_client_connections_succeeds {
Ok(())
} else {
Err(format_err!("stop client connections failed"))
}
}
async fn start_client_connections(&mut self) -> Result<(), Error> {
if self.start_client_connections_succeeds {
Ok(())
} else {
Err(format_err!("start client connections failed"))
}
}
async fn start_ap(
&mut self,
_config: ap_fsm::ApConfig,
) -> Result<oneshot::Receiver<()>, Error> {
unimplemented!()
}
async fn stop_ap(&mut self, _ssid: types::Ssid, _password: Vec<u8>) -> Result<(), Error> {
unimplemented!()
}
async fn stop_all_aps(&mut self) -> Result<(), Error> {
unimplemented!()
}
async fn has_wpa3_capable_client(&mut self) -> Result<bool, Error> {
Ok(self.wpa3_capable)
}
async fn set_country(
&mut self,
_country_code: Option<[u8; client_types::REGION_CODE_LEN]>,
) -> Result<(), Error> {
unimplemented!()
}
}
/// Requests a new ClientController from the given ClientProvider.
fn request_controller(
provider: &fidl_policy::ClientProviderProxy,
) -> (fidl_policy::ClientControllerProxy, fidl_policy::ClientStateUpdatesRequestStream) {
let (controller, requests) = create_proxy::<fidl_policy::ClientControllerMarker>()
.expect("failed to create ClientController proxy");
let (update_sink, update_stream) =
create_request_stream::<fidl_policy::ClientStateUpdatesMarker>()
.expect("failed to create ClientStateUpdates proxy");
provider.get_controller(requests, update_sink).expect("error getting controller");
(controller, update_stream)
}
struct TestValues {
saved_networks: SavedNetworksPtr,
net_id_open: fidl_policy::NetworkIdentifier,
net_id_wpa2_w_password: fidl_policy::NetworkIdentifier,
net_id_wpa2_w_psk: fidl_policy::NetworkIdentifier,
provider: fidl_policy::ClientProviderProxy,
requests: fidl_policy::ClientProviderRequestStream,
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
iface_mgr_req_recvr: mpsc::Receiver<IfaceManagerRequest>,
scan_requester: Arc<FakeScanRequester>,
update_sender: mpsc::UnboundedSender<listener::ClientListenerMessage>,
listener_updates: mpsc::UnboundedReceiver<listener::ClientListenerMessage>,
client_provider_lock: Arc<Mutex<()>>,
telemetry_sender: TelemetrySender,
telemetry_receiver: mpsc::Receiver<TelemetryEvent>,
}
// setup channels and proxies needed for the tests to use use the Client Provider and
// Client Controller APIs in tests. The stash id should be the test name so that each
// test will have a unique persistent store behind it.
fn test_setup() -> TestValues {
let net_id_open = fidl_policy::NetworkIdentifier {
ssid: "foobar".to_string().into_bytes(),
type_: fidl_policy::SecurityType::None,
};
let net_id_wpa2_w_password = fidl_policy::NetworkIdentifier {
ssid: b"foobar-wpa2".to_vec(),
type_: fidl_policy::SecurityType::Wpa2,
};
let net_id_wpa2_w_psk = fidl_policy::NetworkIdentifier {
ssid: b"foobar-psk".to_vec(),
type_: fidl_policy::SecurityType::Wpa2,
};
let presaved_default_configs = vec![
fidl_policy::NetworkConfig {
id: Some(net_id_open.clone()),
credential: Some(fidl_policy::Credential::None(fidl_policy::Empty)),
..Default::default()
},
fidl_policy::NetworkConfig {
id: Some(net_id_wpa2_w_password.clone()),
credential: Some(fidl_policy::Credential::Password(b"foobar-password".to_vec())),
..Default::default()
},
fidl_policy::NetworkConfig {
id: Some(net_id_wpa2_w_psk.clone()),
credential: Some(fidl_policy::Credential::Psk(vec![64; WPA_PSK_BYTE_LEN].to_vec())),
..Default::default()
},
];
let saved_networks =
Arc::new(FakeSavedNetworksManager::new_with_saved_networks(presaved_default_configs));
let (telemetry_sender, telemetry_receiver) = mpsc::channel::<TelemetryEvent>(100);
let (provider, requests) = create_proxy::<fidl_policy::ClientProviderMarker>()
.expect("failed to create ClientProvider proxy");
let requests = requests.into_stream().expect("failed to create stream");
let (proxy, _server) = create_proxy::<fidl_fuchsia_wlan_sme::ClientSmeMarker>()
.expect("failed to create ClientSmeProxy");
let (req_sender, iface_mgr_req_recvr) = mpsc::channel(1);
let iface_manager = FakeIfaceManager::new(proxy.clone(), req_sender);
let iface_manager = Arc::new(Mutex::new(iface_manager));
let scan_requester = Arc::new(FakeScanRequester::new());
let (update_sender, listener_updates) = mpsc::unbounded();
TestValues {
saved_networks,
net_id_open,
net_id_wpa2_w_password,
net_id_wpa2_w_psk,
provider,
requests,
iface_manager,
iface_mgr_req_recvr,
scan_requester,
update_sender,
listener_updates,
client_provider_lock: Arc::new(Mutex::new(())),
telemetry_sender: TelemetrySender::new(telemetry_sender),
telemetry_receiver,
}
}
#[fuchsia::test]
fn connect_request_unknown_network() {
let ssid = client_types::Ssid::try_from("foobar-unknown").unwrap();
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
Arc::clone(&test_values.saved_networks),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue connect request.
let connect_fut = controller.connect(&fidl_policy::NetworkIdentifier {
ssid: ssid.to_vec(),
type_: fidl_policy::SecurityType::None,
});
let mut connect_fut = pin!(connect_fut);
// Process connect request and verify connect response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::RejectedNotSupported))
);
// Unknown network should not have been saved by saved networks manager
// since we did not successfully connect.
let id = NetworkIdentifier::new(ssid, SecurityType::None);
let lookup_fut = test_values.saved_networks.lookup(&id);
assert!(exec.run_singlethreaded(lookup_fut).is_empty());
}
#[fuchsia::test]
fn connect_request_open_network() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
Arc::clone(&test_values.saved_networks),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue connect request.
let connect_fut = controller.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request and verify connect response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Verify that the connect call is acknowledged.
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[fuchsia::test]
fn connect_request_protected_network() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
Arc::clone(&test_values.saved_networks),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue connect request.
let connect_fut = controller.connect(&test_values.net_id_wpa2_w_password);
let mut connect_fut = pin!(connect_fut);
// Process connect request and verify connect response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Verify that the connect call is acknowledged.
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[fuchsia::test]
fn connect_request_protected_psk_network() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
Arc::clone(&test_values.saved_networks),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue connect request.
let connect_fut = controller.connect(&test_values.net_id_wpa2_w_psk);
let mut connect_fut = pin!(connect_fut);
// Process connect request and verify connect response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Verify that the connect call is acknowledged.
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[fuchsia::test]
fn test_connect_wpa3_not_supported() {
let mut exec = fasync::TestExecutor::new();
let mut test_values = test_setup();
// Create a Fake IfaceManager without WPA3 support for this test.
let (proxy, _server) = create_proxy::<fidl_fuchsia_wlan_sme::ClientSmeMarker>()
.expect("failed to create ClientSmeProxy");
let (req_sender, _req_recvr) = mpsc::channel(1);
let iface_manager = FakeIfaceManager::new_without_wpa3(proxy.clone(), req_sender);
let iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>> =
Arc::new(Mutex::new(iface_manager));
let serve_fut = serve_provider_requests(
iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Issue connect request to a WPA3 network.
let net_id = fidl_policy::NetworkIdentifier {
ssid: b"foobar".to_vec(),
type_: fidl_policy::SecurityType::Wpa3,
};
// Check that connect request fails because WPA3 is not supported.
let connect_fut = controller.connect(&net_id);
let mut connect_fut = pin!(connect_fut);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::RejectedNotSupported))
);
}
#[fuchsia::test]
fn test_connect_wpa3_is_supported() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Save the network so it can be found to connect.
let net_id = fidl_policy::NetworkIdentifier {
ssid: b"foobar".to_vec(),
type_: fidl_policy::SecurityType::Wpa3,
};
let password = Credential::Password(b"password".to_vec());
let save_fut = test_values.saved_networks.store(net_id.clone().into(), password);
let mut save_fut = pin!(save_fut);
assert!(exec.run_singlethreaded(&mut save_fut).expect("Failed to save network").is_none());
// Issue connect request to a WPA3 network.
let connect_fut = controller.connect(&net_id);
let mut connect_fut = pin!(connect_fut);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[fuchsia::test]
fn start_and_stop_client_connections() {
let mut exec = fasync::TestExecutor::new();
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should now be waiting for request.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue request to start client connections.
let start_fut = controller.start_client_connections();
let mut start_fut = pin!(start_fut);
// Request should be acknowledged.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut start_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
// A message should be sent to telemetry.
assert_variant!(test_values.telemetry_receiver.try_next(), Ok(Some(event)) => {
assert_variant!(event, TelemetryEvent::StartClientConnectionsRequest);
});
// Perform a connect operation.
let connect_fut = controller.connect(&test_values.net_id_wpa2_w_password);
let mut connect_fut = pin!(connect_fut);
// Process connect request and verify connect response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Verify that the connect call is acknowledged.
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
// The client state machine will immediately query for status.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue request to stop client connections.
let stop_fut = controller.stop_client_connections();
let mut stop_fut = pin!(stop_fut);
// Run the serve future until it stalls and expect the client to disconnect
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// A message should be sent to telemetry.
assert_variant!(test_values.telemetry_receiver.try_next(), Ok(Some(event)) => {
assert_variant!(event, TelemetryEvent::StopClientConnectionsRequest);
});
// Request should be acknowledged.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut stop_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
/// End-to-end test of the scan function, verifying that an incoming
/// FIDL scan request results in a scan in the SME, and that the results
/// make it back to the requester.
#[fuchsia::test]
fn scan_end_to_end() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester.clone(),
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue a scan request.
let (_iter, server) = fidl::endpoints::create_proxy().expect("failed to create iterator");
controller.scan_for_networks(server).expect("Failed to call scan for networks");
// Currently the FakeScanRequester will panic if a scan is requested and no results
// are queued, so add something.
exec.run_singlethreaded(test_values.scan_requester.add_scan_result(Ok(vec![])));
// A second scan request for all the likely-hidden networks
exec.run_singlethreaded(test_values.scan_requester.add_scan_result(Ok(vec![])));
// Process scan request and verify scan response.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Check that a scan request was sent to the scan module, including an active scan for the
// potentially-hidden networks.
let expected_active_ssids = vec![
test_values.net_id_wpa2_w_password,
test_values.net_id_wpa2_w_psk,
test_values.net_id_open,
]
.iter()
.map(|id| types::Ssid::from_bytes_unchecked(id.ssid.clone()))
.collect();
exec.run_singlethreaded(test_values.scan_requester.verify_scan_request((
scan::ScanReason::ClientRequest,
vec![],
vec![],
)));
exec.run_singlethreaded(test_values.scan_requester.verify_scan_request((
scan::ScanReason::ClientRequest,
expected_active_ssids,
vec![],
)));
}
#[fuchsia::test]
fn save_network() {
let mut exec = fasync::TestExecutor::new();
let saved_networks = Arc::new(FakeSavedNetworksManager::new());
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Save some network
let network_id = fidl_policy::NetworkIdentifier {
ssid: b"foo".to_vec(),
type_: fidl_policy::SecurityType::None,
};
let network_config = fidl_policy::NetworkConfig {
id: Some(network_id.clone()),
credential: Some(fidl_policy::Credential::None(fidl_policy::Empty)),
..Default::default()
};
let mut save_fut = controller.save_network(&network_config);
// Run server_provider forward so that it will process the save network request
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Check that the the response says we succeeded.
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Ready(Ok(Ok(()))));
// Check that the value was actually saved in the saved networks manager.
let target_id = NetworkIdentifier::from(network_id);
let target_config = NetworkConfig::new(target_id.clone(), Credential::None, false)
.expect("Failed to create network config");
assert_eq!(exec.run_singlethreaded(saved_networks.lookup(&target_id)), vec![target_config]);
}
#[fuchsia::test]
fn save_network_with_disconnected_iface() {
let mut exec = fasync::TestExecutor::new();
let saved_networks = Arc::new(FakeSavedNetworksManager::new());
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager.clone(),
test_values.update_sender,
saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// Setup the IfaceManager so that it has a disconnected iface.
{
let iface_manager = test_values.iface_manager.clone();
let iface_manager_fut = iface_manager.lock();
let mut iface_manager_fut = pin!(iface_manager_fut);
let mut iface_manager = match exec.run_until_stalled(&mut iface_manager_fut) {
Poll::Ready(iface_manager) => iface_manager,
Poll::Pending => panic!("expected to acquire iface_manager lock"),
};
let record_idle_fut = iface_manager.record_idle_client(0);
let mut record_idle_fut = pin!(record_idle_fut);
assert_variant!(exec.run_until_stalled(&mut record_idle_fut), Poll::Ready(Ok(())));
}
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Save some network
let network_id = fidl_policy::NetworkIdentifier {
ssid: b"foo".to_vec(),
type_: fidl_policy::SecurityType::None,
};
let network_config = fidl_policy::NetworkConfig {
id: Some(network_id.clone()),
credential: Some(fidl_policy::Credential::None(fidl_policy::Empty)),
..Default::default()
};
let mut save_fut = controller.save_network(&network_config);
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Check that the the response says we succeeded.
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Ready(result) => {
let save_result = result.expect("Failed to get save network response");
assert_eq!(save_result, Ok(()));
});
// Check that the value was actually saved in the saved networks manager.
let target_id = NetworkIdentifier::from(network_id);
let target_config = NetworkConfig::new(target_id.clone(), Credential::None, false)
.expect("Failed to create network config");
assert_eq!(exec.run_singlethreaded(saved_networks.lookup(&target_id)), vec![target_config]);
}
#[fuchsia::test]
fn save_network_overwrite_disconnects() {
let mut exec = fasync::TestExecutor::new();
let mut test_values = test_setup();
let saved_networks = Arc::new(FakeSavedNetworksManager::new());
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Save the network directly.
let network_id = NetworkIdentifier::try_from("foo", SecurityType::Wpa2).unwrap();
let credential = Credential::Password(b"password".to_vec());
let save_fut = saved_networks.store(network_id.clone(), credential.clone());
let mut save_fut = pin!(save_fut);
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Ready(Ok(None)));
// Save a network with the same identifier but a different password
let network_config = fidl_policy::NetworkConfig {
id: Some(fidl_policy::NetworkIdentifier::from(network_id.clone())),
credential: Some(fidl_policy::Credential::Password(b"other-password".to_vec())),
..Default::default()
};
let mut save_fut = controller.save_network(&network_config);
// Process the remove request on the server side and handle requests to stash on the way.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Check that the iface manager was asked to disconnect from some network
assert_variant!(exec.run_until_stalled(&mut test_values.iface_mgr_req_recvr.next()), Poll::Ready(Some(IfaceManagerRequest::Disconnect(net_id, reason))) => {
assert_eq!(net_id, network_id.clone());
assert_eq!(reason, client_types::DisconnectReason::NetworkConfigUpdated);
});
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Ready(Ok(Ok(()))));
}
#[fuchsia::test]
fn save_bad_network_should_fail() {
let mut exec = fasync::TestExecutor::new();
let mut saved_networks = FakeSavedNetworksManager::new();
saved_networks.fail_all_stores = true;
let saved_networks = Arc::new(saved_networks);
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Create a network config whose password is too short. FIDL network config does not
// require valid fields unlike our crate define config. We should not be able to
// successfully save this network through the API.
let bad_network_id = fidl_policy::NetworkIdentifier {
ssid: b"foo".to_vec(),
type_: fidl_policy::SecurityType::Wpa2,
};
let network_config = fidl_policy::NetworkConfig {
id: Some(bad_network_id.clone()),
credential: Some(fidl_policy::Credential::Password(b"bar".to_vec())),
..Default::default()
};
// Attempt to save the config
let mut save_fut = controller.save_network(&network_config);
// Run server_provider forward so that it will process the save network request
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Check that the the response says we failed to save the network.
assert_variant!(exec.run_until_stalled(&mut save_fut), Poll::Ready(result) => {
let error = result.expect("Failed to get save network response");
assert_eq!(error, Err(fidl_policy::NetworkConfigChangeError::NetworkConfigWriteError));
});
// Check that the value was was not saved in saved networks manager.
let target_id = NetworkIdentifier::from(bad_network_id);
assert_eq!(exec.run_singlethreaded(saved_networks.lookup(&target_id)), vec![]);
}
#[fuchsia::test]
fn test_remove_a_network() {
let mut exec = fasync::TestExecutor::new();
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(_)
);
// Request to a network that is saved
let network_config = fidl_policy::NetworkConfig {
id: Some(test_values.net_id_open.clone()),
credential: Some(fidl_policy::Credential::None(fidl_policy::Empty)),
..Default::default()
};
let mut remove_fut = controller.remove_network(&network_config);
// Process the remove request on the server side and handle requests to stash on the way.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Successfully removing a network should always request a disconnect from IfaceManager,
// which will know whether we are connected to the network to disconnect from. This checks
// that the IfaceManager is told to disconnect (if connected).
assert_variant!(exec.run_until_stalled(&mut test_values.iface_mgr_req_recvr.next()), Poll::Ready(Some(IfaceManagerRequest::Disconnect(net_id, reason))) => {
assert_eq!(net_id, test_values.net_id_open.clone().into());
assert_eq!(reason, client_types::DisconnectReason::NetworkUnsaved);
});
assert_variant!(exec.run_until_stalled(&mut remove_fut), Poll::Ready(Ok(Ok(()))));
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert!(exec
.run_singlethreaded(
test_values.saved_networks.lookup(&test_values.net_id_open.clone().into())
)
.is_empty());
// Removing a network that is not saved should not trigger a disconnect.
let mut remove_fut = controller.remove_network(&network_config);
// Process the remove request on the server side and handle requests to stash on the way.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.iface_mgr_req_recvr.next()),
Poll::Pending
);
assert_variant!(exec.run_until_stalled(&mut remove_fut), Poll::Ready(Ok(Ok(()))));
}
#[fuchsia::test]
fn test_get_saved_network() {
let expected_configs = vec![generate_random_fidl_network_config()];
let expected_num_sends = 1;
run_get_saved_networks_test(expected_configs, expected_num_sends);
}
#[fuchsia::test]
fn test_get_saved_networks_multiple_chunks() {
// Save MAX_CONFIGS_PER_RESPONSE + 1 configs so that get_saved_networks should respond with
// 2 chunks of responses plus one response with an empty vector.
let mut configs = vec![];
for index in 0..MAX_CONFIGS_PER_RESPONSE + 1 {
// Create unique network config to be saved.
let ssid = format!("some_config{}", index);
let network_config = generate_random_fidl_network_config_with_ssid(&ssid);
configs.push(network_config);
}
let expected_num_sends = 2;
run_get_saved_networks_test(configs, expected_num_sends);
}
/// Test that get saved networks with the given saved networks
/// test_id: the name of the test to create a unique persistent store for each test
/// configs: list of FIDL NetworkConfigs that we expect to get from get_saved_networks and
/// and which are returned by SavedNetworksManager
/// expected_num_sends: number of chunks of results we expect to get from get_saved_networks.
/// This is not counting the empty vector that signifies no more results.
fn run_get_saved_networks_test(
configs: Vec<fidl_policy::NetworkConfig>,
expected_num_sends: usize,
) {
let mut exec = fasync::TestExecutor::new();
let saved_networks =
Arc::new(FakeSavedNetworksManager::new_with_saved_networks(configs.clone()));
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue request to get the list of saved networks.
let (iter, server) =
fidl::endpoints::create_proxy::<fidl_policy::NetworkConfigIteratorMarker>()
.expect("failed to create iterator");
controller.get_saved_networks(server).expect("Failed to call get saved networks");
// Get responses from iterator. Expect to see the specified number of responses with
// results plus one response of an empty vector indicating the end of results.
let mut saved_networks_results = vec![];
for i in 0..expected_num_sends {
let get_saved_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
let results = exec
.run_singlethreaded(get_saved_fut)
.expect("Failed to get next chunk of saved networks results");
// the size of received chunk should either be max chunk size or whatever is left
// to receive in the last chunk
if i < expected_num_sends - 1 {
assert_eq!(results.len(), MAX_CONFIGS_PER_RESPONSE);
} else {
assert_eq!(results.len(), configs.len() % MAX_CONFIGS_PER_RESPONSE);
}
saved_networks_results.extend(results);
}
let get_saved_end_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
let results = exec
.run_singlethreaded(get_saved_end_fut)
.expect("Failed to get next chunk of saved networks results");
assert!(results.is_empty());
// check whether each network we saved is in the results and that nothing else is there.
for network_config in &configs {
assert!(saved_networks_results.contains(network_config));
}
assert_eq!(configs.len(), saved_networks_results.len());
}
#[fuchsia::test]
fn register_update_listener() {
let mut exec = fasync::TestExecutor::new();
let mut test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a new controller.
let (_controller, _update_stream) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.listener_updates.next()),
Poll::Ready(Some(listener::Message::NewListener(_)))
);
}
#[fuchsia::test]
fn get_listener() {
let mut exec = fasync::TestExecutor::new();
let (listener, requests) = create_proxy::<fidl_policy::ClientListenerMarker>()
.expect("failed to create ClientProvider proxy");
let requests = requests.into_stream().expect("failed to create stream");
let (update_sender, mut listener_updates) = mpsc::unbounded();
let serve_fut = serve_listener_requests(update_sender, requests);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Register listener.
let (update_sink, _update_stream) =
create_request_stream::<fidl_policy::ClientStateUpdatesMarker>()
.expect("failed to create ClientStateUpdates proxy");
listener.get_listener(update_sink).expect("error getting listener");
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut listener_updates.next()),
Poll::Ready(Some(listener::Message::NewListener(_)))
);
}
#[fuchsia::test]
fn multiple_controllers_write_attempt() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a controller.
let (controller1, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request another controller.
let (controller2, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Ensure first controller is operable. Issue connect request.
let connect_fut = controller1.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request from first controller. Verify success.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
// Ensure second controller is not operable. Issue connect request.
let connect_fut = controller2.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request from second controller. Verify failure.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Err(fidl::Error::ClientChannelClosed {
status: zx::Status::ALREADY_BOUND,
..
}))
);
// Drop first controller. A new controller can now take control.
drop(controller1);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request another controller.
let (controller3, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Ensure third controller is operable. Issue connect request.
let connect_fut = controller3.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request from third controller. Verify success.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[fuchsia::test]
fn multiple_controllers_epitaph() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager,
test_values.update_sender,
test_values.saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a controller.
let (_controller1, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request another controller.
let (controller2, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Verify Epitaph was received.
let mut controller2_event_stream = controller2.take_event_stream();
let controller2_event_fut = controller2_event_stream.next();
let mut controller2_event_fut = pin!(controller2_event_fut);
assert_variant!(
exec.run_until_stalled(&mut controller2_event_fut),
Poll::Ready(Some(Err(fidl::Error::ClientChannelClosed {
status: zx::Status::ALREADY_BOUND,
..
})))
);
assert!(controller2.is_closed());
}
struct FakeIfaceManagerNoIfaces {}
#[async_trait]
impl IfaceManagerApi for FakeIfaceManagerNoIfaces {
async fn disconnect(
&mut self,
_network_id: client_types::NetworkIdentifier,
_reason: client_types::DisconnectReason,
) -> Result<(), Error> {
Err(format_err!("No ifaces"))
}
async fn connect(&mut self, _connect_req: ConnectAttemptRequest) -> Result<(), Error> {
Err(format_err!("No ifaces"))
}
async fn record_idle_client(&mut self, _iface_id: u16) -> Result<(), Error> {
unimplemented!()
}
async fn has_idle_client(&mut self) -> Result<bool, Error> {
unimplemented!()
}
async fn handle_added_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
unimplemented!()
}
async fn handle_removed_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
unimplemented!()
}
async fn get_sme_proxy_for_scan(&mut self) -> Result<SmeForScan, Error> {
Err(format_err!("No ifaces"))
}
async fn stop_client_connections(
&mut self,
_reason: client_types::DisconnectReason,
) -> Result<(), Error> {
unimplemented!()
}
async fn start_client_connections(&mut self) -> Result<(), Error> {
unimplemented!()
}
async fn start_ap(
&mut self,
_config: ap_fsm::ApConfig,
) -> Result<oneshot::Receiver<()>, Error> {
unimplemented!()
}
async fn stop_ap(&mut self, _ssid: types::Ssid, _password: Vec<u8>) -> Result<(), Error> {
unimplemented!()
}
async fn stop_all_aps(&mut self) -> Result<(), Error> {
unimplemented!()
}
async fn has_wpa3_capable_client(&mut self) -> Result<bool, Error> {
Ok(true)
}
async fn set_country(
&mut self,
_country_code: Option<[u8; client_types::REGION_CODE_LEN]>,
) -> Result<(), Error> {
unimplemented!()
}
}
#[fuchsia::test]
fn no_client_interface() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let iface_manager = Arc::new(Mutex::new(FakeIfaceManagerNoIfaces {}));
let serve_fut = serve_provider_requests(
iface_manager,
test_values.update_sender,
test_values.saved_networks,
test_values.scan_requester,
test_values.client_provider_lock,
test_values.requests,
test_values.telemetry_sender,
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a controller.
let (controller, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Issue connect request.
let connect_fut = controller.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request from first controller. Verify success.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::RejectedIncompatibleMode))
);
}
// Gets a saved network config with a particular SSID, security type, and credential.
// If there are more than one configs saved for the same SSID, security type, and credential,
// the function will panic.
async fn get_config(
saved_networks: Arc<dyn SavedNetworksManagerApi>,
id: NetworkIdentifier,
cred: Credential,
) -> Option<NetworkConfig> {
let mut cfgs = saved_networks
.lookup(&id)
.await
.into_iter()
.filter(|cfg| cfg.credential == cred)
.collect::<Vec<_>>();
// there should not be multiple configs with the same SSID, security type, and credential.
assert!(cfgs.len() <= 1);
cfgs.pop()
}
#[fuchsia::test]
async fn get_correct_config() {
let saved_networks = Arc::new(FakeSavedNetworksManager::new());
let network_id = NetworkIdentifier::try_from("foo", SecurityType::Wpa2).unwrap();
let cfg = NetworkConfig::new(
network_id.clone(),
Credential::Password(b"password".to_vec()),
false,
)
.expect("Failed to create network config");
assert!(saved_networks
.store(network_id.clone(), Credential::Password(b"password".to_vec()))
.await
.expect("Failed to store network config")
.is_none());
assert_eq!(
Some(cfg),
get_config(
saved_networks.clone(),
network_id,
Credential::Password(b"password".to_vec())
)
.await
);
assert_eq!(
None,
get_config(
saved_networks.clone(),
NetworkIdentifier::try_from("foo", SecurityType::Wpa2).unwrap(),
Credential::Password(b"not-saved".to_vec())
)
.await
);
}
#[fuchsia::test]
fn multiple_api_clients() {
let mut exec = fasync::TestExecutor::new();
let test_values = test_setup();
let serve_fut = serve_provider_requests(
test_values.iface_manager.clone(),
test_values.update_sender.clone(),
test_values.saved_networks.clone(),
test_values.scan_requester.clone(),
test_values.client_provider_lock.clone(),
test_values.requests,
test_values.telemetry_sender.clone(),
);
let mut serve_fut = pin!(serve_fut);
// No request has been sent yet. Future should be idle.
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Request a controller.
let (controller1, _) = request_controller(&test_values.provider);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
// Create another client provider request stream and start serving it. This is equivalent
// to the behavior that occurs when a second client connects to the ClientProvider service.
let (provider, requests) = create_proxy::<fidl_policy::ClientProviderMarker>()
.expect("failed to create ClientProvider proxy");
let requests = requests.into_stream().expect("failed to create stream");
let second_serve_fut = serve_provider_requests(
test_values.iface_manager.clone(),
test_values.update_sender.clone(),
test_values.saved_networks.clone(),
test_values.scan_requester,
test_values.client_provider_lock.clone(),
requests,
test_values.telemetry_sender,
);
let mut second_serve_fut = pin!(second_serve_fut);
// Request a client controller from the second instance of the service.
let (controller2, _) = request_controller(&provider);
assert_variant!(exec.run_until_stalled(&mut second_serve_fut), Poll::Pending);
// Verify Epitaph was received.
let mut controller2_event_stream = controller2.take_event_stream();
let controller2_event_fut = controller2_event_stream.next();
let mut controller2_event_fut = pin!(controller2_event_fut);
assert_variant!(
exec.run_until_stalled(&mut controller2_event_fut),
Poll::Ready(Some(Err(fidl::Error::ClientChannelClosed {
status: zx::Status::ALREADY_BOUND,
..
})))
);
assert!(controller2.is_closed());
// Drop the first controller and verify that the second provider client can get a
// functional client controller.
drop(controller1);
assert_variant!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
let (controller2, _) = request_controller(&provider);
assert_variant!(exec.run_until_stalled(&mut second_serve_fut), Poll::Pending);
// Ensure the new controller works by issuing a connect request.
let connect_fut = controller2.connect(&test_values.net_id_open);
let mut connect_fut = pin!(connect_fut);
// Process connect request from first controller. Verify success.
assert_variant!(exec.run_until_stalled(&mut second_serve_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut connect_fut),
Poll::Ready(Ok(fidl_common::RequestStatus::Acknowledged))
);
}
#[test_case(true)]
#[test_case(false)]
#[fuchsia::test(add_test_attr = false)]
fn test_start_client_connections(start_client_connections_succeeds: bool) {
let mut exec = fasync::TestExecutor::new();
let (proxy, _server) = create_proxy::<fidl_fuchsia_wlan_sme::ClientSmeMarker>()
.expect("failed to create ClientSmeProxy");
let (req_sender, _req_recvr) = mpsc::channel(1);
let mut iface_manager = FakeIfaceManager::new(proxy.clone(), req_sender);
// Setup the IfaceManager to succeed or fail depending on the test configuration.
iface_manager.start_client_connections_succeeds = start_client_connections_succeeds;
let iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>> =
Arc::new(Mutex::new(iface_manager));
// Regardless of what the IfaceManager is actually able to do, we expect the response to
// simply ack the caller.
let fut = handle_client_request_start_client_connections(iface_manager);
let mut fut = pin!(fut);
assert_variant!(
exec.run_until_stalled(&mut fut),
Poll::Ready(fidl_common::RequestStatus::Acknowledged)
);
}
#[test_case(true)]
#[test_case(false)]
#[fuchsia::test(add_test_attr = false)]
fn test_stop_client_connections(stop_client_connections_succeeds: bool) {
let mut exec = fasync::TestExecutor::new();
let (proxy, _server) = create_proxy::<fidl_fuchsia_wlan_sme::ClientSmeMarker>()
.expect("failed to create ClientSmeProxy");
let (req_sender, _req_recvr) = mpsc::channel(1);
let mut iface_manager = FakeIfaceManager::new(proxy.clone(), req_sender);
// Setup the IfaceManager to succeed or fail depending on the test configuration.
iface_manager.stop_client_connections_succeeds = stop_client_connections_succeeds;
let iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>> =
Arc::new(Mutex::new(iface_manager));
// Regardless of what the IfaceManager is actually able to do, we expect the response to
// simply ack the caller.
let fut = handle_client_request_stop_client_connections(iface_manager);
let mut fut = pin!(fut);
assert_variant!(
exec.run_until_stalled(&mut fut),
Poll::Ready(fidl_common::RequestStatus::Acknowledged)
);
}
}