blob: adf0a6511d03ff86d8f7156f641f54c929d7938c [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.
use {
crate::legacy::IfaceRef,
fidl, fidl_fuchsia_wlan_product_deprecatedclient as deprecated,
fidl_fuchsia_wlan_sme as fidl_sme,
futures::prelude::*,
log::{debug, error},
};
const MAX_CONCURRENT_WLAN_REQUESTS: usize = 1000;
/// Takes in stream of deprecated client requests and handles each one.
pub(crate) async fn serve_deprecated_client(
requests: deprecated::DeprecatedClientRequestStream,
iface: IfaceRef,
) -> Result<(), fidl::Error> {
requests
.try_for_each_concurrent(MAX_CONCURRENT_WLAN_REQUESTS, |req| {
handle_request(iface.clone(), req)
})
.await
}
/// Handles an individual request from the deprecated client API.
async fn handle_request(
iface: IfaceRef,
req: deprecated::DeprecatedClientRequest,
) -> Result<(), fidl::Error> {
match req {
deprecated::DeprecatedClientRequest::Status { responder } => {
debug!("Deprecated WLAN client API used for status request");
let mut r = status(&iface).await;
responder.send(&mut r)
}
}
}
/// Produces a status representing the state where no client interface is present.
fn no_client_status() -> deprecated::WlanStatus {
deprecated::WlanStatus { state: deprecated::State::NoClient, current_ap: None }
}
/// Manages the calling of client SME status and translation into a format that is compatible with
/// the deprecated client API.
async fn status(iface: &IfaceRef) -> deprecated::WlanStatus {
let iface = match iface.get() {
Ok(iface) => iface,
Err(_) => return no_client_status(),
};
let status = match iface.sme.status().await {
Ok(status) => status,
Err(e) => {
// An error here indicates that the SME channel is broken.
error!("Failed to query status: {}", e);
return no_client_status();
}
};
deprecated::WlanStatus {
state: convert_state(&status),
current_ap: extract_current_ap(&status),
}
}
/// Translates a client SME's status information into a deprecated client state.
fn convert_state(status: &fidl_sme::ClientStatusResponse) -> deprecated::State {
if status.connected_to.is_some() {
deprecated::State::Associated
} else if !status.connecting_to_ssid.is_empty() {
deprecated::State::Associating
} else {
deprecated::State::Disassociated
}
}
/// Parses a Client SME's status and extracts AP SSID and RSSI if applicable.
fn extract_current_ap(status: &fidl_sme::ClientStatusResponse) -> Option<Box<deprecated::Ap>> {
match status.connected_to.as_ref() {
None => None,
Some(bss_info) => {
let ssid = std::string::String::from_utf8_lossy(&bss_info.ssid).to_string();
let rssi_dbm = bss_info.rssi_dbm;
Some(Box::new(deprecated::Ap { ssid, rssi_dbm }))
}
}
}
#[cfg(test)]
mod tests {
use {
super::*, crate::legacy::Iface, fidl::endpoints::create_proxy,
fidl_fuchsia_wlan_common as fidl_common, fuchsia_async as fasync, futures::task::Poll,
pin_utils::pin_mut, wlan_common::assert_variant,
};
struct TestValues {
iface: IfaceRef,
sme_stream: fidl_sme::ClientSmeRequestStream,
}
fn test_setup() -> TestValues {
let (sme, server) =
create_proxy::<fidl_sme::ClientSmeMarker>().expect("failed to create ClientSmeProxy");
let iface = Iface { sme, iface_id: 0 };
let iface_ref = IfaceRef::new();
iface_ref.set_if_empty(iface);
TestValues {
iface: iface_ref,
sme_stream: server.into_stream().expect("failed to create ClientSmeRequestStream"),
}
}
#[test]
fn test_no_client() {
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let iface = IfaceRef::new();
let status_fut = status(&iface);
pin_mut!(status_fut);
// Expect that no client is reported and the AP status information is empty.
assert_variant!(
exec.run_until_stalled(&mut status_fut),
Poll::Ready(deprecated::WlanStatus {
state: deprecated::State::NoClient,
current_ap: None,
})
);
}
#[test]
fn test_broken_sme() {
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let test_values = test_setup();
// Drop the SME request stream so that the client request will fail.
drop(test_values.sme_stream);
let status_fut = status(&test_values.iface);
pin_mut!(status_fut);
// Expect that no client is reported and the AP status information is empty.
assert_variant!(
exec.run_until_stalled(&mut status_fut),
Poll::Ready(deprecated::WlanStatus {
state: deprecated::State::NoClient,
current_ap: None,
})
);
}
#[test]
fn test_disconnected_client() {
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let mut test_values = test_setup();
let status_fut = status(&test_values.iface);
pin_mut!(status_fut);
// Expect an SME status request and send back a response indicating that the SME is neither
// connected nor connecting.
assert_variant!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
responder.send(&mut fidl_sme::ClientStatusResponse{
connecting_to_ssid: vec![],
connected_to: None,
}).expect("could not send sme response")
}
);
// Expect a disconnected status.
assert_variant!(
exec.run_until_stalled(&mut status_fut),
Poll::Ready(deprecated::WlanStatus {
state: deprecated::State::Disassociated,
current_ap: None,
})
);
}
#[test]
fn test_connecting_client() {
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let mut test_values = test_setup();
let status_fut = status(&test_values.iface);
pin_mut!(status_fut);
// Expect an SME status request and send back a response indicating that the SME is
// connecting.
assert_variant!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
responder.send(&mut fidl_sme::ClientStatusResponse{
connecting_to_ssid: "test_ssid".as_bytes().to_vec(),
connected_to: None,
}).expect("could not send sme response")
}
);
// Expect a connecting status.
assert_variant!(
exec.run_until_stalled(&mut status_fut),
Poll::Ready(deprecated::WlanStatus {
state: deprecated::State::Associating,
current_ap: None,
})
);
}
#[test]
fn test_connected_client() {
let mut exec = fasync::Executor::new().expect("failed to create an executor");
let mut test_values = test_setup();
let ssid = "test_ssid";
let rssi_dbm = -70;
let status_fut = status(&test_values.iface);
pin_mut!(status_fut);
// Expect an SME status request and send back a response indicating that the SME is
// connected.
assert_variant!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
assert_variant!(
exec.run_until_stalled(&mut test_values.sme_stream.next()),
Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
responder.send(&mut fidl_sme::ClientStatusResponse{
connecting_to_ssid: vec![],
connected_to: Some(Box::new(fidl_sme::BssInfo{
bssid: [0, 0, 0, 0, 0, 0],
ssid: ssid.as_bytes().to_vec(),
rssi_dbm,
snr_db: 0,
channel: fidl_common::WlanChan {
primary: 1,
cbw: fidl_common::Cbw::Cbw20,
secondary80: 0,
},
protection: fidl_sme::Protection::Unknown,
compatible: true,
bss_desc: None,
}))
}).expect("could not send sme response")
}
);
// Expect a connected status.
let expected_current_ap =
Some(Box::new(deprecated::Ap { ssid: ssid.to_string(), rssi_dbm }));
assert_variant!(
exec.run_until_stalled(&mut status_fut),
Poll::Ready(deprecated::WlanStatus {
state: deprecated::State::Associated,
current_ap,
}) => {
assert_eq!(current_ap, expected_current_ap);
}
);
}
}