blob: f5aec8599d2bad96f426e436832965a9bdb388fd [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Manages Scan requests for the Client Policy API.
use {
crate::{
client::types,
config_management::{SavedNetworksManager, ScanResultType},
mode_management::iface_manager_api::IfaceManagerApi,
util::sme_conversion::security_from_sme_protection,
},
anyhow::{format_err, Error},
async_trait::async_trait,
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_component::client::connect_to_service,
fuchsia_zircon as zx,
futures::{lock::Mutex, prelude::*},
log::{debug, error, info, warn},
std::{collections::HashMap, sync::Arc},
stream::FuturesUnordered,
wlan_common::channel::Channel,
};
// Arbitrary count of networks (ssid/security pairs) to output per request
const OUTPUT_CHUNK_NETWORK_COUNT: usize = 5;
// Delay between scanning retries when the firmware returns "ShouldWait" error code
const SCAN_RETRY_DELAY_MS: i64 = 100;
#[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<fidl_sme::BssInfo>, ()> {
enum SmeScanError {
ShouldRetryLater,
Other,
}
async fn sme_scan_internal(
sme_proxy: &fidl_sme::ClientSmeProxy,
mut scan_request: fidl_sme::ScanRequest,
) -> Result<Vec<fidl_sme::BssInfo>, 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![];
while let Some(Ok(event)) = stream.next().await {
match event {
fidl_sme::ScanTransactionEvent::OnResult { aps: new_aps } => {
debug!("Received scan results from SME");
scanned_networks.extend(new_aps);
}
fidl_sme::ScanTransactionEvent::OnFinished {} => {
debug!("Finished getting scan results from SME");
return Ok(scanned_networks);
}
fidl_sme::ScanTransactionEvent::OnError { error } => {
error!("Scan error from SME: {:?}", error);
return Err(
if error.code == fidl_sme::ScanErrorCode::ShouldWait
|| error.code == fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
{
SmeScanError::ShouldRetryLater
} else {
SmeScanError::Other
},
);
}
};
}
error!("SME closed scan result channel without sending OnFinished");
Err(SmeScanError::Other)
}
match sme_scan_internal(sme_proxy, scan_request.clone()).await {
Ok(results) => Ok(results),
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;
sme_scan_internal(sme_proxy, scan_request.clone()).await.map_err(|_| ())
}
Err(_) => Err(()),
}
}
/// 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<F>(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
saved_networks_manager: Arc<SavedNetworksManager>,
mut output_iterator: Option<fidl::endpoints::ServerEnd<fidl_policy::ScanResultIteratorMarker>>,
mut network_selector: impl ScanResultUpdate,
mut location_sensor_updater: impl ScanResultUpdate,
active_scan_decider: F,
) where
F: FnOnce(&Vec<types::Ssid>) -> Option<Vec<types::NetworkIdentifier>>,
{
let mut bss_by_network: HashMap<SmeNetworkIdentifier, Vec<types::Bss>> = HashMap::new();
// Get an SME proxy
let mut iface_manager_guard = iface_manager.lock().await;
let sme_proxy = match iface_manager_guard.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;
}
};
// Determine whether WPA2/WPA3 is should be considered WPA2 or WPA3.
let wpa3_supported = iface_manager_guard.has_wpa3_capable_client().await.unwrap_or_else(|e| {
error!("Failed to get whether the device supports WPA3. Assuming no WPA3 support. {}", e);
false
});
// Drop lock on iface manager befor starting a scan.
drop(iface_manager_guard);
// 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) => {
insert_bss_to_network_bss_map(&mut bss_by_network, results, true);
}
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,
fidl_policy::ScanErrorCode::GeneralError,
)
.await
.unwrap_or_else(|e| error!("Failed to send scan error: {}", e));
}
return;
}
};
// Determine which active scans to perform by asking the active_scan_decider()
let observed_ssid_list = bss_by_network.iter().map(|(id, _)| id.ssid.clone()).collect();
if let Some(requested_active_scan_ids) = active_scan_decider(&observed_ssid_list) {
let requested_active_scan_ssids =
requested_active_scan_ids.iter().map(|id| id.ssid.clone()).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,
wpa3_supported,
)
.await;
insert_bss_to_network_bss_map(&mut bss_by_network, results, false);
}
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,
fidl_policy::ScanErrorCode::GeneralError,
)
.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 {}
}
/// 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: &Vec<u8>,
channels: Option<Vec<u8>>,
) -> Result<Vec<types::ScanResult>, ()> {
let scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![ssid.clone()],
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_results: &Vec<fidl_sme::BssInfo>,
saved_networks_manager: Arc<SavedNetworksManager>,
wpa3_supported: bool,
) {
let ids = scan_results
.iter()
.filter_map(|result| {
// If the converted security is None, skip this scan result.
security_from_sme_protection(result.protection, wpa3_supported).map(|security| {
types::NetworkIdentifier { ssid: result.ssid.clone(), type_: security }
})
})
.collect();
// TODO(fxbug.dev/70965): modify this result to use the detailed security type
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_service::<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::BssInfo 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>>,
new_bss: Vec<fidl_sme::BssInfo>,
observed_in_passive_scan: bool,
) {
for bss in new_bss.into_iter() {
let entry = bss_by_network
.entry(SmeNetworkIdentifier { ssid: bss.ssid.to_vec(), protection: bss.protection })
.or_insert(vec![]);
// Check if this BSSID is already in the hashmap
if !entry.iter().any(|existing_bss| existing_bss.bssid == bss.bssid) {
entry.push(types::Bss {
bssid: bss.bssid,
rssi: bss.rssi_dbm,
snr_db: bss.snr_db,
channel: bss.channel,
timestamp_nanos: 0, // TODO(mnck): find where this comes from
observed_in_passive_scan,
compatible: bss.compatible,
bss_desc: bss.bss_desc,
});
};
}
}
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_infos)| {
let compatibility = if bss_infos.iter().any(|bss| bss.compatible) {
fidl_policy::Compatibility::Supported
} else {
fidl_policy::Compatibility::DisallowedNotSupported
};
types::ScanResult {
ssid: ssid,
security_type_detailed: protection,
entries: bss_infos,
compatibility: compatibility,
}
})
.collect();
scan_results.sort_by(|a, b| a.ssid.cmp(&b.ssid));
return scan_results;
}
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) =
security_from_sme_protection(internal.security_type_detailed, wpa3_supported)
{
Some(fidl_policy::ScanResult {
id: Some(types::NetworkIdentifier {
ssid: internal.ssid.clone(),
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 = Channel::from_fidl(input.channel)
.get_center_freq()
.unwrap_or(0);
fidl_policy::Bss {
bssid: Some(input.bssid),
rssi: Some(input.rssi),
frequency: Some(frequency.into()), // u16.into() -> u32
timestamp_nanos: Some(input.timestamp_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.
/// 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> {
let mut chunks = scan_results.chunks(OUTPUT_CHUNK_NETWORK_COUNT);
let mut sent_some_results = false;
// Wait to get a request for a chunk of scan results
let (mut stream, ctrl) = output_iterator.into_stream_and_control_handle()?;
loop {
if let Some(fidl_policy::ScanResultIteratorRequest::GetNext { responder }) =
stream.try_next().await?
{
sent_some_results = true;
if let Some(chunk) = chunks.next() {
let mut next_result: fidl_policy::ScanResultIteratorGetNextResult =
Ok(chunk.to_vec());
responder.send(&mut next_result)?;
} else {
// When no results are left, send an empty vec and close the channel.
let mut next_result: fidl_policy::ScanResultIteratorGetNextResult = Ok(vec![]);
responder.send(&mut next_result)?;
ctrl.shutdown();
break;
}
} 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"));
}
}
}
Ok(())
}
/// 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, PROB_HIDDEN_DEFAULT},
util::{
logger::set_logger_for_test,
testing::{
generate_random_bss_desc, generate_random_bss_info,
validate_sme_scan_request_and_send_results,
},
},
},
anyhow::Error,
fidl::endpoints::{create_proxy, Proxy},
fidl_fuchsia_wlan_common as fidl_common, fuchsia_async as fasync, fuchsia_zircon as zx,
futures::{channel::oneshot, lock::Mutex, task::Poll},
pin_utils::pin_mut,
std::sync::Arc,
test_case::test_case,
wlan_common::assert_variant,
};
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: fidl_fuchsia_wlan_policy::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: Vec<u8>, _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) {
set_logger_for_test();
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) {
set_logger_for_test();
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::BssInfo>,
passive_internal_aps: Vec<types::ScanResult>,
passive_fidl_aps: Vec<fidl_policy::ScanResult>,
active_input_aps: Vec<fidl_sme::BssInfo>,
combined_internal_aps: Vec<types::ScanResult>,
combined_fidl_aps: Vec<fidl_policy::ScanResult>,
}
fn create_scan_ap_data() -> MockScanData {
let bss_desc1 = generate_random_bss_desc();
let bss_desc2 = generate_random_bss_desc();
let bss_desc3 = generate_random_bss_desc();
let passive_input_aps = vec![
fidl_sme::BssInfo {
bssid: [0, 0, 0, 0, 0, 0],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: bss_desc1.clone(),
},
fidl_sme::BssInfo {
bssid: [1, 2, 3, 4, 5, 6],
ssid: "unique ssid".as_bytes().to_vec(),
rssi_dbm: 7,
snr_db: 2,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa2Personal,
compatible: true,
bss_desc: bss_desc2.clone(),
},
fidl_sme::BssInfo {
bssid: [7, 8, 9, 10, 11, 12],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 13,
snr_db: 3,
channel: fidl_common::WlanChan {
primary: 11,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: false,
bss_desc: bss_desc3.clone(),
},
];
// input_aps contains some duplicate SSIDs, which should be
// grouped in the output.
let passive_internal_aps = vec![
types::ScanResult {
ssid: "duplicated ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![
types::Bss {
bssid: [0, 0, 0, 0, 0, 0],
rssi: 0,
timestamp_nanos: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: bss_desc1.clone(),
},
types::Bss {
bssid: [7, 8, 9, 10, 11, 12],
rssi: 13,
timestamp_nanos: 0,
snr_db: 3,
channel: fidl_common::WlanChan {
primary: 11,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: false,
bss_desc: bss_desc3.clone(),
},
],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: "unique ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: [1, 2, 3, 4, 5, 6],
rssi: 7,
timestamp_nanos: 0,
snr_db: 2,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: bss_desc2.clone(),
}],
compatibility: types::Compatibility::Supported,
},
];
let passive_fidl_aps = vec![
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "duplicated ssid".as_bytes().to_vec(),
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(0),
..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(0),
..fidl_policy::Bss::EMPTY
},
]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "unique ssid".as_bytes().to_vec(),
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(0),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
];
let bss_desc4 = generate_random_bss_desc();
let bss_desc5 = generate_random_bss_desc();
let active_input_aps = vec![
fidl_sme::BssInfo {
bssid: [9, 9, 9, 9, 9, 9],
ssid: "foo active ssid".as_bytes().to_vec(),
rssi_dbm: 0,
snr_db: 8,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: bss_desc4.clone(),
},
fidl_sme::BssInfo {
bssid: [8, 8, 8, 8, 8, 8],
ssid: "misc ssid".as_bytes().to_vec(),
rssi_dbm: 7,
snr_db: 9,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa2Personal,
compatible: true,
bss_desc: bss_desc5.clone(),
},
];
let combined_internal_aps = vec![
types::ScanResult {
ssid: "duplicated ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![
types::Bss {
bssid: [0, 0, 0, 0, 0, 0],
rssi: 0,
timestamp_nanos: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: bss_desc1.clone(),
},
types::Bss {
bssid: [7, 8, 9, 10, 11, 12],
rssi: 13,
timestamp_nanos: 0,
snr_db: 3,
channel: fidl_common::WlanChan {
primary: 11,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: false,
bss_desc: bss_desc3.clone(),
},
],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: "foo active ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa3Enterprise,
entries: vec![types::Bss {
bssid: [9, 9, 9, 9, 9, 9],
rssi: 0,
timestamp_nanos: 0,
snr_db: 8,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: false,
compatible: true,
bss_desc: bss_desc4,
}],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: "misc ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: [8, 8, 8, 8, 8, 8],
rssi: 7,
timestamp_nanos: 0,
snr_db: 9,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: false,
compatible: true,
bss_desc: bss_desc5,
}],
compatibility: types::Compatibility::Supported,
},
types::ScanResult {
ssid: "unique ssid".as_bytes().to_vec(),
security_type_detailed: types::SecurityTypeDetailed::Wpa2Personal,
entries: vec![types::Bss {
bssid: [1, 2, 3, 4, 5, 6],
rssi: 7,
timestamp_nanos: 0,
snr_db: 2,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: bss_desc2,
}],
compatibility: types::Compatibility::Supported,
},
];
let combined_fidl_aps = vec![
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "duplicated ssid".as_bytes().to_vec(),
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(0),
..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(0),
..fidl_policy::Bss::EMPTY
},
]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "foo active ssid".as_bytes().to_vec(),
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(0),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "misc ssid".as_bytes().to_vec(),
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(0),
..fidl_policy::Bss::EMPTY
}]),
compatibility: Some(fidl_policy::Compatibility::Supported),
..fidl_policy::ScanResult::EMPTY
},
fidl_policy::ScanResult {
id: Some(fidl_policy::NetworkIdentifier {
ssid: "unique ssid".as_bytes().to_vec(),
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(0),
..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,
}
}
#[test]
fn sme_scan_with_passive_request() {
let mut exec = fasync::Executor::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) => {
assert_eq!(result, Ok(input_aps));
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[test]
fn sme_scan_with_active_request() {
let mut exec = fasync::Executor::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!["foo_ssid".as_bytes().to_vec(), "bar_ssid".as_bytes().to_vec()],
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) => {
assert_eq!(result, Ok(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")]
fn sme_scan_error(error_code: fidl_sme::ScanErrorCode) {
let mut exec = fasync::Executor::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) => {
assert_eq!(result, Err(()));
});
// 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")]
fn sme_scan_error_with_retry(error_code: fidl_sme::ScanErrorCode, retry_succeeds: bool) {
let mut exec = fasync::Executor::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) => {
assert_eq!(result, Err(()));
});
}
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[test]
fn sme_scan_channel_closed() {
let mut exec = fasync::Executor::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) => {
assert_eq!(result, Err(()));
});
// No further requests to the sme
assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), Poll::Pending);
}
#[test]
fn basic_scan() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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,
|_| 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())
);
}
#[test]
fn scan_with_active_scan_decider() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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 the network that isn't seen in the scan so we can check its hidden probability.
let unseen_ssid = b"unseen_id".to_vec();
let unseen_active_id =
types::NetworkIdentifier { ssid: unseen_ssid.clone(), type_: types::SecurityType::Wpa };
let credential = Credential::Password(b"some-cred".to_vec());
exec.run_singlethreaded(
saved_networks_manager.store(unseen_active_id.clone().into(), credential),
)
.expect("failed to store network");
let config = exec
.run_singlethreaded(saved_networks_manager.lookup(unseen_active_id.clone().into()))
.pop()
.expect("failed to lookup");
assert_eq!(config.hidden_probability, PROB_HIDDEN_DEFAULT);
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let expected_passive_results: Vec<types::Ssid> =
passive_internal_aps.iter().map(|result| result.ssid.clone()).collect();
let active_ssid = b"foo active ssid".to_vec();
let scan_fut = perform_scan(
client,
saved_networks_manager.clone(),
Some(iter_server),
network_selector,
location_sensor,
|passive_results| {
assert_eq!(passive_results.clone().sort(), expected_passive_results.clone().sort());
Some(vec![
types::NetworkIdentifier {
ssid: active_ssid.clone(),
type_: types::SecurityType::Wpa2,
},
unseen_active_id.clone(),
])
},
);
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);
// Respond to the second (active) scan request
let expected_scan_request = fidl_sme::ScanRequest::Active(fidl_sme::ActiveScanRequest {
ssids: vec![active_ssid.clone(), unseen_ssid.clone()],
channels: vec![],
});
validate_sme_scan_request_and_send_results(
&mut exec,
&mut sme_stream,
&expected_scan_request,
active_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, combined_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(combined_internal_aps.clone())
);
assert_eq!(
*exec.run_singlethreaded(location_sensor_results.lock()),
Some(combined_internal_aps.clone())
);
// Verify that the network we actively scanned for but didn't see had its hidden
// probability updated.
let config = exec
.run_singlethreaded(saved_networks_manager.lookup(unseen_active_id.clone().into()))
.pop()
.expect("Failed to get network config");
assert!(config.hidden_probability < PROB_HIDDEN_DEFAULT);
}
#[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_bss_desc = generate_random_bss_desc();
let passive_input_aps = vec![
fidl_sme::BssInfo {
bssid: [0, 0, 0, 0, 0, 0],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: passive_bss_desc.clone(),
},
fidl_sme::BssInfo {
bssid: [0, 0, 0, 0, 0, 0],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 13,
snr_db: 3,
channel: fidl_common::WlanChan {
primary: 14,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: generate_random_bss_desc(),
},
];
let expected_id = SmeNetworkIdentifier {
ssid: "duplicated ssid".as_bytes().to_vec(),
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: [0, 0, 0, 0, 0, 0],
rssi: 0,
timestamp_nanos: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: passive_bss_desc.clone(),
}];
insert_bss_to_network_bss_map(&mut bss_by_network, passive_input_aps, 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_bss_desc = generate_random_bss_desc();
let active_input_aps = vec![
fidl_sme::BssInfo {
bssid: [0, 0, 0, 0, 0, 0],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 100,
snr_db: 100,
channel: fidl_common::WlanChan {
primary: 100,
cbw: fidl_common::Cbw::Cbw40,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: generate_random_bss_desc(),
},
fidl_sme::BssInfo {
bssid: [1, 2, 3, 4, 5, 6],
ssid: "duplicated ssid".as_bytes().to_vec(),
rssi_dbm: 101,
snr_db: 101,
channel: fidl_common::WlanChan {
primary: 101,
cbw: fidl_common::Cbw::Cbw40,
secondary80: 0,
},
protection: fidl_sme::Protection::Wpa3Enterprise,
compatible: true,
bss_desc: active_bss_desc.clone(),
},
];
// After the active scan, there should be a second bss included in the results
let expected_bss = vec![
types::Bss {
bssid: [0, 0, 0, 0, 0, 0],
rssi: 0,
timestamp_nanos: 0,
snr_db: 1,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
observed_in_passive_scan: true,
compatible: true,
bss_desc: passive_bss_desc.clone(),
},
types::Bss {
bssid: [1, 2, 3, 4, 5, 6],
rssi: 101,
timestamp_nanos: 0,
snr_db: 101,
channel: fidl_common::WlanChan {
primary: 101,
cbw: fidl_common::Cbw::Cbw40,
secondary80: 0,
},
observed_in_passive_scan: false,
compatible: true,
bss_desc: active_bss_desc.clone(),
},
];
insert_bss_to_network_bss_map(&mut bss_by_network, active_input_aps, false);
assert_eq!(bss_by_network.len(), 1);
assert_eq!(bss_by_network[&expected_id], expected_bss);
}
#[test]
fn scan_with_active_scan_decider_and_active_scan_failure() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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();
// Issue request to scan.
let (iter, iter_server) =
fidl::endpoints::create_proxy().expect("failed to create iterator");
let expected_passive_results: Vec<types::Ssid> =
passive_internal_aps.iter().map(|result| result.ssid.clone()).collect();
let active_ssid = b"foo active ssid".to_vec();
let scan_fut = perform_scan(
client,
saved_networks_manager,
Some(iter_server),
network_selector,
location_sensor,
|passive_results| {
assert_eq!(passive_results.clone().sort(), expected_passive_results.clone().sort());
Some(vec![types::NetworkIdentifier {
ssid: active_ssid.clone(),
type_: types::SecurityType::Wpa,
}])
},
);
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![active_ssid.clone()],
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())
);
}
#[test]
fn scan_iterator_never_polled() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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,
|_| 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,
|_| 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())
);
}
#[test]
fn scan_iterator_shut_down() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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,
|_| 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())
);
}
#[test]
fn scan_error() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
// 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,
|_| 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);
}
#[test]
fn overlapping_scans() {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
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_manager.clone(),
Some(iter_server0),
network_selector1,
location_sensor1,
|_| None,
);
pin_mut!(scan_fut0);
let active_ssid = b"foo active ssid".to_vec();
let expected_passive_results: Vec<types::Ssid> =
passive_internal_aps.iter().map(|result| result.ssid.clone()).collect();
let scan_fut1 = perform_scan(
client.clone(),
saved_networks_manager,
Some(iter_server1),
network_selector2,
location_sensor2,
|passive_results| {
assert_eq!(passive_results.clone().sort(), expected_passive_results.clone().sort());
Some(vec![types::NetworkIdentifier {
ssid: active_ssid.clone(),
type_: types::SecurityType::Wpa3,
}])
},
);
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.clone()],
});
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)]
fn perform_scan_wpa3_supported(wpa3_capable: bool) {
let mut exec = fasync::Executor::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 = create_saved_networks_manager(&mut exec);
set_logger_for_test();
// 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,
|_| 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 = b"some_ssid".to_vec();
let bss_info = fidl_sme::BssInfo {
ssid: ssid.clone(),
protection: fidl_sme::Protection::Wpa2Wpa3Personal,
compatible: true,
channel: fidl_common::WlanChan {
primary: 8,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
..generate_random_bss_info()
};
let scan_result_aps = vec![bss_info.clone()];
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.clone(),
// 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_: types::SecurityType::Wpa2,
}),
entries: Some(vec![fidl_policy::Bss {
bssid: Some(bss_info.bssid),
rssi: Some(bss_info.rssi_dbm),
// For now frequency and timestamp are set to 0 when converting types.
frequency: Some(CENTER_FREQ_CHAN_8),
timestamp_nanos: Some(0),
..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: bss_info.bssid.clone(),
rssi: bss_info.rssi_dbm,
snr_db: bss_info.snr_db,
channel: bss_info.channel,
timestamp_nanos: 0,
observed_in_passive_scan: true,
compatible: bss_info.compatible,
bss_desc: bss_info.bss_desc,
}],
}];
// 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,
scan_result_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, 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.
#[test]
fn partial_scan_result_consumption_has_no_error() {
set_logger_for_test();
let mut exec = fasync::Executor::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(())));
}
#[test]
fn no_scan_result_consumption_has_error() {
set_logger_for_test();
let mut exec = fasync::Executor::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(_)));
}
#[test]
fn directed_active_scan_filters_desired_network() {
let mut exec = fasync::Executor::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 = "test_ssid".as_bytes().to_vec();
let desired_channels = vec![1, 36];
let scan_fut =
perform_directed_active_scan(&sme_proxy, &desired_ssid, Some(desired_channels.clone()));
pin_mut!(scan_fut);
// Generate scan results
let scan_result_aps = vec![
fidl_sme::BssInfo {
ssid: desired_ssid.clone(),
protection: fidl_sme::Protection::Wpa3Enterprise,
..generate_random_bss_info()
},
fidl_sme::BssInfo {
ssid: desired_ssid.clone(),
protection: fidl_sme::Protection::Wpa2Wpa3Personal,
..generate_random_bss_info()
},
fidl_sme::BssInfo {
ssid: desired_ssid.clone(),
protection: fidl_sme::Protection::Wpa2Wpa3Personal,
..generate_random_bss_info()
},
fidl_sme::BssInfo {
ssid: "other ssid".as_bytes().to_vec(),
protection: fidl_sme::Protection::Wpa2Personal,
..generate_random_bss_info()
},
];
// 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.clone()],
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);
});
}
// TODO(fxbug.dev/52700) Ignore this test until the location sensor module exists.
#[ignore]
#[test]
fn scan_observer_sends_to_location_sensor() {
set_logger_for_test();
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let mut location_sensor_updater = LocationSensorUpdater { wpa3_supported: true };
let MockScanData {
passive_input_aps: _,
passive_internal_aps: internal_aps,
passive_fidl_aps: _,
active_input_aps: _,
combined_internal_aps: _,
combined_fidl_aps: _,
} = create_scan_ap_data();
let fut = location_sensor_updater.update_scan_results(&internal_aps);
exec.run_singlethreaded(fut);
panic!("Need to reach into location sensor and check it got data")
}
fn create_saved_networks_manager(exec: &mut fasync::Executor) -> Arc<SavedNetworksManager> {
let saved_networks_manager = exec
.run_singlethreaded(SavedNetworksManager::new_for_test())
.expect("failed to create saved networks manager");
Arc::new(saved_networks_manager)
}
}