| // 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 crate::wlan::types; |
| use anyhow::{Context as _, Error}; |
| use fidl_fuchsia_wlan_device_service::{DeviceMonitorMarker, DeviceMonitorProxy}; |
| use fuchsia_component::client::connect_to_protocol; |
| use fuchsia_sync::RwLock; |
| use ieee80211::{MacAddr, Ssid}; |
| use std::collections::HashMap; |
| use wlan_common::scan::ScanResult; |
| use { |
| fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_internal as fidl_internal, |
| fuchsia_zircon as zx, |
| }; |
| |
| // WlanFacade: proxies commands from sl4f test to proper fidl APIs |
| // |
| // This object is shared among all threads created by server. The inner object is the facade |
| // itself. Callers interact with a wrapped version of the facade that enforces read/write |
| // protection. |
| // |
| // Use: Create once per server instantiation. |
| #[derive(Debug)] |
| struct InnerWlanFacade { |
| // TODO(https://fxbug.dev/42165549) |
| #[allow(unused)] |
| scan_results: bool, |
| } |
| |
| #[derive(Debug)] |
| pub(crate) struct WlanFacade { |
| monitor_svc: DeviceMonitorProxy, |
| // TODO(https://fxbug.dev/42165549) |
| #[allow(unused)] |
| inner: RwLock<InnerWlanFacade>, |
| } |
| |
| impl WlanFacade { |
| pub fn new() -> Result<WlanFacade, Error> { |
| let monitor_svc = connect_to_protocol::<DeviceMonitorMarker>()?; |
| |
| Ok(WlanFacade { monitor_svc, inner: RwLock::new(InnerWlanFacade { scan_results: false }) }) |
| } |
| |
| /// Gets the list of wlan interface IDs. |
| pub async fn get_iface_id_list(&self) -> Result<Vec<u16>, Error> { |
| let wlan_iface_ids = wlan_service_util::get_iface_list(&self.monitor_svc) |
| .await |
| .context("Get Iface Id List: failed to get wlan iface list")?; |
| Ok(wlan_iface_ids) |
| } |
| |
| /// Gets the list of wlan interface IDs. |
| pub async fn get_phy_id_list(&self) -> Result<Vec<u16>, Error> { |
| let wlan_phy_ids = wlan_service_util::get_phy_list(&self.monitor_svc) |
| .await |
| .context("Get Phy Id List: failed to get wlan phy list")?; |
| Ok(wlan_phy_ids) |
| } |
| |
| pub async fn scan(&self) -> Result<Vec<String>, Error> { |
| // get the first client interface |
| let sme_proxy = wlan_service_util::client::get_first_sme(&self.monitor_svc) |
| .await |
| .context("Scan: failed to get client iface sme proxy")?; |
| |
| // start the scan |
| let scan_result_list = |
| wlan_service_util::client::passive_scan(&sme_proxy).await.context("Scan failed")?; |
| |
| // send the ssids back to the test |
| let mut ssid_list = Vec::new(); |
| for scan_result in &scan_result_list { |
| let scan_result: ScanResult = scan_result.clone().try_into()?; |
| let ssid = String::from_utf8_lossy(&scan_result.bss_description.ssid).into_owned(); |
| ssid_list.push(ssid); |
| } |
| Ok(ssid_list) |
| } |
| |
| async fn passive_scan( |
| &self, |
| ) -> Result<impl IntoIterator<Item = Result<ScanResult, Error>>, Error> { |
| // get the first client interface |
| let sme_proxy = wlan_service_util::client::get_first_sme(&self.monitor_svc) |
| .await |
| .context("Scan: failed to get client iface sme proxy")?; |
| // start the scan |
| Ok(wlan_service_util::client::passive_scan(&sme_proxy) |
| .await |
| .context("Scan failed")? |
| .into_iter() |
| .map(ScanResult::try_from)) |
| } |
| |
| pub async fn scan_for_bss_info( |
| &self, |
| ) -> Result<HashMap<String, Vec<Box<types::BssDescriptionDef>>>, Error> { |
| let mut scan_results_by_ssid_string = HashMap::new(); |
| for scan_result in self.passive_scan().await? { |
| let scan_result = scan_result.context("Failed to convert scan result")?; |
| let entry = scan_results_by_ssid_string |
| .entry(String::from(scan_result.bss_description.ssid.to_string_not_redactable())) |
| .or_insert(vec![]); |
| |
| let fidl_bss_desc: fidl_internal::BssDescription = scan_result.bss_description.into(); |
| entry.push(Box::new(fidl_bss_desc.into())); |
| } |
| Ok(scan_results_by_ssid_string) |
| } |
| |
| pub async fn connect( |
| &self, |
| target_ssid: Ssid, |
| target_pwd: Vec<u8>, |
| target_bss_desc: fidl_internal::BssDescription, |
| ) -> Result<bool, Error> { |
| let sme_proxy = wlan_service_util::client::get_first_sme(&self.monitor_svc) |
| .await |
| .context("Connect: failed to get client iface sme proxy")?; |
| wlan_service_util::client::connect(&sme_proxy, target_ssid, target_pwd, target_bss_desc) |
| .await |
| } |
| |
| pub async fn create_iface( |
| &self, |
| phy_id: u16, |
| role: fidl_common::WlanMacRole, |
| sta_addr: MacAddr, |
| ) -> Result<u16, Error> { |
| let iface_id = wlan_service_util::create_iface(&self.monitor_svc, phy_id, role, sta_addr) |
| .await |
| .context("Create: Failed to create iface")?; |
| |
| Ok(iface_id) |
| } |
| |
| /// Destroys a WLAN interface by input interface ID. |
| /// |
| /// # Arguments |
| /// * `iface_id` - The u16 interface id. |
| pub async fn destroy_iface(&self, iface_id: u16) -> Result<(), Error> { |
| wlan_service_util::destroy_iface(&self.monitor_svc, iface_id) |
| .await |
| .context("Destroy: Failed to destroy iface") |
| } |
| |
| pub async fn disconnect(&self) -> Result<(), Error> { |
| wlan_service_util::client::disconnect_all(&self.monitor_svc) |
| .await |
| .context("Disconnect: Failed to disconnect ifaces") |
| } |
| |
| pub async fn status(&self) -> Result<types::ClientStatusResponseDef, Error> { |
| // get the first client interface |
| let sme_proxy = wlan_service_util::client::get_first_sme(&self.monitor_svc) |
| .await |
| .context("Status: failed to get iface sme proxy")?; |
| |
| let rsp = sme_proxy.status().await.context("failed to get status from sme_proxy")?; |
| |
| Ok(rsp.into()) |
| } |
| |
| pub async fn query_iface( |
| &self, |
| iface_id: u16, |
| ) -> Result<types::QueryIfaceResponseWrapper, Error> { |
| let iface_info = self |
| .monitor_svc |
| .query_iface(iface_id) |
| .await |
| .context("Failed to query iface information")? |
| .map_err(|e| zx::Status::from_raw(e)); |
| |
| match iface_info { |
| Ok(info) => Ok(types::QueryIfaceResponseWrapper(info.into())), |
| Err(zx::Status::NOT_FOUND) => { |
| Err(format_err!("no iface information for ID: {}", iface_id)) |
| } |
| Err(e) => Err(e.into()), |
| } |
| } |
| } |