blob: 4562d12c2ea4e9bbddb9ef3979ccfb30d4425b7f [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.
//! Manages Scan requests for the Client Policy API.
use {
crate::{
client::types,
config_management::{
select_subset_potentially_hidden_networks, SavedNetworksManagerApi, ScanResultType,
},
mode_management::iface_manager_api::IfaceManagerApi,
},
anyhow::{format_err, Error},
async_trait::async_trait,
fidl::prelude::*,
fidl_fuchsia_location_sensor as fidl_location_sensor, fidl_fuchsia_wlan_policy as fidl_policy,
fidl_fuchsia_wlan_sme as fidl_sme,
fuchsia_async::{self as fasync, DurationExt},
fuchsia_cobalt::CobaltSender,
fuchsia_component::client::connect_to_protocol,
fuchsia_zircon as zx,
futures::{lock::Mutex, prelude::*},
log::{debug, error, info, warn},
measure_tape_for_scan_result::Measurable as _,
std::{collections::HashMap, convert::TryFrom, sync::Arc},
stream::FuturesUnordered,
wlan_common,
wlan_metrics_registry::{
ActiveScanRequestedForNetworkSelectionMetricDimensionActiveScanSsidsRequested as ActiveScanSsidsRequested,
ACTIVE_SCAN_REQUESTED_FOR_NETWORK_SELECTION_METRIC_ID,
},
};
// TODO(fxbug.dev/80422): Remove this.
// Size of FIDL message header and FIDL error-wrapped vector header
const FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE: usize = 56;
// Delay between scanning retries when the firmware returns "ShouldWait" error code
const SCAN_RETRY_DELAY_MS: i64 = 100;
// Inidication of the scan caller, for use in logging caller specific metrics
pub enum ScanReason {
ClientRequest,
NetworkSelection,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
struct SmeNetworkIdentifier {
ssid: types::Ssid,
protection: types::SecurityTypeDetailed,
}
/// Allows for consumption of updated scan results.
#[async_trait]
pub trait ScanResultUpdate: Sync + Send {
async fn update_scan_results(&mut self, scan_results: &Vec<types::ScanResult>);
}
/// Requests a new SME scan and returns the results.
async fn sme_scan(
sme_proxy: &fidl_sme::ClientSmeProxy,
scan_request: fidl_sme::ScanRequest,
) -> Result<Vec<wlan_common::scan::ScanResult>, types::ScanError> {
#[derive(PartialEq, Eq)]
enum SmeScanError {
ShouldRetryLater,
Other,
}
async fn sme_scan_internal(
sme_proxy: &fidl_sme::ClientSmeProxy,
mut scan_request: fidl_sme::ScanRequest,
) -> Result<Vec<wlan_common::scan::ScanResult>, SmeScanError> {
let (local, remote) = fidl::endpoints::create_proxy().map_err(|e| {
error!("Failed to create FIDL proxy for scan: {:?}", e);
SmeScanError::Other
})?;
let txn = {
match sme_proxy.scan(&mut scan_request, remote) {
Ok(()) => local,
Err(error) => {
error!("Scan initiation error: {:?}", error);
return Err(SmeScanError::Other);
}
}
};
debug!("Sent scan request to SME successfully");
let mut stream = txn.take_event_stream();
let mut scanned_networks: Vec<wlan_common::scan::ScanResult> = vec![];
while let Some(Ok(event)) = stream.next().await {
match event {
fidl_sme::ScanTransactionEvent::OnResult { aps } => {
debug!("Received scan results from SME");
scanned_networks.extend(
aps.iter()
.filter_map(|scan_result| {
wlan_common::scan::ScanResult::try_from(scan_result.clone())
.map(|scan_result| Some(scan_result))
.unwrap_or_else(|e| {
// TODO(fxbug.dev/83708): Report details about which
// scan result failed to convert if possible.
error!("ScanResult conversion failed: {:?}", e);
None
})
})
.collect::<Vec<_>>(),
);
}
fidl_sme::ScanTransactionEvent::OnFinished {} => {
debug!("Finished getting scan results from SME");
return Ok(scanned_networks);
}
fidl_sme::ScanTransactionEvent::OnError { error } => {
return match error.code {
fidl_sme::ScanErrorCode::ShouldWait
| fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware => {
info!("Scan cancelled by SME, retry indicated: {:?}", error);
Err(SmeScanError::ShouldRetryLater)
}
_ => {
error!("Scan error from SME: {:?}", error);
Err(SmeScanError::Other)
}
}
}
};
}
// This case can happen when an interface is removed mid-scan. Any other cases are
// unexpected.
warn!("SME closed scan result channel without sending OnFinished");
Err(SmeScanError::Other)
}
match sme_scan_internal(sme_proxy, scan_request.clone()).await {
Ok(scan_results) => Ok(scan_results),
// This can be in an .or_else() once async closures are supported in stable.
// See issue #62290 <https://github.com/rust-lang/rust/issues/62290>.
Err(SmeScanError::ShouldRetryLater) => {
info!("Driver requested a delay before retrying scan");
fasync::Timer::new(zx::Duration::from_millis(SCAN_RETRY_DELAY_MS).after_now()).await;
// Retry once before returning an error.
sme_scan_internal(sme_proxy, scan_request.clone()).await.or_else(|e| {
if e == SmeScanError::ShouldRetryLater {
Err(types::ScanError::Cancelled)
} else {
Err(types::ScanError::GeneralError)
}
})
}
Err(_) => Err(types::ScanError::GeneralError),
}
}
/// Handles incoming scan requests by creating a new SME scan request.
/// For the output_iterator, returns scan results and/or errors.
/// On successful scan, also provides scan results to:
/// - Emergency Location Provider
/// - Network Selection Module
pub(crate) async fn perform_scan(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
saved_networks_manager: Arc<dyn SavedNetworksManagerApi>,
mut output_iterator: Option<fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>>,
mut network_selector: impl ScanResultUpdate,
mut location_sensor_updater: impl ScanResultUpdate,
scan_reason: ScanReason,
// TODO(fxbug.dev/73821): This should be removed when ScanManager struct is implemented,
// in favor of a field in the struct itself.
cobalt_api: Option<Arc<Mutex<CobaltSender>>>,
) {
let mut bss_by_network: HashMap<SmeNetworkIdentifier, Vec<types::Bss>> = HashMap::new();
let sme_proxy = match iface_manager.lock().await.get_sme_proxy_for_scan().await {
Ok(proxy) => proxy,
Err(e) => {
// The attempt to get an SME proxy failed. Send an error to the requester, return early.
warn!("Failed to get an SME proxy for scan: {:?}", e);
if let Some(output_iterator) = output_iterator {
send_scan_error_over_fidl(
output_iterator,
fidl_policy::ScanErrorCode::GeneralError,
)
.await
.unwrap_or_else(|e| error!("Failed to send scan error: {}", e));
}
return;
}
};
// Perform an initial passive scan
let scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let sme_result = sme_scan(&sme_proxy, scan_request).await;
match sme_result {
Ok(results) => {
record_undirected_scan_results(&results, saved_networks_manager.clone()).await;
insert_bss_to_network_bss_map(&mut bss_by_network, results, true);
}
Err(scan_err) => {
// The passive scan failed. Send an error to the requester and return early.
if let Some(output_iterator) = output_iterator {
send_scan_error_over_fidl(output_iterator, scan_err)
.await
.unwrap_or_else(|e| error!("Failed to send scan error: {}", e));
}
return;
}
};
let requested_active_scan_ids: Vec<types::NetworkIdentifier> =
select_subset_potentially_hidden_networks(saved_networks_manager.get_networks().await);
// Record active scan decisions to metrics. This is optional, based on
// the scan reason and if the caller would like metrics logged.
if let Some(cobalt_sender) = cobalt_api {
match scan_reason {
ScanReason::NetworkSelection => {
let active_scan_request_count_metric = match requested_active_scan_ids.len() {
0 => ActiveScanSsidsRequested::Zero,
1 => ActiveScanSsidsRequested::One,
2..=4 => ActiveScanSsidsRequested::TwoToFour,
5..=10 => ActiveScanSsidsRequested::FiveToTen,
11..=20 => ActiveScanSsidsRequested::ElevenToTwenty,
21..=50 => ActiveScanSsidsRequested::TwentyOneToFifty,
51..=100 => ActiveScanSsidsRequested::FiftyOneToOneHundred,
101..=usize::MAX => ActiveScanSsidsRequested::OneHundredAndOneOrMore,
_ => unreachable!(),
};
let mut cobalt_sender_guard = cobalt_sender.lock().await;
let cobalt_lock = &mut *cobalt_sender_guard;
cobalt_lock.log_event(
ACTIVE_SCAN_REQUESTED_FOR_NETWORK_SELECTION_METRIC_ID,
active_scan_request_count_metric,
);
drop(cobalt_sender_guard);
}
_ => {}
}
}
if requested_active_scan_ids.len() > 0 {
let requested_active_scan_ssids =
requested_active_scan_ids.iter().map(|id| id.ssid.to_vec()).collect();
let scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: requested_active_scan_ssids,
channels: vec![],
});
let sme_result = sme_scan(&sme_proxy, scan_request).await;
match sme_result {
Ok(results) => {
record_directed_scan_results(
requested_active_scan_ids,
&results,
saved_networks_manager,
)
.await;
insert_bss_to_network_bss_map(&mut bss_by_network, results, false);
}
Err(scan_err) => {
// There was an error in the active scan. For the FIDL interface, send an error. We
// `.take()` the output_iterator here, so it won't be used for sending results below.
if let Some(output_iterator) = output_iterator.take() {
send_scan_error_over_fidl(output_iterator, scan_err)
.await
.unwrap_or_else(|e| error!("Failed to send scan error: {}", e));
};
info!("Proceeding with passive scan results for non-FIDL scan consumers");
}
}
};
let scan_results = network_bss_map_to_scan_result(bss_by_network);
// TODO(b/182569380): use actual wpa3 support in this conversion rather than hardcoding 'false'
let fidl_scan_results = scan_result_to_policy_scan_result(&scan_results, false);
let mut scan_result_consumers = FuturesUnordered::new();
// Send scan results to the location sensor
scan_result_consumers.push(location_sensor_updater.update_scan_results(&scan_results));
// Send scan results to the network selection module
scan_result_consumers.push(network_selector.update_scan_results(&scan_results));
// If the requester provided a channel, send the results to them
if let Some(output_iterator) = output_iterator {
let requester_fut = send_scan_results_over_fidl(output_iterator, &fidl_scan_results)
.unwrap_or_else(|e| {
error!("Failed to send scan results to requester: {:?}", e);
});
scan_result_consumers.push(Box::pin(requester_fut));
}
while let Some(_) = scan_result_consumers.next().await {}
}
/// Update the hidden network probabilties of saved networks seen in a
/// passive scan.
async fn record_undirected_scan_results(
scan_results: &Vec<wlan_common::scan::ScanResult>,
saved_networks_manager: Arc<dyn SavedNetworksManagerApi>,
) {
let ids = scan_results
.iter()
.map(|result| types::NetworkIdentifierDetailed {
ssid: result.bss_description.ssid.clone(),
security_type: result.bss_description.protection().into(),
})
.collect();
saved_networks_manager.record_scan_result(ScanResultType::Undirected, ids).await;
}
/// Perform a directed active scan for a given network on given channels.
pub(crate) async fn perform_directed_active_scan(
sme_proxy: &fidl_sme::ClientSmeProxy,
ssid: &types::Ssid,
channels: Option<Vec<u8>>,
) -> Result<Vec<types::ScanResult>, types::ScanError> {
let scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![ssid.to_vec()],
channels: channels.unwrap_or(vec![]),
});
let sme_result = sme_scan(sme_proxy, scan_request).await;
sme_result.map(|results| {
let mut bss_by_network: HashMap<SmeNetworkIdentifier, Vec<types::Bss>> = HashMap::new();
insert_bss_to_network_bss_map(&mut bss_by_network, results, false);
// The active scan targets a specific SSID, ensure only that SSID is present in results
bss_by_network.retain(|network_id, _| network_id.ssid == *ssid);
network_bss_map_to_scan_result(bss_by_network)
})
}
/// Figure out which saved networks we actively scanned for and did not get results for, and update
/// their configs to update the rate at which we would actively scan for these networks.
async fn record_directed_scan_results(
target_ids: Vec<types::NetworkIdentifier>,
scan_result_list: &Vec<wlan_common::scan::ScanResult>,
saved_networks_manager: Arc<dyn SavedNetworksManagerApi>,
) {
let ids = scan_result_list
.iter()
.map(|scan_result| types::NetworkIdentifierDetailed {
ssid: scan_result.bss_description.ssid.clone(),
security_type: scan_result.bss_description.protection().into(),
})
.collect();
saved_networks_manager.record_scan_result(ScanResultType::Directed(target_ids), ids).await;
}
/// The location sensor module uses scan results to help determine the
/// device's location, for use by the Emergency Location Provider.
pub struct LocationSensorUpdater {
pub wpa3_supported: bool,
}
#[async_trait]
impl ScanResultUpdate for LocationSensorUpdater {
async fn update_scan_results(&mut self, scan_results: &Vec<types::ScanResult>) {
async fn send_results(scan_results: &Vec<fidl_policy::ScanResult>) -> Result<(), Error> {
// Get an output iterator
let (iter, server) =
fidl::endpoints::create_endpoints::<fidl_policy::ScanResultIteratorMarker>()
.map_err(|err| format_err!("failed to create iterator: {:?}", err))?;
let location_watcher_proxy =
connect_to_protocol::<fidl_location_sensor::WlanBaseStationWatcherMarker>()
.map_err(|err| {
format_err!("failed to connect to location sensor service: {:?}", err)
})?;
location_watcher_proxy
.report_current_stations(iter)
.map_err(|err| format_err!("failed to call location sensor service: {:?}", err))?;
// Send results to the iterator
send_scan_results_over_fidl(server, &scan_results).await
}
let scan_results = scan_result_to_policy_scan_result(scan_results, self.wpa3_supported);
// Filter out any errors and just log a message.
// No error recovery, we'll just try again next time a scan result comes in.
if let Err(e) = send_results(&scan_results).await {
// TODO(fxbug.dev/52700) Upgrade this to a "warn!" once the location sensor works.
debug!("Failed to send scan results to location sensor: {:?}", e)
} else {
debug!("Updated location sensor")
};
}
}
/// Converts sme::ScanResult to our internal BSS type, then adds it to the provided bss_by_network map.
/// Only keeps the first unique instance of a BSSID
fn insert_bss_to_network_bss_map(
bss_by_network: &mut HashMap<SmeNetworkIdentifier, Vec<types::Bss>>,
scan_result_list: Vec<wlan_common::scan::ScanResult>,
observed_in_passive_scan: bool,
) {
for scan_result in scan_result_list.into_iter() {
let entry = bss_by_network
.entry(SmeNetworkIdentifier {
ssid: scan_result.bss_description.ssid.clone(),
protection: scan_result.bss_description.protection().into(),
})
.or_insert(vec![]);
// Check if this BSSID is already in the hashmap
if !entry.iter().any(|existing_bss| existing_bss.bssid == scan_result.bss_description.bssid)
{
entry.push(types::Bss {
bssid: scan_result.bss_description.bssid,
rssi: scan_result.bss_description.rssi_dbm,
snr_db: scan_result.bss_description.snr_db,
channel: scan_result.bss_description.channel.into(),
timestamp: scan_result.timestamp,
observation: if observed_in_passive_scan {
types::ScanObservation::Passive
} else {
types::ScanObservation::Active
},
compatible: scan_result.compatible,
bss_description: scan_result.bss_description.into(),
});
};
}
}
fn network_bss_map_to_scan_result(
mut bss_by_network: HashMap<SmeNetworkIdentifier, Vec<types::Bss>>,
) -> Vec<types::ScanResult> {
let mut scan_results: Vec<types::ScanResult> = bss_by_network
.drain()
.map(|(SmeNetworkIdentifier { ssid, protection }, bss_entries)| {
let compatibility = if bss_entries.iter().any(|bss| bss.compatible) {
fidl_policy::Compatibility::Supported
} else {
fidl_policy::Compatibility::DisallowedNotSupported
};
types::ScanResult {
ssid: ssid,
security_type_detailed: protection,
entries: bss_entries,
compatibility: compatibility,
}
})
.collect();
scan_results.sort_by(|a, b| a.ssid.cmp(&b.ssid));
return scan_results;
}
/// Convert the protection type we receive from the SME in scan results to the Policy layer
/// security type. This function should only be used when converting to results for the public
/// FIDL API, and not for internal use within Policy, where we should prefer the detailed SME
/// security types.
fn fidl_security_from_sme_protection(
protection: fidl_sme::Protection,
wpa3_supported: bool,
) -> Option<fidl_policy::SecurityType> {
use fidl_policy::SecurityType;
use fidl_sme::Protection::*;
match protection {
Wpa3Enterprise | Wpa3Personal | Wpa2Wpa3Personal => {
Some(if wpa3_supported { SecurityType::Wpa3 } else { SecurityType::Wpa2 })
}
Wpa2Enterprise
| Wpa2Personal
| Wpa1Wpa2Personal
| Wpa2PersonalTkipOnly
| Wpa1Wpa2PersonalTkipOnly => Some(SecurityType::Wpa2),
Wpa1 => Some(SecurityType::Wpa),
Wep => Some(SecurityType::Wep),
Open => Some(SecurityType::None),
Unknown => None,
}
}
fn scan_result_to_policy_scan_result(
internal_results: &Vec<types::ScanResult>,
wpa3_supported: bool,
) -> Vec<fidl_policy::ScanResult> {
let scan_results: Vec<fidl_policy::ScanResult> = internal_results
.iter()
.filter_map(|internal| {
if let Some(security) =
fidl_security_from_sme_protection(internal.security_type_detailed, wpa3_supported)
{
Some(fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: internal.ssid.to_vec(),
type_: security,
}),
entries: Some(
internal
.entries
.iter()
.map(|input| {
// Get the frequency. On error, default to Some(0) rather than None
// to protect against consumer code that expects this field to
// always be set.
let frequency = types::WlanChan::from(input.channel)
.get_center_freq()
.unwrap_or(0);
fidl_policy::Bss {
bssid: Some(input.bssid.0),
rssi: Some(input.rssi),
frequency: Some(frequency.into()), // u16.into() -> u32
timestamp_nanos: Some(input.timestamp.into_nanos()),
..fidl_policy::Bss::EMPTY
}
})
.collect(),
),
compatibility: if internal.entries.iter().any(|bss| bss.compatible) {
Some(fidl_policy::Compatibility::Supported)
} else {
Some(fidl_policy::Compatibility::DisallowedNotSupported)
},
..fidl_policy::ScanResult::EMPTY
})
} else {
debug!(
"Unknown security type present in scan results: {:?}",
internal.security_type_detailed
);
None
}
})
.collect();
return scan_results;
}
/// Send batches of results to the output iterator when getNext() is called on it.
/// Send empty batch and close the channel when no results are remaining.
async fn send_scan_results_over_fidl(
output_iterator: fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>,
scan_results: &Vec<fidl_policy::ScanResult>,
) -> Result<(), Error> {
// Wait to get a request for a chunk of scan results
let (mut stream, ctrl) = output_iterator.into_stream_and_control_handle()?;
let max_batch_size = zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize;
let mut sent_some_results = false;
let mut remaining_results = scan_results.iter().peekable();
let mut batch_size: usize;
// Verify consumer is expecting results before each batch
loop {
if let Some(fidl_policy::ScanResultIteratorRequest::GetNext { responder }) =
stream.try_next().await?
{
sent_some_results = true;
let mut batch = vec![];
batch_size = FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE;
// Peek if next element exists and will fit in current batch
while let Some(peeked_result) = remaining_results.peek() {
let result_size = peeked_result.measure().num_bytes;
if result_size + FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE > max_batch_size {
return Err(format_err!("Single scan result too large to send via FIDL"));
}
// Peeked result will not fit. Send batch and continue.
if result_size + batch_size > max_batch_size {
break;
}
// Actually remove result and push to batch
if let Some(result) = remaining_results.next() {
batch.push(result.clone());
batch_size += result_size;
}
}
let close_channel = batch.is_empty();
responder.send(&mut Ok(batch))?;
// Guarantees empty batch is sent before channel is closed.
if close_channel {
ctrl.shutdown();
return Ok(());
}
} else {
// This will happen if the iterator request stream was closed and we expected to send
// another response.
if sent_some_results {
// Some consumers may not care about all scan results, e.g. if they find the
// particular network they were looking for. This is not an error.
debug!("Scan result consumer closed channel before consuming all scan results");
return Ok(());
} else {
return Err(format_err!("Peer closed channel before receiving any scan results"));
}
}
}
}
/// On the next request for results, send an error to the output iterator and
/// shut it down.
async fn send_scan_error_over_fidl(
output_iterator: fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>,
error_code: fidl_policy::ScanErrorCode,
) -> Result<(), fidl::Error> {
// Wait to get a request for a chunk of scan results
let (mut stream, ctrl) = output_iterator.into_stream_and_control_handle()?;
if let Some(req) = stream.try_next().await? {
let fidl_policy::ScanResultIteratorRequest::GetNext { responder } = req;
let mut err: fidl_policy::ScanResultIteratorGetNextResult = Err(error_code);
responder.send(&mut err)?;
ctrl.shutdown();
} else {
// This will happen if the iterator request stream was closed and we expected to send
// another response.
info!("Peer closed channel for getting scan results unexpectedly");
}
Ok(())
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{
access_point::state_machine as ap_fsm,
config_management::network_config::Credential,
util::testing::{
fakes::FakeSavedNetworksManager, generate_random_sme_scan_result,
validate_sme_scan_request_and_send_results,
},
},
anyhow::Error,
fidl::endpoints::{create_proxy, Proxy},
fuchsia_async as fasync, fuchsia_zircon as zx,
futures::{channel::oneshot, lock::Mutex, task::Poll},
pin_utils::pin_mut,
std::{convert::TryInto, sync::Arc},
test_case::test_case,
wlan_common::{assert_variant, random_fidl_bss_description},
};
const CENTER_FREQ_CHAN_1: u32 = 2412;
const CENTER_FREQ_CHAN_8: u32 = 2447;
const CENTER_FREQ_CHAN_11: u32 = 2462;
struct FakeIfaceManager {
pub sme_proxy: fidl_fuchsia_wlan_sme::ClientSmeProxy,
pub wpa3_capable: bool,
}
impl FakeIfaceManager {
pub fn new(proxy: fidl_fuchsia_wlan_sme::ClientSmeProxy) -> Self {
FakeIfaceManager { sme_proxy: proxy, wpa3_capable: true }
}
}
#[async_trait]
impl IfaceManagerApi for FakeIfaceManager {
async fn disconnect(
&mut self,
_network_id: types::NetworkIdentifier,
_reason: types::DisconnectReason,
) -> Result<(), Error> {
unimplemented!()
}
async fn connect(
&mut self,
_connect_req: types::ConnectRequest,
) -> Result<oneshot::Receiver<()>, Error> {
unimplemented!()
}
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 scan(
&mut self,
mut scan_request: fidl_sme::ScanRequest,
) -> Result<fidl_fuchsia_wlan_sme::ScanTransactionProxy, Error> {
let (local, remote) = fidl::endpoints::create_proxy()?;
let _ = self.sme_proxy.scan(&mut scan_request, remote);
Ok(local)
}
async fn get_sme_proxy_for_scan(
&mut self,
) -> Result<fidl_fuchsia_wlan_sme::ClientSmeProxy, Error> {
Ok(self.sme_proxy.clone())
}
async fn stop_client_connections(
&mut self,
_reason: 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(self.wpa3_capable)
}
async fn set_country(
&mut self,
_country_code: Option<[u8; types::REGION_CODE_LEN]>,
) -> Result<(), Error> {
unimplemented!()
}
}
/// Creates a Client wrapper.
async fn create_iface_manager(
) -> (Arc<Mutex<FakeIfaceManager>>, fidl_sme::ClientSmeRequestStream) {
let (client_sme, remote) =
create_proxy::<fidl_sme::ClientSmeMarker>().expect("error creating proxy");
let iface_manager = Arc::new(Mutex::new(FakeIfaceManager::new(client_sme)));
(iface_manager, remote.into_stream().expect("failed to create stream"))
}
/// Creates an SME proxy for tests.
async fn create_sme_proxy() -> (fidl_sme::ClientSmeProxy, fidl_sme::ClientSmeRequestStream) {
let (client_sme, remote) =
create_proxy::<fidl_sme::ClientSmeMarker>().expect("error creating proxy");
(client_sme, remote.into_stream().expect("failed to create stream"))
}
struct MockScanResultConsumer {
scan_results: Arc<Mutex<Option<Vec<types::ScanResult>>>>,
}
impl MockScanResultConsumer {
fn new() -> (Self, Arc<Mutex<Option<Vec<types::ScanResult>>>>) {
let scan_results = Arc::new(Mutex::new(None));
(Self { scan_results: Arc::clone(&scan_results) }, scan_results)
}
}
#[async_trait]
impl ScanResultUpdate for MockScanResultConsumer {
async fn update_scan_results(&mut self, scan_results: &Vec<types::ScanResult>) {
let mut guard = self.scan_results.lock().await;
*guard = Some(scan_results.clone());
}
}
// Creates test data for the scan functions.
struct MockScanData {
passive_input_aps: Vec<fidl_sme::ScanResult>,
passive_internal_aps: Vec<types::ScanResult>,
passive_fidl_aps: Vec<fidl_policy::ScanResult>,
active_input_aps: Vec<fidl_sme::ScanResult>,
combined_internal_aps: Vec<types::ScanResult>,
combined_fidl_aps: Vec<fidl_policy::ScanResult>,
}
fn create_scan_ap_data() -> MockScanData {
let passive_result_1 = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [0, 0, 0, 0, 0, 0],
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
rssi_dbm: 0,
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
),
};
let passive_result_2 = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa2,
bssid: [1, 2, 3, 4, 5, 6],
ssid: types::Ssid::try_from("unique ssid").unwrap(),
rssi_dbm: 7,
snr_db: 2,
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
),
};
let passive_result_3 = fidl_sme::ScanResult {
compatible: false,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [7, 8, 9, 10, 11, 12],
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
rssi_dbm: 13,
snr_db: 3,
channel: types::WlanChan::new(11, types::Cbw::Cbw20),
),
};
let passive_input_aps =
vec![passive_result_1.clone(), passive_result_2.clone(), passive_result_3.clone()];
// input_aps contains some duplicate SSIDs, which should be
// grouped in the output.
let passive_internal_aps = vec![
types::ScanResult {
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![
types::Bss {
bssid: types::Bssid([0, 0, 0, 0, 0, 0]),
rssi: 0,
timestamp: zx::Time::from_nanos(passive_result_1.timestamp_nanos),
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result_1.bss_description.clone(),
},
types::Bss {
bssid: types::Bssid([7, 8, 9, 10, 11, 12]),
rssi: 13,
timestamp: zx::Time::from_nanos(passive_result_3.timestamp_nanos),
snr_db: 3,
channel: types::WlanChan::new(11, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: false,
bss_description: passive_result_3.bss_description.clone(),
},
],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: types::Ssid::try_from("unique ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: types::Bssid([1, 2, 3, 4, 5, 6]),
rssi: 7,
timestamp: zx::Time::from_nanos(passive_result_2.timestamp_nanos),
snr_db: 2,
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result_2.bss_description.clone(),
}],
compatibility: types::Compatibility::Supported,
},
];
let passive_fidl_aps = vec![
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("duplicated ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![
fidl_policy::Bss {
bssid: Some([0, 0, 0, 0, 0, 0]),
rssi: Some(0),
frequency: Some(CENTER_FREQ_CHAN_1),
timestamp_nanos: Some(passive_result_1.timestamp_nanos),
..fidl_policy::Bss::EMPTY
},
fidl_policy::Bss {
bssid: Some([7, 8, 9, 10, 11, 12]),
rssi: Some(13),
frequency: Some(CENTER_FREQ_CHAN_11),
timestamp_nanos: Some(passive_result_3.timestamp_nanos),
..fidl_policy::Bss::EMPTY
},
]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("unique ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some([1, 2, 3, 4, 5, 6]),
rssi: Some(7),
frequency: Some(CENTER_FREQ_CHAN_8),
timestamp_nanos: Some(passive_result_2.timestamp_nanos),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
];
let active_result_1 = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [9, 9, 9, 9, 9, 9],
ssid: types::Ssid::try_from("foo active ssid").unwrap(),
rssi_dbm: 0,
snr_db: 8,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
),
};
let active_result_2 = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa2,
bssid: [8, 8, 8, 8, 8, 8],
ssid: types::Ssid::try_from("misc ssid").unwrap(),
rssi_dbm: 7,
snr_db: 9,
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
),
};
let active_input_aps = vec![active_result_1.clone(), active_result_2.clone()];
let combined_internal_aps = vec![
types::ScanResult {
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![
types::Bss {
bssid: types::Bssid([0, 0, 0, 0, 0, 0]),
rssi: 0,
timestamp: zx::Time::from_nanos(passive_result_1.timestamp_nanos),
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result_1.bss_description.clone(),
},
types::Bss {
bssid: types::Bssid([7, 8, 9, 10, 11, 12]),
rssi: 13,
timestamp: zx::Time::from_nanos(passive_result_3.timestamp_nanos),
snr_db: 3,
channel: types::WlanChan::new(11, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: false,
bss_description: passive_result_3.bss_description.clone(),
},
],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: types::Ssid::try_from("foo active ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![types::Bss {
bssid: types::Bssid([9, 9, 9, 9, 9, 9]),
rssi: 0,
timestamp: zx::Time::from_nanos(active_result_1.timestamp_nanos),
snr_db: 8,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
observation: types::ScanObservation::Active,
compatible: true,
bss_description: active_result_1.bss_description.clone(),
}],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: types::Ssid::try_from("misc ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: types::Bssid([8, 8, 8, 8, 8, 8]),
rssi: 7,
timestamp: zx::Time::from_nanos(active_result_2.timestamp_nanos),
snr_db: 9,
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
observation: types::ScanObservation::Active,
compatible: true,
bss_description: active_result_2.bss_description.clone(),
}],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: types::Ssid::try_from("unique ssid").unwrap(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: types::Bssid([1, 2, 3, 4, 5, 6]),
rssi: 7,
timestamp: zx::Time::from_nanos(passive_result_2.timestamp_nanos),
snr_db: 2,
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result_2.bss_description.clone(),
}],
compatibility: types::Compatibility::Supported,
},
];
let combined_fidl_aps = vec![
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("duplicated ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![
fidl_policy::Bss {
bssid: Some([0, 0, 0, 0, 0, 0]),
rssi: Some(0),
frequency: Some(CENTER_FREQ_CHAN_1),
timestamp_nanos: Some(passive_result_1.timestamp_nanos),
..fidl_policy::Bss::EMPTY
},
fidl_policy::Bss {
bssid: Some([7, 8, 9, 10, 11, 12]),
rssi: Some(13),
frequency: Some(CENTER_FREQ_CHAN_11),
timestamp_nanos: Some(passive_result_3.timestamp_nanos),
..fidl_policy::Bss::EMPTY
},
]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("foo active ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some([9, 9, 9, 9, 9, 9]),
rssi: Some(0),
frequency: Some(CENTER_FREQ_CHAN_1),
timestamp_nanos: Some(active_result_1.timestamp_nanos),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("misc ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some([8, 8, 8, 8, 8, 8]),
rssi: Some(7),
frequency: Some(CENTER_FREQ_CHAN_8),
timestamp_nanos: Some(active_result_2.timestamp_nanos),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::try_from("unique ssid").unwrap().into(),
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some([1, 2, 3, 4, 5, 6]),
rssi: Some(7),
frequency: Some(CENTER_FREQ_CHAN_8),
timestamp_nanos: Some(passive_result_2.timestamp_nanos),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
];
MockScanData {
passive_input_aps,
passive_internal_aps,
passive_fidl_aps,
active_input_aps,
combined_internal_aps,
combined_fidl_aps,
}
}
/// Generate a vector of FIDL scan results, each sized based on the input
/// vector parameter. Size, in bytes, must be greater than the baseline scan
/// result's size, measure below, and divisible into octets (by 8).
fn create_fidl_scan_results_from_size(
result_sizes: Vec<usize>,
) -> Vec<fidl_policy::ScanResult> {
// Create a baseline result
let minimal_scan_result = fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: types::Ssid::empty().into(),
type_: fidl_policy::SecurityType::None,
}),
entries: Some(vec![]),
..fidl_policy::ScanResult::EMPTY
};
let minimal_result_size: usize = minimal_scan_result.measure().num_bytes;
// Create result with single entry
let mut scan_result_with_one_bss = minimal_scan_result.clone();
scan_result_with_one_bss.entries = Some(vec![fidl_policy::Bss::EMPTY]);
// Size of each additional BSS entry to FIDL ScanResult
let empty_bss_entry_size: usize =
scan_result_with_one_bss.measure().num_bytes - minimal_result_size;
// Validate size is possible
if result_sizes.iter().any(|size| size < &minimal_result_size || size % 8 != 0) {
panic!("Invalid size. Requested size must be larger than {} minimum bytes and divisible into octets (by 8)", minimal_result_size);
}
let mut fidl_scan_results = vec![];
for size in result_sizes {
let mut scan_result = minimal_scan_result.clone();
let num_bss_for_ap = (size - minimal_result_size) / empty_bss_entry_size;
// Every 8 characters for SSID adds 8 bytes (1 octet).
let ssid_length =
(size - minimal_result_size) - (num_bss_for_ap * empty_bss_entry_size);
scan_result.id = Some(fidl_policy::NetworkIdentifier {
ssid: (0..ssid_length).map(|_| rand::random::<u8>()).collect(),
type_: fidl_policy::SecurityType::None,
});
scan_result.entries = Some(vec![fidl_policy::Bss::EMPTY; num_bss_for_ap]);
// Validate result measures to expected size.
assert_eq!(scan_result.measure().num_bytes, size);
fidl_scan_results.push(scan_result);
}
fidl_scan_results
}
#[fuchsia::test]
fn sme_scan_with_passive_request() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let scan_fut = sme_scan(&sme_proxy, scan_request.clone());
pin_mut!(scan_fut);
// Request scan data from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: _,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Validate the SME received the scan_request and send back mock data
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&scan_request,
input_aps.clone(),
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let scan_results: Vec<fidl_sme::ScanResult> = result.expect("failed to get scan results")
.iter().map(|r| r.clone().into()).collect();
assert_eq!(scan_results, input_aps);
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[fuchsia::test]
fn sme_scan_with_active_request() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![
types::Ssid::try_from("foo_ssid").unwrap().into(),
types::Ssid::try_from("bar_ssid").unwrap().into(),
],
channels: vec![1, 20],
});
let scan_fut = sme_scan(&sme_proxy, scan_request.clone());
pin_mut!(scan_fut);
// Request scan data from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: _,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Validate the SME received the scan_request and send back mock data
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&scan_request,
input_aps.clone(),
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let scan_results: Vec<fidl_sme::ScanResult> = result.expect("failed to get scan results")
.iter().map(|r| r.clone().into()).collect();
assert_eq!(scan_results, input_aps);
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[test_case(fidl_sme::ScanErrorCode::InternalError; "SME scan error InternalError")]
#[test_case(fidl_sme::ScanErrorCode::NotSupported; "SME scan error NotSupported")]
#[fuchsia::test(add_test_attr = false)]
fn sme_scan_error(error_code: fidl_sme::ScanErrorCode) {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let scan_fut = sme_scan(&sme_proxy, scan_request.clone());
pin_mut!(scan_fut);
// Request scan data from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check that a scan request was sent to the sme and send back an error
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send failed scan response.
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
ctrl.send_on_error(&mut fidl_sme::ScanError {
code: error_code,
message: "Failed to scan".to_string()
})
.expect("failed to send scan error");
}
);
// No retry expected, check for results on the scan request
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let error = result.expect_err("did not expect to receive scan results");
assert_eq!(error, types::ScanError::GeneralError);
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[test_case(fidl_sme::ScanErrorCode::ShouldWait, false; "SME scan error ShouldWait with failed retry")]
#[test_case(fidl_sme::ScanErrorCode::ShouldWait, true; "SME scan error ShouldWait with successful retry")]
#[test_case(fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware, true; "SME scan error CanceledByDriverOrFirmware with successful retry")]
#[fuchsia::test(add_test_attr = false)]
fn sme_scan_error_with_retry(error_code: fidl_sme::ScanErrorCode, retry_succeeds: bool) {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let scan_fut = sme_scan(&sme_proxy, scan_request.clone());
pin_mut!(scan_fut);
// Request scan data from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check that a scan request was sent to the sme and send back an error
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send failed scan response.
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
ctrl.send_on_error(&mut fidl_sme::ScanError {
code: error_code,
message: "Failed to scan".to_string()
})
.expect("failed to send scan error");
}
);
// Advance the scanning future
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// There shouldn't yet be a request in the SME stream, since there should be
// some delay before the scan is retried
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
// Wake up the timer and advance the scanning future
assert!(exec.wake_next_timer().is_some());
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Expect a retry: we should now see a new request in the SME stream
if retry_succeeds {
// Validate the SME received the scan_request and send back mock data
let aps = vec![];
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&scan_request,
aps,
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
assert_eq!(result, Ok(vec![]));
});
} else {
// Check that a scan request was sent to the sme and send back an error
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send failed scan response.
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
ctrl.send_on_error(&mut fidl_sme::ScanError {
code: error_code,
message: "Failed to scan".to_string()
})
.expect("failed to send scan error");
}
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let error = result.expect_err("did not expect scan results");
assert_eq!(error, types::ScanError::Cancelled);
});
}
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[fuchsia::test]
fn sme_scan_channel_closed() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let scan_fut = sme_scan(&sme_proxy, scan_request);
pin_mut!(scan_fut);
// Request scan data from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check that a scan request was sent to the sme and send back an error
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send failed scan response.
txn.close_with_epitaph(zx::Status::OK).expect("Failed to close channel");
}
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let error = result.expect_err("did not expect scan results");
assert_eq!(error, types::ScanError::GeneralError);
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[fuchsia::test]
fn basic_scan() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, network_selector_results) = MockScanResultConsumer::new();
let (location_sensor, location_sensor_results) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: internal_aps,
passive_fidl_aps: fidl_aps,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check for results
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, fidl_aps);
});
// Request the next chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
// Process scan handler
// Note: this will be Poll::Ready because the scan handler will exit after sending the final
// scan results.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(()));
// Check for results
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, vec![]);
});
// Check both successful scan consumers got results
assert_eq!(
*exec.run_singlethreaded(network_selector_results.lock()),
Some(internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results.lock()),
Some(internal_aps.clone())
);
}
/// Verify that only a passive scan occurs if all saved networks have a 0
/// hidden network probability.
#[fuchsia::test]
fn passive_scan_only_with_zero_hidden_network_probabilities() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, _) = MockScanResultConsumer::new();
let (location_sensor, _) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Create passive scan info
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: _,
passive_fidl_aps: fidl_aps,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Save a network and set its hidden probability to 0
let network_id = types::NetworkIdentifier {
ssid: types::Ssid::try_from("some ssid").unwrap(),
security_type: types::SecurityType::Wpa3,
};
assert!(exec
.run_singlethreaded(
saved_networks_manager
.store(network_id.clone().into(), Credential::Password(b"randompass".to_vec()))
)
.expect("failed to store network")
.is_none());
exec.run_singlethreaded(
saved_networks_manager.update_hidden_prob(network_id.clone().into(), 0.0),
);
let config = exec
.run_singlethreaded(saved_networks_manager.lookup(&network_id.clone().into()))
.pop()
.expect("failed to lookup");
assert_eq!(config.hidden_probability, 0.0);
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager.clone(),
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check for results, verifying no active scan was requested.
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, fidl_aps);
});
}
/// Verify that saved networks seen in passive scans have their hidden network probabilities updated.
#[fuchsia::test]
fn passive_scan_updates_hidden_network_probabilities() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, _) = MockScanResultConsumer::new();
let (location_sensor, _) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Create the passive scan info
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: _,
passive_fidl_aps,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Save a network that WILL be seen in the passive scan.
let seen_in_passive_network =
passive_fidl_aps[0].id.as_ref().expect("failed to get net id");
assert!(exec
.run_singlethreaded(saved_networks_manager.store(
seen_in_passive_network.clone().into(),
Credential::Password(b"randompass".to_vec())
))
.expect("failed to store network")
.is_none());
// Save a network that will NOT be seen in the passive scan.
let not_seen_net_id = types::NetworkIdentifier {
ssid: types::Ssid::try_from("not_seen_net_id").unwrap(),
security_type: types::SecurityType::Wpa,
};
assert!(exec
.run_singlethreaded(
saved_networks_manager.store(
not_seen_net_id.clone().into(),
Credential::Password(b"foobarbaz".to_vec())
)
)
.expect("failed to store network")
.is_none());
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager.clone(),
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Verify that the passive scan results were recorded.
assert!(
*exec.run_singlethreaded(saved_networks_manager.passive_scan_result_recorded.lock())
);
// Note: the decision to active scan is non-deterministic (using the hidden network probabilities),
// no need to continue and verify the results in this test case.
}
/// Verify that an active scan is occurs when there is a saved network with a 1.0
/// hidden network probability, and that the probability is decreased when it
/// goes unseen.
#[fuchsia::test]
fn active_scan_due_to_high_hidden_network_probabilities() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, _) = MockScanResultConsumer::new();
let (location_sensor, _) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Create the passive scan info
let MockScanData {
passive_input_aps,
passive_internal_aps: _,
passive_fidl_aps,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Save a network that will NOT be seen in the either scan and set
// its initial hidden network probability to 1.0
let unseen_ssid = types::Ssid::try_from("some ssid").unwrap();
let unseen_network = types::NetworkIdentifier {
ssid: unseen_ssid.clone(),
security_type: types::SecurityType::Wpa3,
};
assert!(exec
.run_singlethreaded(
saved_networks_manager.store(
unseen_network.clone().into(),
Credential::Password(b"randompass".to_vec())
)
)
.expect("failed to store network")
.is_none());
exec.run_singlethreaded(
saved_networks_manager.update_hidden_prob(unseen_network.clone().into(), 1.0),
);
let config = exec
.run_singlethreaded(saved_networks_manager.lookup(&unseen_network.clone().into()))
.pop()
.expect("failed to lookup");
assert_eq!(config.hidden_probability, 1.0);
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager.clone(),
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
passive_input_aps.clone(),
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check iterator is still waiting results, since there should be an
// active scan still to come.
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Verify record passive scan result was called.
assert!(
*exec.run_singlethreaded(saved_networks_manager.passive_scan_result_recorded.lock())
);
// Create mock active scan data. This should verify that an active scan was
// issues based on the hidden network probabilties.
let expected_scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![unseen_ssid.to_vec()],
channels: vec![],
});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
vec![],
);
// Process SME result
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Get results from scans. Results should just be the passive results.
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("failed to get scan results").unwrap();
assert_eq!(results, passive_fidl_aps);
});
// Verify that active scan results were recorded.
assert!(*exec.run_singlethreaded(saved_networks_manager.active_scan_result_recorded.lock()));
}
#[fuchsia::test]
fn insert_bss_to_network_bss_map_duplicated_bss() {
let mut bss_by_network = HashMap::new();
// Create some input data with duplicated BSSID and Network Identifiers
let passive_result = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [0, 0, 0, 0, 0, 0],
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
rssi_dbm: 0,
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
),
};
let passive_input_aps = vec![
passive_result.clone(),
fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [0, 0, 0, 0, 0, 0],
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
rssi_dbm: 13,
snr_db: 3,
channel: types::WlanChan::new(14, types::Cbw::Cbw20),
),
},
];
let expected_id = SmeNetworkIdentifier {
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
protection: fidl_sme::Protection::Wpa3Enterprise,
};
// We should only see one entry for the duplicated BSSs in the passive scan results
let expected_bss = vec![types::Bss {
bssid: types::Bssid([0, 0, 0, 0, 0, 0]),
rssi: 0,
timestamp: zx::Time::from_nanos(passive_result.timestamp_nanos),
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result.bss_description.clone(),
}];
insert_bss_to_network_bss_map(
&mut bss_by_network,
passive_input_aps
.iter()
.map(|scan_result| {
scan_result.clone().try_into().expect("Failed to convert ScanResult")
})
.collect::<Vec<wlan_common::scan::ScanResult>>(),
true,
);
assert_eq!(bss_by_network.len(), 1);
assert_eq!(bss_by_network[&expected_id], expected_bss);
// Create some input data with one duplicate BSSID and one new BSSID
let active_result = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
bssid: [1, 2, 3, 4, 5, 6],
rssi_dbm: 101,
snr_db: 101,
channel: types::WlanChan::new(101, types::Cbw::Cbw40),
),
};
let active_input_aps = vec![
fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa3Enterprise,
bssid: [0, 0, 0, 0, 0, 0],
ssid: types::Ssid::try_from("duplicated ssid").unwrap(),
rssi_dbm: 100,
snr_db: 100,
channel: types::WlanChan::new(100, types::Cbw::Cbw40),
),
},
active_result.clone(),
];
// After the active scan, there should be a second bss included in the results
let expected_bss = vec![
types::Bss {
bssid: types::Bssid([0, 0, 0, 0, 0, 0]),
rssi: 0,
timestamp: zx::Time::from_nanos(passive_result.timestamp_nanos),
snr_db: 1,
channel: types::WlanChan::new(1, types::Cbw::Cbw20),
observation: types::ScanObservation::Passive,
compatible: true,
bss_description: passive_result.bss_description.clone(),
},
types::Bss {
bssid: types::Bssid([1, 2, 3, 4, 5, 6]),
rssi: 101,
timestamp: zx::Time::from_nanos(active_result.timestamp_nanos),
snr_db: 101,
channel: types::WlanChan::new(101, types::Cbw::Cbw40),
observation: types::ScanObservation::Active,
compatible: true,
bss_description: active_result.bss_description.clone(),
},
];
insert_bss_to_network_bss_map(
&mut bss_by_network,
active_input_aps
.iter()
.map(|scan_result| {
scan_result.clone().try_into().expect("Failed to convert ScanResult")
})
.collect::<Vec<wlan_common::scan::ScanResult>>(),
false,
);
assert_eq!(bss_by_network.len(), 1);
assert_eq!(bss_by_network[&expected_id], expected_bss);
}
#[fuchsia::test]
fn scan_with_active_scan_failure() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, network_selector_results) = MockScanResultConsumer::new();
let (location_sensor, location_sensor_results) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Create the passive and active scan info
let MockScanData {
passive_input_aps,
passive_internal_aps,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
// Save a network with hidden probability 1.0, which will guarantee an
// active scan takes place
let unseen_ssid = types::Ssid::try_from("foobarbaz ssid").unwrap();
let unseen_network = types::NetworkIdentifier {
ssid: unseen_ssid.clone(),
security_type: types::SecurityType::Wpa3,
};
assert!(exec
.run_singlethreaded(
saved_networks_manager.store(
unseen_network.clone().into(),
Credential::Password(b"randompass".to_vec())
)
)
.expect("failed to store network")
.is_none());
exec.run_singlethreaded(
saved_networks_manager.update_hidden_prob(unseen_network.clone().into(), 1.0),
);
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Respond to the first (passive) scan request
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
passive_input_aps.clone(),
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check that a scan request was sent to the sme and send back an error
let expected_scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![unseen_ssid.to_vec()],
channels: vec![],
});
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, req, ..
}))) => {
assert_eq!(req, expected_scan_request);
// Send failed scan response.
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
ctrl.send_on_error(&mut fidl_sme::ScanError {
code: fidl_sme::ScanErrorCode::InternalError,
message: "Failed to scan".to_string()
})
.expect("failed to send scan error");
}
);
// Process scan handler
// Note: this will be Poll::Ready because the scan handler will exit after sending the final
// scan results.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(()));
// Check the FIDL result -- this should be an error, since the active scan failed
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let result = result.expect("Failed to get next scan results").unwrap_err();
assert_eq!(result, fidl_policy::ScanErrorCode::GeneralError);
});
// Check both scan consumers got just the passive scan results, since the active scan failed
assert_eq!(
*exec.run_singlethreaded(network_selector_results.lock()),
Some(passive_internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results.lock()),
Some(passive_internal_aps.clone())
);
}
#[fuchsia::test]
fn scan_iterator_never_polled() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector1, network_selector_results1) = MockScanResultConsumer::new();
let (location_sensor1, location_sensor_results1) = MockScanResultConsumer::new();
let (network_selector2, network_selector_results2) = MockScanResultConsumer::new();
let (location_sensor2, location_sensor_results2) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Issue request to scan.
let (_iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client.clone(),
saved_networks_manager.clone(),
Some(iter_server),
network_selector1,
location_sensor1,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Progress scan side forward without ever calling getNext() on the scan result iterator
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: internal_aps,
passive_fidl_aps: fidl_aps,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Progress scan side forward without progressing the scan result iterator
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Issue a second request to scan, to make sure that everything is still
// moving along even though the first scan result iterator was never progressed.
let (iter2, iter_server2) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut2 = perform_scan(
client,
saved_networks_manager,
Some(iter_server2),
network_selector2,
location_sensor2,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut2);
// Progress scan side forward
assert_variant!(exec.run_until_stalled(&mut scan_fut2), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Request the results on the second iterator
let mut output_iter_fut2 = iter2.get_next();
// Progress scan side forward
assert_variant!(exec.run_until_stalled(&mut scan_fut2), Poll::Pending);
// Ensure results are present on the iterator
assert_variant!(exec.run_until_stalled(&mut output_iter_fut2), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, fidl_aps);
});
// Check all successful scan consumers got results
assert_eq!(
*exec.run_singlethreaded(network_selector_results1.lock()),
Some(internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results1.lock()),
Some(internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(network_selector_results2.lock()),
Some(internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results2.lock()),
Some(internal_aps.clone())
);
}
#[fuchsia::test]
fn scan_iterator_shut_down() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, network_selector_results) = MockScanResultConsumer::new();
let (location_sensor, location_sensor_results) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
let MockScanData {
passive_input_aps: input_aps,
passive_internal_aps: internal_aps,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
input_aps.clone(),
);
// Close the channel
drop(iter.into_channel());
// Process scan handler
// Note: this will be Poll::Ready because the scan handler will exit since all the consumers are done
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(()));
// Check both successful scan consumers got results
assert_eq!(
*exec.run_singlethreaded(network_selector_results.lock()),
Some(internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results.lock()),
Some(internal_aps.clone())
);
}
#[fuchsia::test]
fn scan_error() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector, network_selector_results) = MockScanResultConsumer::new();
let (location_sensor, location_sensor_results) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check that a scan request was sent to the sme and send back an error
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send failed scan response.
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
ctrl.send_on_error(&mut fidl_sme::ScanError {
code: fidl_sme::ScanErrorCode::InternalError,
message: "Failed to scan".to_string()
})
.expect("failed to send scan error");
}
);
// Process SME result.
// Note: this will be Poll::Ready, since the scan handler will quit after sending the error
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(()));
// the iterator should have an error on it
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results");
assert_eq!(results, Err(fidl_policy::ScanErrorCode::GeneralError));
});
// Check both successful scan consumers have no results
assert_eq!(*exec.run_singlethreaded(network_selector_results.lock()), None);
assert_eq!(*exec.run_singlethreaded(location_sensor_results.lock()), None);
}
#[fuchsia::test]
fn overlapping_scans() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (client, mut sme_stream) = exec.run_singlethreaded(create_iface_manager());
let (network_selector1, network_selector_results1) = MockScanResultConsumer::new();
let (location_sensor1, location_sensor_results1) = MockScanResultConsumer::new();
let (network_selector2, network_selector_results2) = MockScanResultConsumer::new();
let (location_sensor2, location_sensor_results2) = MockScanResultConsumer::new();
// Use separate saved network managers so only one expects an active scan
// based on saved networks.
let saved_networks_manager1 = Arc::new(FakeSavedNetworksManager::new());
let saved_networks_manager2 = Arc::new(FakeSavedNetworksManager::new());
// Save a network with 1.0 hidden network probability to guarantee an active scan for scan_fut1.
let active_ssid = types::Ssid::try_from("foo active ssid").unwrap();
let active_id = types::NetworkIdentifier {
ssid: active_ssid.clone(),
security_type: types::SecurityType::Wpa3,
};
assert!(exec
.run_singlethreaded(
saved_networks_manager2
.store(active_id.clone().into(), Credential::Password(b"randompass".to_vec()))
)
.expect("failed to store network")
.is_none());
exec.run_singlethreaded(
saved_networks_manager2.update_hidden_prob(active_id.clone().into(), 1.0),
);
let MockScanData {
passive_input_aps,
passive_internal_aps,
passive_fidl_aps,
active_input_aps,
combined_internal_aps,
combined_fidl_aps,
} = create_scan_ap_data();
// Create two sets of endpoints
let (iter0, iter_server0) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let (iter1, iter_server1) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
// Issue request to scan on both iterator.
let scan_fut0 = perform_scan(
client.clone(),
saved_networks_manager1.clone(),
Some(iter_server0),
network_selector1,
location_sensor1,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut0);
let scan_fut1 = perform_scan(
client.clone(),
saved_networks_manager2,
Some(iter_server1),
network_selector2,
location_sensor2,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut1);
// Request a chunk of scan results on both iterators. Progress until waiting on
// response from server side of the iterator.
let mut output_iter_fut0 = iter0.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut0), Poll::Pending);
let mut output_iter_fut1 = iter1.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut1), Poll::Pending);
// Progress first scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut0), Poll::Pending);
// Check that a scan request was sent to the sme and send back results
assert_variant!(
exec.run_until_stalled(&mut sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
txn, ..
}))) => {
// Send the first AP
let (_stream, ctrl) = txn
.into_stream_and_control_handle().expect("error accessing control handle");
let mut aps = [passive_input_aps[0].clone()];
ctrl.send_on_result(&mut aps.iter_mut())
.expect("failed to send scan data");
// Process SME result.
assert_variant!(exec.run_until_stalled(&mut scan_fut0), Poll::Pending);
// The iterator should not have any data yet, until the sme is done
assert_variant!(exec.run_until_stalled(&mut output_iter_fut0), Poll::Pending);
// Progress second scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut1), Poll::Pending);
// Check that the second scan request was sent to the sme and send back results
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(&mut exec, &mut sme_stream, &expected_scan_request, passive_input_aps.clone()); // for output_iter_fut1
// Process SME result.
assert_variant!(exec.run_until_stalled(&mut scan_fut1), Poll::Pending);
// The second request should now result in an active scan
let expected_scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
channels: vec![],
ssids: vec![active_ssid.to_vec()],
});
validate_sme_scan_request_and_send_results(&mut exec, &mut sme_stream, &expected_scan_request, active_input_aps.clone()); // for output_iter_fut1
// Process SME result.
assert_variant!(exec.run_until_stalled(&mut scan_fut1), Poll::Pending);// The second iterator should have all its data
assert_variant!(exec.run_until_stalled(&mut output_iter_fut1), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results.len(), combined_fidl_aps.len());
assert_eq!(results, combined_fidl_aps);
});
// Send the remaining APs for the first iterator
let mut aps = passive_input_aps[1..].iter().map(|a| a.clone()).collect::<Vec<_>>();
ctrl.send_on_result(&mut aps.iter_mut())
.expect("failed to send scan data");
// Process SME result.
assert_variant!(exec.run_until_stalled(&mut scan_fut0), Poll::Pending);
// Send the end of data
ctrl.send_on_finished()
.expect("failed to send scan data");
}
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut0), Poll::Pending);
// The first iterator should have all its data
assert_variant!(exec.run_until_stalled(&mut output_iter_fut0), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results.len(), passive_fidl_aps.len());
assert_eq!(results, passive_fidl_aps);
});
// Check both successful scan consumers got results
assert_eq!(
*exec.run_singlethreaded(network_selector_results1.lock()),
Some(passive_internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results1.lock()),
Some(passive_internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(network_selector_results2.lock()),
Some(combined_internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results2.lock()),
Some(combined_internal_aps.clone())
);
}
#[test_case(true)]
#[test_case(false)]
#[fuchsia::test(add_test_attr = false)]
fn perform_scan_wpa3_supported(wpa3_capable: bool) {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (network_selector, network_selector_results) = MockScanResultConsumer::new();
let (location_sensor, location_sensor_results) = MockScanResultConsumer::new();
let saved_networks_manager = Arc::new(FakeSavedNetworksManager::new());
// Create a FakeIfaceManager that returns has_wpa3_iface() based on test value
let (sme_proxy, remote) =
create_proxy::<fidl_sme::ClientSmeMarker>().expect("error creating proxy");
let mut sme_stream = remote.into_stream().expect("failed to create stream");
let client = Arc::new(Mutex::new(FakeIfaceManager { sme_proxy, wpa3_capable }));
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
ScanReason::NetworkSelection,
None,
);
pin_mut!(scan_fut);
// Request a chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Pending);
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Generate scan results
let ssid = types::Ssid::try_from("some_ssid").unwrap();
let sme_scan_result = fidl_sme::ScanResult {
compatible: true,
timestamp_nanos: zx::Time::get_monotonic().into_nanos(),
bss_description: random_fidl_bss_description!(
Wpa2Wpa3,
ssid: ssid.clone(),
channel: types::WlanChan::new(8, types::Cbw::Cbw20),
),
};
let common_scan_result: wlan_common::scan::ScanResult = sme_scan_result
.clone()
.try_into()
.expect("Failed to convert fidl_sme::ScanResult into wlan_common::scan::ScanResult");
let _type_ =
if wpa3_capable { types::SecurityType::Wpa3 } else { types::SecurityType::Wpa2 };
let expected_scan_results = vec![fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: ssid.to_vec(),
// 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): change this back to a variable `type_` based on WPA3 support.
type_: fidl_policy::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some(common_scan_result.bss_description.bssid.0),
rssi: Some(common_scan_result.bss_description.rssi_dbm),
// For now frequency and timestamp are set to 0 when converting types.
frequency: Some(CENTER_FREQ_CHAN_8),
timestamp_nanos: Some(common_scan_result.timestamp.into_nanos()),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(types::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
}];
let expected_internal_scans = vec![types::ScanResult {
ssid: ssid.clone(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Wpa3Personal,
compatibility: fidl_policy::Compatibility::Supported,
entries: vec![types::Bss {
bssid: common_scan_result.bss_description.bssid,
rssi: common_scan_result.bss_description.rssi_dbm,
snr_db: common_scan_result.bss_description.snr_db,
channel: common_scan_result.bss_description.channel.into(),
timestamp: common_scan_result.timestamp,
observation: types::ScanObservation::Passive,
compatible: common_scan_result.compatible,
bss_description: common_scan_result.bss_description.into(),
}],
}];
// Create mock scan data and send it via the SME
let expected_scan_request = fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
vec![sme_scan_result],
);
// Process response from SME
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Check for results
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, expected_scan_results);
});
// Request the next chunk of scan results. Progress until waiting on response from server side of
// the iterator.
let mut output_iter_fut = iter.get_next();
// Process scan handler
// Note: this will be Poll::Ready because the scan handler will exit after sending the final
// scan results.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(()));
// Check for results
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, vec![]);
});
// Check both successful scan consumers got results
assert_eq!(
*exec.run_singlethreaded(network_selector_results.lock()),
Some(expected_internal_scans.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results.lock()),
Some(expected_internal_scans.clone())
);
}
// TODO(fxbug.dev/54255): Separate test case for "empty final vector not consumed" vs "partial ap list"
// consumed.
#[fuchsia::test]
fn partial_scan_result_consumption_has_no_error() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let MockScanData {
passive_input_aps: _,
passive_internal_aps: _,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps,
} = create_scan_ap_data();
// Create an iterator and send scan results
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let send_fut = send_scan_results_over_fidl(iter_server, &combined_fidl_aps);
pin_mut!(send_fut);
// Request a chunk of scan results.
let mut output_iter_fut = iter.get_next();
// Send first chunk of scan results
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
// Make sure the first chunk of results were delivered
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, combined_fidl_aps);
});
// Close the channel without getting remaining results
// Note: as of the writing of this test, the "remaining results" are just the final message
// with an empty vector of networks that signify the end of results. That final empty vector
// is still considered part of the results, so this test successfully exercises the
// "partial results read" path.
drop(output_iter_fut);
drop(iter);
// This should not result in error, since some results were consumed
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Ok(())));
}
#[fuchsia::test]
fn no_scan_result_consumption_has_error() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let MockScanData {
passive_input_aps: _,
passive_internal_aps: _,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps,
} = create_scan_ap_data();
// Create an iterator and send scan results
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let send_fut = send_scan_results_over_fidl(iter_server, &combined_fidl_aps);
pin_mut!(send_fut);
// Close the channel without getting results
drop(iter);
// This should result in error, since no results were consumed
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Err(_)));
}
#[fuchsia::test]
fn directed_active_scan_filters_desired_network() {
let mut exec = fasync::TestExecutor::new().expect("failed to create an executor");
let (sme_proxy, mut sme_stream) = exec.run_singlethreaded(create_sme_proxy());
// Issue request to scan.
let desired_ssid = types::Ssid::try_from("test_ssid").unwrap();
let desired_channels = vec![1, 36];
let scan_fut_desired_ssid = desired_ssid.clone();
let scan_fut = perform_directed_active_scan(
&sme_proxy,
&scan_fut_desired_ssid,
Some(desired_channels.clone()),
);
pin_mut!(scan_fut);
// Generate scan results
let scan_result_aps = vec![
fidl_sme::ScanResult {
bss_description: random_fidl_bss_description!(Wpa3Enterprise, ssid: desired_ssid.clone()),
..generate_random_sme_scan_result()
},
fidl_sme::ScanResult {
bss_description: random_fidl_bss_description!(Wpa2Wpa3, ssid: desired_ssid.clone()),
..generate_random_sme_scan_result()
},
fidl_sme::ScanResult {
bss_description: random_fidl_bss_description!(Wpa2Wpa3, ssid: desired_ssid.clone()),
..generate_random_sme_scan_result()
},
fidl_sme::ScanResult {
bss_description: random_fidl_bss_description!(Wpa2, ssid: types::Ssid::try_from("other ssid").unwrap()),
..generate_random_sme_scan_result()
},
];
// Progress scan handler forward so that it will respond to the iterator get next request.
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Pending);
// Respond to the scan request
let expected_scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![desired_ssid.to_vec()],
channels: desired_channels,
});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
scan_result_aps.clone(),
);
// Check for results
assert_variant!(exec.run_until_stalled(&mut scan_fut), Poll::Ready(result) => {
let mut result = result.unwrap();
// Two networks with the desired SSID are present
assert_eq!(result.len(), 2);
result.sort_by_key(|r| r.security_type_detailed.clone());
// One network is WPA2WPA3
assert_eq!(result[0].ssid, desired_ssid.clone());
assert_eq!(result[0].security_type_detailed, types::SecurityTypeDetailed::Wpa2Wpa3Personal);
// Two BSSs for this network
assert_eq!(result[0].entries.len(), 2);
// Other network is WPA3
assert_eq!(result[1].ssid, desired_ssid.clone());
assert_eq!(result[1].security_type_detailed, types::SecurityTypeDetailed::Wpa3Enterprise);
// One BSS for this network
assert_eq!(result[1].entries.len(), 1);
});
}
#[fuchsia::test]
fn sme_protection_converts_to_policy_security() {
use {super::fidl_policy::SecurityType, super::fidl_sme::Protection};
let wpa3_supported = true;
let wpa3_not_supported = false;
let test_pairs = vec![
// Below are pairs when WPA3 is supported.
(Protection::Wpa3Enterprise, wpa3_supported, Some(SecurityType::Wpa3)),
(Protection::Wpa3Personal, wpa3_supported, Some(SecurityType::Wpa3)),
(Protection::Wpa2Wpa3Personal, wpa3_supported, Some(SecurityType::Wpa3)),
(Protection::Wpa2Enterprise, wpa3_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2Personal, wpa3_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1Wpa2Personal, wpa3_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2PersonalTkipOnly, wpa3_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1Wpa2PersonalTkipOnly, wpa3_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1, wpa3_supported, Some(SecurityType::Wpa)),
(Protection::Wep, wpa3_supported, Some(SecurityType::Wep)),
(Protection::Open, wpa3_supported, Some(SecurityType::None)),
(Protection::Unknown, wpa3_supported, None),
// Below are pairs when WPA3 is not supported.
(Protection::Wpa3Enterprise, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa3Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2Wpa3Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2Enterprise, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1Wpa2Personal, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa2PersonalTkipOnly, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1Wpa2PersonalTkipOnly, wpa3_not_supported, Some(SecurityType::Wpa2)),
(Protection::Wpa1, wpa3_not_supported, Some(SecurityType::Wpa)),
(Protection::Wep, wpa3_not_supported, Some(SecurityType::Wep)),
(Protection::Open, wpa3_not_supported, Some(SecurityType::None)),
(Protection::Unknown, wpa3_not_supported, None),
];
for (input, wpa3_capable, output) in test_pairs {
assert_eq!(fidl_security_from_sme_protection(input, wpa3_capable), output);
}
}
#[fuchsia::test]
fn scan_result_generate_from_size() {
let scan_results = create_fidl_scan_results_from_size(vec![112; 4]);
assert_eq!(scan_results.len(), 4);
assert!(scan_results.iter().all(|scan_result| scan_result.measure().num_bytes == 112));
}
#[fuchsia::test]
fn scan_result_sends_max_message_size() {
let mut exec = fasync::TestExecutor::new().expect("failed to create and executor");
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
// Create a single scan result at the max allowed size to send in single
// FIDL message.
let fidl_scan_results = create_fidl_scan_results_from_size(vec![
zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize
- FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE,
]);
let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
pin_mut!(send_fut);
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, fidl_scan_results);
})
}
#[fuchsia::test]
fn scan_result_exceeding_max_size_throws_error() {
let mut exec = fasync::TestExecutor::new().expect("failed to create and executor");
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
// Create a single scan result exceeding the max allowed size to send in single
// FIDL message.
let fidl_scan_results = create_fidl_scan_results_from_size(vec![
(zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize
- FIDL_HEADER_AND_ERR_WRAPPED_VEC_HEADER_SIZE)
+ 8,
]);
let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
pin_mut!(send_fut);
let _ = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Ready(Err(_)));
}
#[fuchsia::test]
fn scan_result_sends_single_batch() {
let mut exec = fasync::TestExecutor::new().expect("failed to create and executor");
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
// Create a set of scan results that does not exceed the the max message
// size, so it should be sent in a single batch.
let fidl_scan_results =
create_fidl_scan_results_from_size(vec![
zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize / 4;
3
]);
let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
pin_mut!(send_fut);
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results, fidl_scan_results);
});
}
#[fuchsia::test]
fn scan_result_sends_multiple_batches() {
let mut exec = fasync::TestExecutor::new().expect("failed to create and executor");
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
// Create a set of scan results that exceed the max FIDL message size, so
// they should be split into batches.
let fidl_scan_results =
create_fidl_scan_results_from_size(vec![
zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize / 8;
8
]);
let send_fut = send_scan_results_over_fidl(iter_server, &fidl_scan_results);
pin_mut!(send_fut);
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
let mut aggregate_results = vec![];
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results.len(), 7);
aggregate_results.extend(results);
});
let mut output_iter_fut = iter.get_next();
assert_variant!(exec.run_until_stalled(&mut send_fut), Poll::Pending);
assert_variant!(exec.run_until_stalled(&mut output_iter_fut), Poll::Ready(result) => {
let results = result.expect("Failed to get next scan results").unwrap();
assert_eq!(results.len(), 1);
aggregate_results.extend(results);
});
assert_eq!(aggregate_results, fidl_scan_results);
}
}