blob: afd4082a603a1a6e0a05e1f92de8123ccdacc149 [file] [log] [blame]
// 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_common as fidl_common;
use fidl_fuchsia_wlan_device_service::{DeviceMonitorMarker, DeviceMonitorProxy};
use fidl_fuchsia_wlan_internal as fidl_internal;
use fuchsia_component::client::connect_to_protocol;
use fuchsia_sync::RwLock;
use fuchsia_zircon as zx;
use ieee80211::{MacAddr, Ssid};
use std::collections::HashMap;
use wlan_common::scan::ScanResult;
// 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()),
}
}
}