| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use anyhow::Context as _; |
| use core::sync::atomic::AtomicUsize; |
| use fidl_fuchsia_wlan_common as fidl_common; |
| use fidl_fuchsia_wlan_device_service::{self as fidl_svc, DeviceServiceRequest}; |
| use fidl_fuchsia_wlan_mlme::{self as fidl_mlme, MinstrelStatsResponse}; |
| use fuchsia_async as fasync; |
| use fuchsia_inspect_contrib::{auto_persist, inspect_log}; |
| use fuchsia_zircon as zx; |
| use futures::{future::BoxFuture, prelude::*}; |
| use log::{error, info, warn}; |
| use std::sync::{atomic::Ordering, Arc}; |
| use wlan_sme::serve::SmeServer; |
| |
| use crate::device::{self, IfaceMap}; |
| use crate::inspect; |
| use crate::ServiceCfg; |
| |
| /// Thread-safe counter for spawned ifaces. |
| pub struct IfaceCounter(AtomicUsize); |
| |
| impl IfaceCounter { |
| pub fn new() -> Self { |
| Self(AtomicUsize::new(0)) |
| } |
| |
| /// Provides the caller with a new unique id. |
| pub fn next_iface_id(&self) -> usize { |
| self.0.fetch_add(1, Ordering::SeqCst) |
| } |
| } |
| |
| pub async fn serve_device_requests( |
| iface_counter: Arc<IfaceCounter>, |
| cfg: ServiceCfg, |
| ifaces: Arc<IfaceMap>, |
| mut req_stream: fidl_svc::DeviceServiceRequestStream, |
| inspect_tree: Arc<inspect::WlanstackTree>, |
| dev_monitor_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy, |
| persistence_req_sender: auto_persist::PersistenceReqSender, |
| ) -> Result<(), anyhow::Error> { |
| while let Some(req) = req_stream.try_next().await.context("error running DeviceService")? { |
| // Note that errors from responder.send() are propagated intentionally. |
| // If we fail to send a response, the only way to recover is to stop serving the |
| // client and close the channel. Otherwise, the client would be left hanging |
| // forever. |
| match req { |
| DeviceServiceRequest::ListIfaces { responder } => { |
| responder.send(&mut list_ifaces(&ifaces)) |
| } |
| DeviceServiceRequest::QueryIface { iface_id, responder } => { |
| let result = query_iface(&ifaces, iface_id); |
| let (status, mut response) = into_status_and_opt(result); |
| responder.send(status.into_raw(), response.as_mut()) |
| } |
| DeviceServiceRequest::QueryDiscoverySupport { iface_id, responder } => { |
| let result = query_discovery_support(&ifaces, iface_id).await; |
| let (status, mut response) = into_status_and_opt(result); |
| responder.send(status.into_raw(), response.as_mut()) |
| } |
| DeviceServiceRequest::QueryMacSublayerSupport { iface_id, responder } => { |
| let result = query_mac_sublayer_support(&ifaces, iface_id).await; |
| let (status, mut response) = into_status_and_opt(result); |
| responder.send(status.into_raw(), response.as_mut()) |
| } |
| DeviceServiceRequest::QuerySecuritySupport { iface_id, responder } => { |
| let result = query_security_support(&ifaces, iface_id).await; |
| let (status, mut response) = into_status_and_opt(result); |
| responder.send(status.into_raw(), response.as_mut()) |
| } |
| DeviceServiceRequest::QuerySpectrumManagementSupport { iface_id, responder } => { |
| let result = query_spectrum_management_support(&ifaces, iface_id).await; |
| let (status, mut response) = into_status_and_opt(result); |
| responder.send(status.into_raw(), response.as_mut()) |
| } |
| DeviceServiceRequest::AddIface { req, responder } => { |
| let mut add_iface_result = add_iface( |
| req, |
| &cfg, |
| &ifaces, |
| &iface_counter, |
| &inspect_tree, |
| dev_monitor_proxy.clone(), |
| persistence_req_sender.clone(), |
| ) |
| .await; |
| responder.send(add_iface_result.status, add_iface_result.iface_id.as_mut())?; |
| let serve_sme_fut = add_iface_result.result?; |
| fasync::Task::spawn(serve_sme_fut).detach(); |
| Ok(()) |
| } |
| DeviceServiceRequest::GetClientSme { iface_id, sme, responder } => { |
| let status = get_client_sme(&ifaces, iface_id, sme); |
| responder.send(status.into_raw()) |
| } |
| DeviceServiceRequest::GetApSme { iface_id, sme, responder } => { |
| let status = get_ap_sme(&ifaces, iface_id, sme); |
| responder.send(status.into_raw()) |
| } |
| DeviceServiceRequest::GetMeshSme { iface_id, sme, responder } => { |
| let status = get_mesh_sme(&ifaces, iface_id, sme); |
| responder.send(status.into_raw()) |
| } |
| DeviceServiceRequest::GetIfaceStats { .. } => { |
| // TODO(fxbug.dev/82654) - Remove this API call |
| warn!("GetIfaceStats is no longer supported"); |
| Ok(()) |
| } |
| DeviceServiceRequest::GetIfaceCounterStats { iface_id, responder } => { |
| let mut resp = match get_iface_counter_stats(&ifaces, iface_id).await { |
| Ok(resp) => match resp { |
| fidl_mlme::GetIfaceCounterStatsResponse::Stats(stats) => { |
| fidl_svc::GetIfaceCounterStatsResponse::Stats(stats) |
| } |
| fidl_mlme::GetIfaceCounterStatsResponse::ErrorStatus(status) => { |
| fidl_svc::GetIfaceCounterStatsResponse::ErrorStatus(status) |
| } |
| }, |
| Err(status) => { |
| fidl_svc::GetIfaceCounterStatsResponse::ErrorStatus(status.into_raw()) |
| } |
| }; |
| responder.send(&mut resp) |
| } |
| DeviceServiceRequest::GetIfaceHistogramStats { iface_id, responder } => { |
| let mut resp = match get_iface_histogram_stats(&ifaces, iface_id).await { |
| Ok(resp) => match resp { |
| fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => { |
| fidl_svc::GetIfaceHistogramStatsResponse::Stats(stats) |
| } |
| fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(status) => { |
| fidl_svc::GetIfaceHistogramStatsResponse::ErrorStatus(status) |
| } |
| }, |
| Err(status) => { |
| fidl_svc::GetIfaceHistogramStatsResponse::ErrorStatus(status.into_raw()) |
| } |
| }; |
| responder.send(&mut resp) |
| } |
| DeviceServiceRequest::GetMinstrelList { iface_id, responder } => { |
| let (status, mut peers) = list_minstrel_peers(&ifaces, iface_id).await; |
| responder.send(status.into_raw(), &mut peers) |
| } |
| DeviceServiceRequest::GetMinstrelStats { iface_id, peer_addr, responder } => { |
| let (status, mut peer) = get_minstrel_stats(&ifaces, iface_id, peer_addr).await; |
| responder.send(status.into_raw(), peer.as_deref_mut()) |
| } |
| }?; |
| } |
| Ok(()) |
| } |
| |
| fn into_status_and_opt<T>(r: Result<T, zx::Status>) -> (zx::Status, Option<T>) { |
| match r { |
| Ok(x) => (zx::Status::OK, Some(x)), |
| Err(status) => (status, None), |
| } |
| } |
| |
| fn list_ifaces(ifaces: &IfaceMap) -> fidl_svc::ListIfacesResponse { |
| let list = ifaces |
| .get_snapshot() |
| .iter() |
| .map(|(iface_id, _iface)| fidl_svc::IfaceListItem { iface_id: *iface_id }) |
| .collect(); |
| fidl_svc::ListIfacesResponse { ifaces: list } |
| } |
| |
| fn query_iface(ifaces: &IfaceMap, id: u16) -> Result<fidl_svc::QueryIfaceResponse, zx::Status> { |
| info!("query_iface(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| |
| let role = match iface.device_info.role { |
| fidl_common::WlanMacRole::Client => fidl_common::WlanMacRole::Client, |
| fidl_common::WlanMacRole::Ap => fidl_common::WlanMacRole::Ap, |
| fidl_common::WlanMacRole::Mesh => fidl_common::WlanMacRole::Mesh, |
| }; |
| |
| let phy_id = iface.phy_ownership.phy_id; |
| let phy_assigned_id = iface.phy_ownership.phy_assigned_id; |
| let sta_addr = iface.device_info.sta_addr; |
| Ok(fidl_svc::QueryIfaceResponse { role, id, sta_addr, phy_id, phy_assigned_id }) |
| } |
| |
| async fn query_discovery_support( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl_common::DiscoverySupport, zx::Status> { |
| info!("query_discovery_support(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.query_discovery_support().await.map_err(|e| { |
| warn!("query_discovery_support failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| async fn query_mac_sublayer_support( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl_common::MacSublayerSupport, zx::Status> { |
| info!("query_mac_sublayer_support(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.query_mac_sublayer_support().await.map_err(|e| { |
| warn!("query_mac_sublayer_support failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| async fn query_security_support( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl_common::SecuritySupport, zx::Status> { |
| info!("query_security_support(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.query_security_support().await.map_err(|e| { |
| warn!("query_security_support failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| async fn query_spectrum_management_support( |
| ifaces: &IfaceMap, |
| id: u16, |
| ) -> Result<fidl_common::SpectrumManagementSupport, zx::Status> { |
| info!("query_spectrum_management_support(id = {})", id); |
| let iface = ifaces.get(&id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.query_spectrum_management_support().await.map_err(|e| { |
| warn!("query_spectrum_management_support failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| fn get_client_sme( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| endpoint: wlan_sme::serve::client::Endpoint, |
| ) -> zx::Status { |
| let iface = ifaces.get(&iface_id); |
| let server = match iface { |
| None => return zx::Status::NOT_FOUND, |
| Some(ref iface) => match iface.sme_server { |
| SmeServer::Client(ref server) => server, |
| _ => return zx::Status::NOT_SUPPORTED, |
| }, |
| }; |
| match server.unbounded_send(endpoint) { |
| Ok(()) => zx::Status::OK, |
| Err(e) => { |
| error!("error sending an endpoint to the SME server future: {}", e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| fn get_ap_sme( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| endpoint: wlan_sme::serve::ap::Endpoint, |
| ) -> zx::Status { |
| let iface = ifaces.get(&iface_id); |
| let server = match iface { |
| None => return zx::Status::NOT_FOUND, |
| Some(ref iface) => match iface.sme_server { |
| SmeServer::Ap(ref server) => server, |
| _ => return zx::Status::NOT_SUPPORTED, |
| }, |
| }; |
| match server.unbounded_send(endpoint) { |
| Ok(()) => zx::Status::OK, |
| Err(e) => { |
| error!("error sending an endpoint to the SME server future: {}", e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| fn get_mesh_sme( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| endpoint: wlan_sme::serve::mesh::Endpoint, |
| ) -> zx::Status { |
| let iface = ifaces.get(&iface_id); |
| let server = match iface { |
| None => return zx::Status::NOT_FOUND, |
| Some(ref iface) => match iface.sme_server { |
| SmeServer::Mesh(ref server) => server, |
| _ => return zx::Status::NOT_SUPPORTED, |
| }, |
| }; |
| match server.unbounded_send(endpoint) { |
| Ok(()) => zx::Status::OK, |
| Err(e) => { |
| error!("error sending an endpoint to the SME server future: {}", e); |
| zx::Status::INTERNAL |
| } |
| } |
| } |
| |
| pub async fn get_iface_counter_stats( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| ) -> Result<fidl_mlme::GetIfaceCounterStatsResponse, zx::Status> { |
| let iface = ifaces.get(&iface_id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.get_iface_counter_stats().await.map_err(|e| { |
| warn!("get_iface_counter_stats failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| pub async fn get_iface_histogram_stats( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| ) -> Result<fidl_mlme::GetIfaceHistogramStatsResponse, zx::Status> { |
| let iface = ifaces.get(&iface_id).ok_or(zx::Status::NOT_FOUND)?; |
| iface.mlme_proxy.get_iface_histogram_stats().await.map_err(|e| { |
| warn!("get_iface_histogram_stats failed: {}", e); |
| zx::Status::INTERNAL |
| }) |
| } |
| |
| async fn list_minstrel_peers( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| ) -> (zx::Status, fidl_fuchsia_wlan_minstrel::Peers) { |
| let empty_peer_list = fidl_fuchsia_wlan_minstrel::Peers { addrs: vec![] }; |
| let iface = match ifaces.get(&iface_id) { |
| Some(iface) => iface, |
| None => return (zx::Status::NOT_FOUND, empty_peer_list), |
| }; |
| match iface.mlme_proxy.list_minstrel_peers().await { |
| Ok(resp) => (zx::Status::OK, resp.peers), |
| Err(_) => (zx::Status::INTERNAL, empty_peer_list), |
| } |
| } |
| |
| async fn get_minstrel_stats( |
| ifaces: &IfaceMap, |
| iface_id: u16, |
| peer_addr: [u8; 6], |
| ) -> (zx::Status, Option<Box<fidl_fuchsia_wlan_minstrel::Peer>>) { |
| let iface = match ifaces.get(&iface_id) { |
| Some(iface) => iface, |
| None => return (zx::Status::NOT_FOUND, None), |
| }; |
| let mut req = fidl_mlme::MinstrelStatsRequest { peer_addr }; |
| match iface.mlme_proxy.get_minstrel_stats(&mut req).await { |
| Ok(MinstrelStatsResponse { peer }) => (zx::Status::OK, peer), |
| Err(_) => (zx::Status::INTERNAL, None), |
| } |
| } |
| |
| struct AddIfaceResult { |
| result: Result<BoxFuture<'static, ()>, anyhow::Error>, |
| status: i32, |
| iface_id: Option<fidl_svc::AddIfaceResponse>, |
| } |
| |
| impl AddIfaceResult { |
| fn from_error(e: anyhow::Error, status: i32) -> Self { |
| AddIfaceResult { result: Err(e), status, iface_id: None } |
| } |
| |
| fn ok(fut: BoxFuture<'static, ()>, iface_id: u16) -> Self { |
| AddIfaceResult { |
| result: Ok(fut), |
| status: zx::sys::ZX_OK, |
| iface_id: Some(fidl_svc::AddIfaceResponse { iface_id }), |
| } |
| } |
| } |
| |
| async fn add_iface( |
| req: fidl_svc::AddIfaceRequest, |
| cfg: &ServiceCfg, |
| ifaces: &Arc<IfaceMap>, |
| iface_counter: &Arc<IfaceCounter>, |
| inspect_tree: &Arc<inspect::WlanstackTree>, |
| dev_monitor_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy, |
| persistence_req_sender: auto_persist::PersistenceReqSender, |
| ) -> AddIfaceResult { |
| // Utilize the provided MLME channel to construct a future to serve the SME. |
| let mlme_channel = match fasync::Channel::from_channel(req.iface.into_channel()) { |
| Ok(channel) => channel, |
| Err(e) => return AddIfaceResult::from_error(e.into(), zx::sys::ZX_ERR_INTERNAL), |
| }; |
| let mlme_proxy = fidl_mlme::MlmeProxy::new(mlme_channel); |
| |
| let id = iface_counter.next_iface_id() as u16; |
| let phy_ownership = |
| device::PhyOwnership { phy_id: req.phy_id, phy_assigned_id: req.assigned_iface_id }; |
| info!("iface #{} added ({:?})", id, phy_ownership); |
| |
| let inspect_tree = inspect_tree.clone(); |
| let iface_tree_holder = inspect_tree.create_iface_child(id); |
| |
| let device_info = match mlme_proxy.query_device_info().await { |
| Ok(device_info) => device_info, |
| Err(e) => return AddIfaceResult::from_error(e.into(), zx::sys::ZX_ERR_PEER_CLOSED), |
| }; |
| |
| let mac_sublayer_support = match mlme_proxy.query_mac_sublayer_support().await { |
| Ok(support) => support, |
| Err(e) => return AddIfaceResult::from_error(e.into(), zx::sys::ZX_ERR_PEER_CLOSED), |
| }; |
| |
| let security_support = match mlme_proxy.query_security_support().await { |
| Ok(support) => support, |
| Err(e) => return AddIfaceResult::from_error(e.into(), zx::sys::ZX_ERR_PEER_CLOSED), |
| }; |
| |
| let spectrum_management_support = match mlme_proxy.query_spectrum_management_support().await { |
| Ok(support) => support, |
| Err(e) => return AddIfaceResult::from_error(e.into(), zx::sys::ZX_ERR_PEER_CLOSED), |
| }; |
| |
| let serve_sme_fut = match device::create_and_serve_sme( |
| cfg.clone(), |
| id, |
| phy_ownership, |
| mlme_proxy, |
| ifaces.clone(), |
| inspect_tree.clone(), |
| iface_tree_holder, |
| device_info, |
| mac_sublayer_support, |
| security_support, |
| spectrum_management_support, |
| dev_monitor_proxy, |
| persistence_req_sender, |
| req.generic_sme, |
| ) { |
| Ok(fut) => fut, |
| Err(e) => return AddIfaceResult::from_error(e, zx::sys::ZX_ERR_INTERNAL), |
| }; |
| |
| // Handle the Result returned by the SME future. This enables some final cleanup and metrics |
| // logging and also makes the spawned task detachable. |
| let serve_sme_fut = serve_sme_fut.map(move |result| { |
| let msg = match result { |
| Ok(()) => { |
| let msg = format!("iface {} shutdown gracefully", id); |
| info!("{}", msg); |
| msg |
| } |
| Err(e) => { |
| let msg = format!("error serving iface {}: {}", id, e); |
| error!("{}", msg); |
| msg |
| } |
| }; |
| inspect_log!(inspect_tree.device_events.lock().get_mut(), msg: msg); |
| inspect_tree.notify_iface_removed(id); |
| }); |
| |
| AddIfaceResult::ok(Box::pin(serve_sme_fut), id) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::test_helper; |
| use fidl::endpoints::{ |
| create_endpoints, create_proxy, create_proxy_and_stream, ControlHandle, Responder, |
| }; |
| use fidl_fuchsia_wlan_device_service::IfaceListItem; |
| use fidl_fuchsia_wlan_mlme::{self as fidl_mlme, MlmeMarker}; |
| use fidl_fuchsia_wlan_sme as fidl_sme; |
| use fuchsia_async as fasync; |
| use fuchsia_zircon as zx; |
| use futures::channel::mpsc; |
| use futures::task::Poll; |
| use pin_utils::pin_mut; |
| use std::pin::Pin; |
| use wlan_common::{assert_variant, channel::Cbw, RadioConfig}; |
| |
| use crate::device::IfaceDevice; |
| |
| const IFACE_ID: u16 = 10; |
| const INVALID_IFACE_ID: u16 = 11; |
| |
| #[test] |
| fn iface_counter() { |
| let iface_counter = IfaceCounter::new(); |
| assert_eq!(0, iface_counter.next_iface_id()); |
| assert_eq!(1, iface_counter.next_iface_id()); |
| assert_eq!(2, iface_counter.next_iface_id()); |
| assert_eq!(3, iface_counter.next_iface_id()); |
| } |
| |
| #[test] |
| fn list_two_ifaces() { |
| let _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| let iface_null = fake_client_iface(); |
| let iface_zero = fake_client_iface(); |
| iface_map.insert(10u16, iface_null.iface); |
| iface_map.insert(20u16, iface_zero.iface); |
| let mut list = super::list_ifaces(&iface_map).ifaces; |
| list.sort_by_key(|p| p.iface_id); |
| assert_eq!( |
| vec![IfaceListItem { iface_id: 10u16 }, IfaceListItem { iface_id: 20u16 },], |
| list |
| ) |
| } |
| |
| #[test] |
| fn query_iface_success() { |
| let _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| let iface = fake_client_iface(); |
| iface_map.insert(10, iface.iface); |
| |
| let response = super::query_iface(&iface_map, 10).expect("querying iface failed"); |
| let expected = fake_device_info(); |
| assert_eq!(response.role, fidl_common::WlanMacRole::Client); |
| assert_eq!(response.sta_addr, expected.sta_addr); |
| assert_eq!(response.id, 10); |
| } |
| |
| #[test] |
| fn query_iface_not_found() { |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let status = super::query_iface(&iface_map, 10u16).expect_err("querying iface succeeded"); |
| assert_eq!(zx::Status::NOT_FOUND, status); |
| } |
| |
| macro_rules! setup_query_support_test { |
| ($test_helper:expr, $test_fut:expr, $query_support_fut:expr) => { |
| assert_variant!( |
| $test_helper.exec.run_until_stalled(&mut $query_support_fut), |
| Poll::Pending |
| ); |
| assert_variant!($test_helper.exec.run_until_stalled(&mut $test_fut), Poll::Pending); |
| }; |
| } |
| |
| macro_rules! setup_query_support_test_with_broken_mlme { |
| ($test_helper:expr, $test_fut:expr, $query_support_fut:expr, $query_type:path) => { |
| setup_query_support_test!($test_helper, $test_fut, $query_support_fut); |
| |
| // Verify the feature support request is forwarded to MLME |
| let responder = assert_variant!( |
| $test_helper.exec.run_until_stalled(&mut $test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok($query_type{ responder }))) => responder |
| ); |
| // (mlme -> wlanstack) Cause an error. |
| responder.control_handle().shutdown_with_epitaph(zx::Status::INTERNAL); |
| |
| assert_variant!($test_helper.exec.run_until_stalled(&mut $test_fut), Poll::Pending); |
| }; |
| } |
| |
| #[test] |
| fn test_query_discovery_support_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_discovery_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| // Verify the feature support request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryDiscoverySupport{ responder }))) => responder |
| ); |
| // (mlme -> wlanstack) Respond with fake feature support structure. |
| let mut mlme_resp = fake_discovery_support(); |
| responder.send(&mut mlme_resp).expect("failed to respond to MLME QueryDiscoverySupport"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(sme_resp) => { |
| assert_eq!(zx::Status::from_raw(sme_resp.0), zx::Status::OK); |
| let expected = fake_discovery_support(); |
| assert_variant!(sme_resp.1, Some(support) => { |
| assert_eq!(support.scan_offload.supported, expected.scan_offload.supported); |
| assert_eq!(support.probe_response_offload.supported, expected.probe_response_offload.supported); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_discovery_support_failure_for_invalid_iface() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_discovery_support(INVALID_IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::NOT_FOUND); |
| assert!(resp.1.is_none()); |
| }) |
| }); |
| } |
| |
| #[test] |
| fn test_query_discovery_support_failure_for_mlme_error() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_discovery_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test_with_broken_mlme!( |
| test_helper, |
| test_fut, |
| query_support_fut, |
| fidl_mlme::MlmeRequest::QueryDiscoverySupport |
| ); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::INTERNAL); |
| assert!(resp.1.is_none()); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_mac_sublayer_support_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_mac_sublayer_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // Verify the feature support request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryMacSublayerSupport{ responder }))) => responder |
| ); |
| // (mlme -> wlanstack) Respond with fake feature support structure. |
| let mut mlme_resp = fake_mac_sublayer_support(); |
| responder.send(&mut mlme_resp).expect("failed to respond to MLME QueryMacSublayerSupport"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(sme_resp) => { |
| assert_eq!(zx::Status::from_raw(sme_resp.0), zx::Status::OK); |
| let expected = fake_mac_sublayer_support(); |
| assert_variant!(sme_resp.1, Some(support) => { |
| assert_eq!(support.rate_selection_offload.supported, expected.rate_selection_offload.supported); |
| assert_eq!(support.data_plane.data_plane_type, expected.data_plane.data_plane_type); |
| assert_eq!(support.device.is_synthetic, expected.device.is_synthetic); |
| assert_eq!(support.device.mac_implementation_type, expected.device.mac_implementation_type); |
| assert_eq!(support.device.tx_status_report_supported, expected.device.tx_status_report_supported); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_mac_sublayer_support_failure_for_invalid_iface() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = |
| test_helper.dev_svc_proxy.query_mac_sublayer_support(INVALID_IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::NOT_FOUND); |
| assert!(resp.1.is_none()); |
| }) |
| }); |
| } |
| |
| #[test] |
| fn test_query_mac_sublayer_support_failure_for_mlme_error() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_mac_sublayer_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test_with_broken_mlme!( |
| test_helper, |
| test_fut, |
| query_support_fut, |
| fidl_mlme::MlmeRequest::QueryMacSublayerSupport |
| ); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::INTERNAL); |
| assert!(resp.1.is_none()); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_security_support_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_security_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // Verify the feature support request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySecuritySupport{ responder }))) => responder |
| ); |
| // (mlme -> wlanstack) Respond with fake feature support structure. |
| let mut mlme_resp = fake_security_support(); |
| responder.send(&mut mlme_resp).expect("failed to respond to MLME QuerySecuritySupport"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(sme_resp) => { |
| assert_eq!(zx::Status::from_raw(sme_resp.0), zx::Status::OK); |
| let expected = fake_security_support(); |
| assert_variant!(sme_resp.1, Some(support) => { |
| assert_eq!(support.sae.driver_handler_supported, expected.sae.driver_handler_supported); |
| assert_eq!(support.sae.sme_handler_supported, expected.sae.sme_handler_supported); |
| assert_eq!(support.mfp.supported, expected.mfp.supported); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_security_support_failure_for_invalid_iface() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_security_support(INVALID_IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::NOT_FOUND); |
| assert!(resp.1.is_none()); |
| }) |
| }); |
| } |
| |
| #[test] |
| fn test_query_security_support_failure_for_mlme_error() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = test_helper.dev_svc_proxy.query_security_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test_with_broken_mlme!( |
| test_helper, |
| test_fut, |
| query_support_fut, |
| fidl_mlme::MlmeRequest::QuerySecuritySupport |
| ); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::INTERNAL); |
| assert!(resp.1.is_none()); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_spectrum_management_support_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = |
| test_helper.dev_svc_proxy.query_spectrum_management_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // Verify the feature support request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySpectrumManagementSupport{ responder }))) => responder |
| ); |
| // (mlme -> wlanstack) Respond with fake feature support structure. |
| let mut mlme_resp = fake_spectrum_management_support(); |
| responder |
| .send(&mut mlme_resp) |
| .expect("failed to respond to MLME QuerySpectrumManagementSupport"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(sme_resp) => { |
| assert_eq!(zx::Status::from_raw(sme_resp.0), zx::Status::OK); |
| let expected = fake_spectrum_management_support(); |
| assert_variant!(sme_resp.1, Some(support) => { |
| assert_eq!(support.dfs.supported, expected.dfs.supported); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn test_query_spectrum_management_support_failure_for_invalid_iface() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = |
| test_helper.dev_svc_proxy.query_spectrum_management_support(INVALID_IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test!(test_helper, test_fut, query_support_fut); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::NOT_FOUND); |
| assert!(resp.1.is_none()); |
| }) |
| }); |
| } |
| |
| #[test] |
| fn test_query_spectrum_management_support_failure_for_mlme_error() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| let query_support_fut = |
| test_helper.dev_svc_proxy.query_spectrum_management_support(IFACE_ID); |
| pin_mut!(query_support_fut); |
| setup_query_support_test_with_broken_mlme!( |
| test_helper, |
| test_fut, |
| query_support_fut, |
| fidl_mlme::MlmeRequest::QuerySpectrumManagementSupport |
| ); |
| |
| // The future should have returned bad status here. |
| assert_variant!(test_helper.exec.run_until_stalled(&mut query_support_fut), Poll::Ready(result) => { |
| assert_variant!(result, Ok(resp) => { |
| assert_eq!(zx::Status::from_raw(resp.0), zx::Status::INTERNAL); |
| assert!(resp.1.is_none()); |
| }); |
| }); |
| } |
| |
| #[test] |
| fn get_client_sme_success() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let mut iface = fake_client_iface(); |
| iface_map.insert(10, iface.iface); |
| |
| let (proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::OK, super::get_client_sme(&iface_map, 10, server)); |
| |
| // Expect to get a new FIDL client in the stream |
| let endpoint = iface |
| .new_sme_clients |
| .try_next() |
| .expect("expected a message in new_sme_clients") |
| .expect("didn't expect new_sme_clients stream to end"); |
| let mut sme_stream = endpoint.into_stream().expect("failed to create stream for endpoint"); |
| |
| // Verify that `proxy` is indeed connected to `sme_stream` |
| let (_scan_proxy, scan_txn) = |
| create_proxy().expect("failed to create a pair of scan txn endpoints"); |
| proxy.scan(&mut fake_scan_request(), scan_txn).expect("failed to send a scan request"); |
| |
| assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan { req, .. }))) => { |
| assert_eq!(fake_scan_request(), req) |
| } |
| ); |
| } |
| |
| #[test] |
| fn get_client_sme_not_found() { |
| let mut _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let (_proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::NOT_FOUND, super::get_client_sme(&iface_map, 10, server)); |
| } |
| |
| #[test] |
| fn get_client_sme_wrong_role() { |
| let mut _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let iface = fake_ap_iface(); |
| iface_map.insert(10, iface.iface); |
| |
| let (_proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::NOT_SUPPORTED, super::get_client_sme(&iface_map, 10, server)); |
| } |
| |
| #[test] |
| fn get_ap_sme_success() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let mut iface = fake_ap_iface(); |
| iface_map.insert(10, iface.iface); |
| |
| let (proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::OK, super::get_ap_sme(&iface_map, 10, server)); |
| |
| // Expect to get a new FIDL client in the stream |
| let endpoint = iface |
| .new_sme_clients |
| .try_next() |
| .expect("expected a message in new_sme_clients") |
| .expect("didn't expect new_sme_clients stream to end"); |
| let mut sme_stream = endpoint.into_stream().expect("failed to create stream for endpoint"); |
| |
| // Verify that `proxy` is indeed connected to `sme_stream` |
| let mut fut = fidl_sme::ApSmeProxyInterface::start(&proxy, &mut fake_ap_config()); |
| |
| assert_variant!(exec.run_until_stalled(&mut sme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_sme::ApSmeRequest::Start { config, responder }))) => { |
| assert_eq!(fake_ap_config(), config); |
| responder |
| .send(fidl_sme::StartApResultCode::Success) |
| .expect("failed to send response"); |
| } |
| ); |
| |
| let fut_result = exec.run_until_stalled(&mut fut); |
| assert_variant!(fut_result, Poll::Ready(Ok(fidl_sme::StartApResultCode::Success))); |
| } |
| |
| #[test] |
| fn get_ap_sme_not_found() { |
| let mut _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let (_proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::NOT_FOUND, super::get_ap_sme(&iface_map, 10, server)); |
| } |
| |
| #[test] |
| fn get_ap_sme_wrong_role() { |
| let mut _exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| let iface_map = IfaceMap::new(); |
| let iface_map = Arc::new(iface_map); |
| |
| let iface = fake_client_iface(); |
| iface_map.insert(10, iface.iface); |
| |
| let (_proxy, server) = create_proxy().expect("failed to create a pair of SME endpoints"); |
| assert_eq!(zx::Status::NOT_SUPPORTED, super::get_ap_sme(&iface_map, 10, server)); |
| } |
| |
| // Debug is required for assert_variant. |
| impl std::fmt::Debug for AddIfaceResult { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| f.debug_tuple("") |
| .field(&self.result.is_ok()) |
| .field(&self.status) |
| .field(&self.iface_id) |
| .finish() |
| } |
| } |
| |
| #[test] |
| fn test_add_iface() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| |
| // Boilerplate for adding a new interface. |
| let iface_map = Arc::new(IfaceMap::new()); |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree(); |
| let (dev_monitor_proxy, _) = |
| create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let cfg = ServiceCfg { wep_supported: false, wpa1_supported: false }; |
| let (persistence_req_sender, _persistence_stream) = |
| test_helper::create_inspect_persistence_channel(); |
| let (_generic_sme_proxy, generic_sme) = |
| create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy"); |
| |
| // Construct the request. |
| let (mlme_channel, mlme_receiver) = |
| create_endpoints().expect("failed to create fake MLME proxy"); |
| let mut mlme_stream = mlme_receiver.into_stream().expect("failed to create MLME stream"); |
| let req = fidl_svc::AddIfaceRequest { |
| phy_id: 123, |
| assigned_iface_id: 456, |
| iface: mlme_channel, |
| generic_sme, |
| }; |
| let fut = add_iface( |
| req, |
| &cfg, |
| &iface_map, |
| &iface_counter, |
| &inspect_tree, |
| dev_monitor_proxy, |
| persistence_req_sender, |
| ); |
| pin_mut!(fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the PHY's information. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryDeviceInfo { responder }))) => { |
| let mut device_info = fake_device_info(); |
| responder.send(&mut device_info).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the MAC sublayer features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryMacSublayerSupport { responder }))) => { |
| responder.send(&mut fake_mac_sublayer_support()).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the security features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySecuritySupport { responder }))) => { |
| responder.send(&mut fake_security_support()).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the spectrum management features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySpectrumManagementSupport { responder }))) => { |
| responder.send(&mut fake_spectrum_management_support()).expect("failed to send MLME response"); |
| }); |
| |
| // The future should complete successfully. |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Ready(result) => { |
| assert!(result.result.is_ok()); |
| assert_eq!(result.status, zx::sys::ZX_OK); |
| assert_eq!(result.iface_id, Some(fidl_svc::AddIfaceResponse { iface_id: 0 })); |
| }); |
| } |
| |
| #[test] |
| fn test_add_iface_query_fails() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| |
| // Boilerplate for adding a new interface. |
| let iface_map = Arc::new(IfaceMap::new()); |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree(); |
| let (dev_monitor_proxy, _) = |
| create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let cfg = ServiceCfg { wep_supported: false, wpa1_supported: false }; |
| let (persistence_req_sender, _persistence_stream) = |
| test_helper::create_inspect_persistence_channel(); |
| let (_generic_sme_proxy, generic_sme) = |
| create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy"); |
| |
| // Construct the request. |
| let (mlme_channel, mlme_receiver) = |
| create_endpoints().expect("failed to create fake MLME proxy"); |
| |
| // Drop the receiver so that the initial device info query fails. |
| drop(mlme_receiver); |
| |
| let req = fidl_svc::AddIfaceRequest { |
| phy_id: 123, |
| assigned_iface_id: 456, |
| iface: mlme_channel, |
| generic_sme, |
| }; |
| let fut = add_iface( |
| req, |
| &cfg, |
| &iface_map, |
| &iface_counter, |
| &inspect_tree, |
| dev_monitor_proxy, |
| persistence_req_sender, |
| ); |
| pin_mut!(fut); |
| |
| // The future should have returned bad status here. |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Ready(result) => { |
| assert!(result.result.is_err()); |
| assert_eq!(result.status, zx::sys::ZX_ERR_PEER_CLOSED); |
| assert!(result.iface_id.is_none()); |
| }); |
| } |
| |
| #[test] |
| fn test_add_iface_create_sme_fails() { |
| let mut exec = fasync::TestExecutor::new().expect("Failed to create an executor"); |
| |
| // Boilerplate for adding a new interface. |
| let iface_map = Arc::new(IfaceMap::new()); |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree(); |
| let (dev_monitor_proxy, _) = |
| create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let cfg = ServiceCfg { wep_supported: false, wpa1_supported: false }; |
| let (persistence_req_sender, _persistence_stream) = |
| test_helper::create_inspect_persistence_channel(); |
| let (_generic_sme_proxy, generic_sme) = |
| create_proxy::<fidl_sme::GenericSmeMarker>().expect("failed to create MlmeProxy"); |
| |
| // Construct the request. |
| let (mlme_channel, mlme_receiver) = |
| create_endpoints().expect("failed to create fake MLME proxy"); |
| let mut mlme_stream = mlme_receiver.into_stream().expect("failed to create MLME stream"); |
| |
| let req = fidl_svc::AddIfaceRequest { |
| phy_id: 123, |
| assigned_iface_id: 456, |
| iface: mlme_channel, |
| generic_sme, |
| }; |
| let fut = add_iface( |
| req, |
| &cfg, |
| &iface_map, |
| &iface_counter, |
| &inspect_tree, |
| dev_monitor_proxy, |
| persistence_req_sender, |
| ); |
| pin_mut!(fut); |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the PHY's information. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryDeviceInfo { responder }))) => { |
| responder.send(&mut fake_device_info()).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the MAC sublayer features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QueryMacSublayerSupport { responder }))) => { |
| let mut support = fake_mac_sublayer_support(); |
| // Intentionally provide a set of features that is invalid, to cause a failure. |
| // A device that is synthetic but not softmac is currently invalid (but this may change in the future.) |
| support.device.is_synthetic = true; |
| support.device.mac_implementation_type = fidl_common::MacImplementationType::Fullmac; |
| responder.send(&mut support).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the security features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySecuritySupport { responder }))) => { |
| responder.send(&mut fake_security_support()).expect("failed to send MLME response"); |
| }); |
| |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Pending); |
| |
| // The future should have requested the spectrum management features. |
| assert_variant!( |
| exec.run_until_stalled(&mut mlme_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::QuerySpectrumManagementSupport { responder }))) => { |
| let mut support = fake_spectrum_management_support(); |
| responder.send(&mut support).expect("failed to send MLME response"); |
| }); |
| |
| // The device information should be invalid and the future should report the failure here. |
| assert_variant!(exec.run_until_stalled(&mut fut), Poll::Ready(result) => { |
| assert!(result.result.is_err()); |
| assert_eq!(result.status, zx::sys::ZX_ERR_INTERNAL); |
| assert!(result.iface_id.is_none()); |
| }); |
| } |
| |
| #[test] |
| fn test_get_iface_counter_stats_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| |
| let get_stats_fut = test_helper.dev_svc_proxy.get_iface_counter_stats(IFACE_ID); |
| pin_mut!(get_stats_fut); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Pending); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| |
| // Verify the GetIfaceCounterStats request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::GetIfaceCounterStats { responder }))) => responder |
| ); |
| |
| // (mlme -> wlanstack) Respond with mock counter stats |
| let stats = fidl_fuchsia_wlan_stats::IfaceCounterStats { |
| rx_unicast_total: 20, |
| rx_unicast_drop: 3, |
| rx_multicast: 5, |
| tx_total: 10, |
| tx_drop: 1, |
| }; |
| let mut mlme_resp = fidl_mlme::GetIfaceCounterStatsResponse::Stats(stats.clone()); |
| responder.send(&mut mlme_resp).expect("failed to send stats"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(fidl_svc::GetIfaceCounterStatsResponse::Stats(actual_stats)) => { |
| assert_eq!(actual_stats, stats); |
| }); |
| } |
| |
| #[test] |
| fn test_get_iface_counter_stats_failure() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| |
| let get_stats_fut = test_helper.dev_svc_proxy.get_iface_counter_stats(IFACE_ID); |
| pin_mut!(get_stats_fut); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Pending); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| |
| // Verify the GetIfaceCounterStats request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::GetIfaceCounterStats { responder }))) => responder |
| ); |
| |
| // (mlme -> wlanstack) Respond with error status |
| let mut mlme_resp = fidl_mlme::GetIfaceCounterStatsResponse::ErrorStatus(1); |
| responder.send(&mut mlme_resp).expect("failed to send stats"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(fidl_svc::GetIfaceCounterStatsResponse::ErrorStatus(1))); |
| } |
| |
| #[test] |
| fn test_get_iface_histogram_stats_success() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| |
| let get_stats_fut = test_helper.dev_svc_proxy.get_iface_histogram_stats(IFACE_ID); |
| pin_mut!(get_stats_fut); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Pending); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| |
| // Verify the GetIfaceHistogramStats request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::GetIfaceHistogramStats { responder }))) => responder |
| ); |
| |
| // (mlme -> wlanstack) Respond with mock histogram stats |
| let stats = fake_histogram_stats(); |
| let mut mlme_resp = fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats.clone()); |
| responder.send(&mut mlme_resp).expect("failed to send stats"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(fidl_svc::GetIfaceHistogramStatsResponse::Stats(actual_stats)) => { |
| assert_eq!(actual_stats, stats); |
| }); |
| } |
| |
| #[test] |
| fn test_get_iface_histogram_stats_failure() { |
| let (mut test_helper, mut test_fut) = setup_test(); |
| |
| let get_stats_fut = test_helper.dev_svc_proxy.get_iface_histogram_stats(IFACE_ID); |
| pin_mut!(get_stats_fut); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Pending); |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| |
| // Verify the GetIfaceHistogramStats request is forwarded to MLME |
| let responder = assert_variant!( |
| test_helper.exec.run_until_stalled(&mut test_helper.mlme_req_stream.next()), |
| Poll::Ready(Some(Ok(fidl_mlme::MlmeRequest::GetIfaceHistogramStats { responder }))) => responder |
| ); |
| |
| // (mlme -> wlanstack) Respond with error status |
| let mut mlme_resp = fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(1); |
| responder.send(&mut mlme_resp).expect("failed to send stats"); |
| |
| assert_variant!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| let resp = assert_variant!(test_helper.exec.run_until_stalled(&mut get_stats_fut), Poll::Ready(resp) => resp); |
| assert_variant!(resp, Ok(fidl_svc::GetIfaceHistogramStatsResponse::ErrorStatus(1))); |
| } |
| |
| struct TestHelper { |
| dev_svc_proxy: fidl_svc::DeviceServiceProxy, |
| mlme_req_stream: fidl_mlme::MlmeRequestStream, |
| exec: fasync::TestExecutor, |
| } |
| |
| fn setup_test() -> (TestHelper, Pin<Box<impl Future<Output = Result<(), anyhow::Error>>>>) { |
| let mut exec = fasync::TestExecutor::new_with_fake_time().expect("executor should build"); |
| exec.set_fake_time(fasync::Time::from_nanos(0)); |
| |
| let iface_map = Arc::new(IfaceMap::new()); |
| let iface = fake_client_iface(); |
| iface_map.insert(IFACE_ID, iface.iface); |
| let iface_counter = Arc::new(IfaceCounter::new()); |
| let (inspect_tree, _persistence_stream) = test_helper::fake_inspect_tree(); |
| let (dev_monitor_proxy, _) = |
| create_proxy::<fidl_fuchsia_wlan_device_service::DeviceMonitorMarker>() |
| .expect("failed to create DeviceMonitor proxy"); |
| let cfg = ServiceCfg { wep_supported: false, wpa1_supported: false }; |
| |
| let (dev_svc_proxy, dev_svc_req_stream) = |
| create_proxy_and_stream::<fidl_svc::DeviceServiceMarker>() |
| .expect("failed to create DeviceService proxy"); |
| let (persistence_req_sender, _persistence_stream) = |
| test_helper::create_inspect_persistence_channel(); |
| let mut test_fut = Box::pin(serve_device_requests( |
| iface_counter, |
| cfg, |
| iface_map, |
| dev_svc_req_stream, |
| inspect_tree, |
| dev_monitor_proxy, |
| persistence_req_sender, |
| )); |
| assert_variant!(exec.run_until_stalled(&mut test_fut), Poll::Pending); |
| |
| let test_helper = |
| TestHelper { dev_svc_proxy, mlme_req_stream: iface.mlme_req_stream, exec }; |
| (test_helper, test_fut) |
| } |
| |
| fn fake_histogram_stats() -> fidl_fuchsia_wlan_stats::IfaceHistogramStats { |
| fidl_fuchsia_wlan_stats::IfaceHistogramStats { |
| noise_floor_histograms: vec![fidl_fuchsia_wlan_stats::NoiseFloorHistogram { |
| hist_scope: fidl_fuchsia_wlan_stats::HistScope::PerAntenna, |
| antenna_id: Some(Box::new(fidl_fuchsia_wlan_stats::AntennaId { |
| freq: fidl_fuchsia_wlan_stats::AntennaFreq::Antenna2G, |
| index: 0, |
| })), |
| noise_floor_samples: vec![fidl_fuchsia_wlan_stats::HistBucket { |
| bucket_index: 200, |
| num_samples: 999, |
| }], |
| invalid_samples: 44, |
| }], |
| rssi_histograms: vec![fidl_fuchsia_wlan_stats::RssiHistogram { |
| hist_scope: fidl_fuchsia_wlan_stats::HistScope::PerAntenna, |
| antenna_id: Some(Box::new(fidl_fuchsia_wlan_stats::AntennaId { |
| freq: fidl_fuchsia_wlan_stats::AntennaFreq::Antenna2G, |
| index: 0, |
| })), |
| rssi_samples: vec![fidl_fuchsia_wlan_stats::HistBucket { |
| bucket_index: 230, |
| num_samples: 999, |
| }], |
| invalid_samples: 55, |
| }], |
| rx_rate_index_histograms: vec![ |
| fidl_fuchsia_wlan_stats::RxRateIndexHistogram { |
| hist_scope: fidl_fuchsia_wlan_stats::HistScope::Station, |
| antenna_id: None, |
| rx_rate_index_samples: vec![fidl_fuchsia_wlan_stats::HistBucket { |
| bucket_index: 99, |
| num_samples: 1400, |
| }], |
| invalid_samples: 22, |
| }, |
| fidl_fuchsia_wlan_stats::RxRateIndexHistogram { |
| hist_scope: fidl_fuchsia_wlan_stats::HistScope::PerAntenna, |
| antenna_id: Some(Box::new(fidl_fuchsia_wlan_stats::AntennaId { |
| freq: fidl_fuchsia_wlan_stats::AntennaFreq::Antenna5G, |
| index: 1, |
| })), |
| rx_rate_index_samples: vec![fidl_fuchsia_wlan_stats::HistBucket { |
| bucket_index: 100, |
| num_samples: 1500, |
| }], |
| invalid_samples: 33, |
| }, |
| ], |
| snr_histograms: vec![fidl_fuchsia_wlan_stats::SnrHistogram { |
| hist_scope: fidl_fuchsia_wlan_stats::HistScope::PerAntenna, |
| antenna_id: Some(Box::new(fidl_fuchsia_wlan_stats::AntennaId { |
| freq: fidl_fuchsia_wlan_stats::AntennaFreq::Antenna2G, |
| index: 0, |
| })), |
| snr_samples: vec![fidl_fuchsia_wlan_stats::HistBucket { |
| bucket_index: 30, |
| num_samples: 999, |
| }], |
| invalid_samples: 11, |
| }], |
| } |
| } |
| |
| struct FakeClientIface { |
| iface: IfaceDevice, |
| new_sme_clients: mpsc::UnboundedReceiver<wlan_sme::serve::client::Endpoint>, |
| mlme_req_stream: fidl_mlme::MlmeRequestStream, |
| } |
| |
| fn fake_client_iface() -> FakeClientIface { |
| let (sme_sender, sme_receiver) = mpsc::unbounded(); |
| let (mlme_proxy, mlme_req_stream) = |
| create_proxy_and_stream::<MlmeMarker>().expect("Error creating proxy"); |
| let (shutdown_sender, _) = mpsc::channel(1); |
| let device_info = fake_device_info(); |
| let iface = IfaceDevice { |
| phy_ownership: device::PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| sme_server: SmeServer::Client(sme_sender), |
| mlme_proxy, |
| device_info, |
| shutdown_sender, |
| }; |
| FakeClientIface { iface, new_sme_clients: sme_receiver, mlme_req_stream } |
| } |
| |
| struct FakeApIface { |
| iface: IfaceDevice, |
| new_sme_clients: mpsc::UnboundedReceiver<wlan_sme::serve::ap::Endpoint>, |
| } |
| |
| fn fake_ap_iface() -> FakeApIface { |
| let (sme_sender, sme_receiver) = mpsc::unbounded(); |
| let (mlme_proxy, _server) = create_proxy::<MlmeMarker>().expect("Error creating proxy"); |
| let (shutdown_sender, _) = mpsc::channel(1); |
| let device_info = fake_device_info(); |
| let iface = IfaceDevice { |
| phy_ownership: device::PhyOwnership { phy_id: 0, phy_assigned_id: 0 }, |
| sme_server: SmeServer::Ap(sme_sender), |
| mlme_proxy, |
| device_info, |
| shutdown_sender, |
| }; |
| FakeApIface { iface, new_sme_clients: sme_receiver } |
| } |
| |
| fn fake_device_info() -> fidl_mlme::DeviceInfo { |
| fidl_mlme::DeviceInfo { |
| role: fidl_common::WlanMacRole::Client, |
| bands: vec![], |
| sta_addr: [0xAC; 6], |
| softmac_hardware_capability: 0, |
| qos_capable: false, |
| } |
| } |
| |
| fn fake_discovery_support() -> fidl_common::DiscoverySupport { |
| fidl_common::DiscoverySupport { |
| scan_offload: fidl_common::ScanOffloadExtension { supported: true }, |
| probe_response_offload: fidl_common::ProbeResponseOffloadExtension { supported: false }, |
| } |
| } |
| |
| fn fake_mac_sublayer_support() -> fidl_common::MacSublayerSupport { |
| fidl_common::MacSublayerSupport { |
| rate_selection_offload: fidl_common::RateSelectionOffloadExtension { supported: false }, |
| data_plane: fidl_common::DataPlaneExtension { |
| data_plane_type: fidl_common::DataPlaneType::EthernetDevice, |
| }, |
| device: fidl_common::DeviceExtension { |
| is_synthetic: false, |
| mac_implementation_type: fidl_common::MacImplementationType::Fullmac, |
| tx_status_report_supported: false, |
| }, |
| } |
| } |
| |
| fn fake_security_support() -> fidl_common::SecuritySupport { |
| fidl_common::SecuritySupport { |
| sae: fidl_common::SaeFeature { |
| driver_handler_supported: false, |
| sme_handler_supported: true, |
| }, |
| mfp: fidl_common::MfpFeature { supported: false }, |
| } |
| } |
| |
| fn fake_spectrum_management_support() -> fidl_common::SpectrumManagementSupport { |
| fidl_common::SpectrumManagementSupport { dfs: fidl_common::DfsFeature { supported: false } } |
| } |
| |
| fn fake_scan_request() -> fidl_sme::ScanRequest { |
| fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {}) |
| } |
| |
| fn fake_ap_config() -> fidl_sme::ApConfig { |
| fidl_sme::ApConfig { |
| ssid: b"qwerty".to_vec(), |
| password: vec![], |
| radio_cfg: RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, 6).into(), |
| } |
| } |
| } |