blob: 515b47eaf5a8977a4b4b599b6eb33743faa280d1 [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.
#![recursion_limit = "1024"]
mod access_point;
mod client;
mod config_management;
mod legacy;
mod mode_management;
mod regulatory_manager;
mod telemetry;
mod util;
use {
crate::{
client::network_selection::NetworkSelector,
config_management::{SavedNetworksManager, SavedNetworksManagerApi},
legacy::{device, IfaceRef},
mode_management::{
create_iface_manager, iface_manager_api::IfaceManagerApi, phy_manager::PhyManager,
},
regulatory_manager::RegulatoryManager,
telemetry::serve_telemetry,
},
anyhow::{format_err, Context as _, Error},
fidl::{endpoints::RequestStream, handle::AsyncChannel},
fidl_fuchsia_location_namedplace::RegulatoryRegionWatcherMarker,
fidl_fuchsia_process_lifecycle::{LifecycleRequest, LifecycleRequestStream},
fidl_fuchsia_wlan_device_service::{DeviceMonitorMarker, DeviceServiceMarker},
fidl_fuchsia_wlan_policy as fidl_policy, fuchsia_async as fasync,
fuchsia_async::DurationExt,
fuchsia_cobalt::{CobaltConnector, ConnectionType},
fuchsia_component::server::ServiceFs,
fuchsia_inspect::component,
fuchsia_runtime::{self as fruntime, HandleInfo, HandleType},
fuchsia_syslog as syslog,
fuchsia_zircon::{self as zx, prelude::*},
futures::{
self,
channel::{mpsc, oneshot},
future::{try_join, BoxFuture},
lock::Mutex,
prelude::*,
select, TryFutureExt,
},
log::{error, info, warn},
pin_utils::pin_mut,
std::{process, sync::Arc},
void::Void,
wlan_metrics_registry::{self as metrics},
};
const REGULATORY_LISTENER_TIMEOUT_SEC: i64 = 30;
async fn serve_fidl(
ap: access_point::AccessPoint,
configurator: legacy::deprecated_configuration::DeprecatedConfigurator,
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
legacy_client_ref: IfaceRef,
saved_networks: Arc<dyn SavedNetworksManagerApi>,
network_selector: Arc<NetworkSelector>,
client_sender: util::listener::ClientListenerMessageSender,
client_listener_msgs: mpsc::UnboundedReceiver<util::listener::ClientListenerMessage>,
ap_listener_msgs: mpsc::UnboundedReceiver<util::listener::ApMessage>,
regulatory_receiver: oneshot::Receiver<()>,
) -> Result<Void, Error> {
// Wait a bit for the country code to be set before serving the policy APIs.
let regulatory_listener_timeout =
fasync::Timer::new(REGULATORY_LISTENER_TIMEOUT_SEC.seconds().after_now());
select! {
_ = regulatory_listener_timeout.fuse() => {
info!(
"Country code was not set after {} seconds. Proceeding to serve policy API.",
REGULATORY_LISTENER_TIMEOUT_SEC,
);
},
result = regulatory_receiver.fuse() => {
match result {
Ok(()) => {
info!("Country code has been set. Proceeding to serve policy API.");
},
Err(e) => info!("Waiting for initial country code failed: {:?}", e),
}
}
}
let mut fs = ServiceFs::new();
inspect_runtime::serve(component::inspector(), &mut fs)?;
let client_sender1 = client_sender.clone();
let client_sender2 = client_sender.clone();
let second_ap = ap.clone();
let saved_networks_clone = saved_networks.clone();
let client_provider_lock = Arc::new(Mutex::new(()));
// TODO(sakuma): Once the legacy API is deprecated, the interface manager should default to
// stopped.
{
let mut iface_manager = iface_manager.lock().await;
iface_manager.start_client_connections().await?;
}
let _ = fs
.dir("svc")
.add_fidl_service(move |reqs| {
fasync::Task::spawn(client::serve_provider_requests(
iface_manager.clone(),
client_sender1.clone(),
Arc::clone(&saved_networks_clone),
Arc::clone(&network_selector),
client_provider_lock.clone(),
reqs,
))
.detach()
})
.add_fidl_service(move |reqs| {
fasync::Task::spawn(client::serve_listener_requests(client_sender2.clone(), reqs))
.detach()
})
.add_fidl_service(move |reqs| {
fasync::Task::spawn(ap.clone().serve_provider_requests(reqs)).detach()
})
.add_fidl_service(move |reqs| {
fasync::Task::spawn(second_ap.clone().serve_listener_requests(reqs)).detach()
})
.add_fidl_service(move |reqs| {
fasync::Task::spawn(configurator.clone().serve_deprecated_configuration(reqs)).detach()
})
.add_fidl_service(|reqs| {
let fut =
legacy::deprecated_client::serve_deprecated_client(reqs, legacy_client_ref.clone())
.unwrap_or_else(|e| error!("error serving deprecated client API: {}", e));
fasync::Task::spawn(fut).detach()
});
let service_fut = fs.take_and_serve_directory_handle()?.collect::<()>().fuse();
pin_mut!(service_fut);
let serve_client_policy_listeners = util::listener::serve::<
fidl_policy::ClientStateUpdatesProxy,
fidl_policy::ClientStateSummary,
util::listener::ClientStateUpdate,
>(client_listener_msgs)
.fuse();
pin_mut!(serve_client_policy_listeners);
let serve_ap_policy_listeners = util::listener::serve::<
fidl_policy::AccessPointStateUpdatesProxy,
Vec<fidl_policy::AccessPointState>,
util::listener::ApStatesUpdate,
>(ap_listener_msgs)
.fuse();
pin_mut!(serve_ap_policy_listeners);
loop {
select! {
_ = service_fut => (),
_ = serve_client_policy_listeners => (),
_ = serve_ap_policy_listeners => (),
}
}
}
/// Calls the metric recording function immediately and every 24 hours.
async fn saved_networks_manager_metrics_loop(saved_networks: Arc<dyn SavedNetworksManagerApi>) {
loop {
saved_networks.record_periodic_metrics().await;
fasync::Timer::new(24.hours().after_now()).await;
}
}
/// Runs the recording and sending of metrics to Cobalt.
async fn serve_metrics(
saved_networks: Arc<dyn SavedNetworksManagerApi>,
cobalt_fut: impl Future<Output = ()>,
) -> Result<(), Error> {
let record_metrics_fut = saved_networks_manager_metrics_loop(saved_networks);
try_join(record_metrics_fut.map(|()| Ok(())), cobalt_fut.map(|()| Ok(()))).await.map(|_| ())
}
// Some builds will not include the RegulatoryRegionWatcher. In such cases, wlancfg can continue
// to run, though it will not be able to set its country code and will fallback to world wide.
fn run_regulatory_manager(
iface_manager: Arc<Mutex<dyn IfaceManagerApi + Send>>,
regulatory_sender: oneshot::Sender<()>,
) -> BoxFuture<'static, Result<(), Error>> {
match fuchsia_component::client::connect_to_protocol::<RegulatoryRegionWatcherMarker>() {
Ok(regulatory_svc) => {
let regulatory_manager = RegulatoryManager::new(regulatory_svc, iface_manager);
let regulatory_fut = async move {
regulatory_manager.run(regulatory_sender).await.unwrap_or_else(|e| {
error!("regulatory manager failed: {:?}", e);
});
Ok(())
};
Box::pin(regulatory_fut)
}
Err(e) => {
error!("could not connect to regulatory manager: {:?}", e);
let regulatory_fut = async move { Ok(()) };
Box::pin(regulatory_fut)
}
}
}
// This lifecycle handler is required to make use of the "main_process_critical" functionality
// in Components V2. It ensures that our process exits cleanly when the system begins a shutdown.
// If the process were to exit with a non-zero exit code, the system would abruptly reboot rather
// than performing a clean shutdown.
async fn run_lifecycle_handler() -> Result<(), Error> {
match fruntime::take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0)) {
Some(lifecycle_handle) => {
let x: zx::Channel = lifecycle_handle.into();
let async_x = AsyncChannel::from(
fasync::Channel::from_channel(x).expect("Async channel conversion failed."),
);
let mut req_stream = LifecycleRequestStream::from_channel(async_x);
while let Some(request) =
req_stream.try_next().await.expect("Failure receiving lifecycle FIDL message")
{
match request {
LifecycleRequest::Stop { control_handle: _c } => {
info!("Receive lifecycle request to stop.");
process::exit(0);
}
}
}
// We only arrive here if the lifecycle channel closed without
// first sending the shutdown event, which is unexpected.
error!("Lifecycle channel closed without first sending shutdown event");
process::abort();
}
None => {
warn!("No lifecycle channel received, likely running as a v1 component.");
Ok(())
}
}
}
async fn run_all_futures() -> Result<(), Error> {
let wlan_svc = fuchsia_component::client::connect_to_protocol::<DeviceServiceMarker>()
.context("failed to connect to device service")?;
let monitor_svc = fuchsia_component::client::connect_to_protocol::<DeviceMonitorMarker>()
.context("failed to connect to device monitor")?;
let (cobalt_api, cobalt_fut) =
CobaltConnector::default().serve(ConnectionType::project_id(metrics::PROJECT_ID));
let (telemetry_sender, telemetry_fut) =
serve_telemetry(component::inspector().root().create_child("client_stats"));
let saved_networks = Arc::new(SavedNetworksManager::new(cobalt_api.clone()).await?);
let network_selector = Arc::new(NetworkSelector::new(
saved_networks.clone(),
cobalt_api.clone(),
component::inspector().root().create_child("network_selector"),
telemetry_sender.clone(),
));
let phy_manager = Arc::new(Mutex::new(PhyManager::new(
wlan_svc.clone(),
monitor_svc.clone(),
component::inspector().root().create_child("phy_manager"),
)));
let configurator =
legacy::deprecated_configuration::DeprecatedConfigurator::new(phy_manager.clone());
let (watcher_proxy, watcher_server_end) = fidl::endpoints::create_proxy()?;
monitor_svc.watch_devices(watcher_server_end)?;
let (client_sender, client_receiver) = mpsc::unbounded();
let (ap_sender, ap_receiver) = mpsc::unbounded();
let (iface_manager, iface_manager_service) = create_iface_manager(
phy_manager.clone(),
client_sender.clone(),
ap_sender.clone(),
wlan_svc.clone(),
saved_networks.clone(),
network_selector.clone(),
cobalt_api.clone(),
telemetry_sender.clone(),
);
let legacy_client = IfaceRef::new();
let listener = device::Listener::new(
wlan_svc.clone(),
legacy_client.clone(),
phy_manager.clone(),
iface_manager.clone(),
);
let (regulatory_sender, regulatory_receiver) = oneshot::channel();
let ap =
access_point::AccessPoint::new(iface_manager.clone(), ap_sender, Arc::new(Mutex::new(())));
let fidl_fut = serve_fidl(
ap,
configurator,
iface_manager.clone(),
legacy_client,
saved_networks.clone(),
network_selector,
client_sender,
client_receiver,
ap_receiver,
regulatory_receiver,
);
let dev_watcher_fut = watcher_proxy
.take_event_stream()
.try_for_each(|evt| device::handle_event(&listener, evt).map(Ok))
.err_into()
.and_then(|_| {
let result: Result<(), Error> =
Err(format_err!("Device watcher future exited unexpectedly"));
future::ready(result)
});
let metrics_fut = serve_metrics(saved_networks.clone(), cobalt_fut);
let regulatory_fut = run_regulatory_manager(iface_manager.clone(), regulatory_sender);
let lifecycle_fut = run_lifecycle_handler();
let _ = futures::try_join!(
fidl_fut,
dev_watcher_fut,
iface_manager_service,
metrics_fut,
regulatory_fut,
lifecycle_fut,
telemetry_fut.map(Ok),
)?;
Ok(())
}
// The return value from main() gets swallowed, including if it returns a Result<Err>. Therefore,
// use this simple wrapper to ensure that any errors from run_all_futures() are printed to the log.
#[fasync::run_singlethreaded]
async fn main() {
// Initialize logging with a tag that can be used to select these logs for forwarding to console
syslog::init_with_tags(&["wlan"]).expect("Syslog init should not fail");
if let Err(e) = run_all_futures().await {
error!("{:?}", e);
}
}