| // 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::{ |
| client::{ |
| connection_selection::ConnectionSelectionRequester, |
| roaming::local_roam_manager::LocalRoamManagerApi, |
| }, |
| config_management::SavedNetworksManagerApi, |
| telemetry::TelemetrySender, |
| util::listener, |
| }, |
| anyhow::Error, |
| fuchsia_async as fasync, |
| futures::{channel::mpsc, lock::Mutex, Future}, |
| std::{convert::Infallible, sync::Arc}, |
| }; |
| |
| pub mod device_monitor; |
| mod iface_manager; |
| pub mod iface_manager_api; |
| mod iface_manager_types; |
| pub mod phy_manager; |
| pub mod recovery; |
| |
| pub fn create_iface_manager( |
| phy_manager: Arc<Mutex<dyn phy_manager::PhyManagerApi + Send>>, |
| client_update_sender: listener::ClientListenerMessageSender, |
| ap_update_sender: listener::ApListenerMessageSender, |
| dev_monitor_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy, |
| saved_networks: Arc<dyn SavedNetworksManagerApi>, |
| local_roam_manager: Arc<Mutex<dyn LocalRoamManagerApi>>, |
| connection_selection_requester: ConnectionSelectionRequester, |
| telemetry_sender: TelemetrySender, |
| recovery_receiver: recovery::RecoveryActionReceiver, |
| ) -> (Arc<Mutex<iface_manager_api::IfaceManager>>, impl Future<Output = Result<Infallible, Error>>) |
| { |
| let (sender, receiver) = mpsc::channel(0); |
| let iface_manager_sender = Arc::new(Mutex::new(iface_manager_api::IfaceManager { sender })); |
| let (defect_sender, defect_receiver) = mpsc::unbounded(); |
| let iface_manager = iface_manager::IfaceManagerService::new( |
| phy_manager, |
| client_update_sender, |
| ap_update_sender, |
| dev_monitor_proxy, |
| saved_networks, |
| connection_selection_requester, |
| local_roam_manager, |
| telemetry_sender, |
| defect_sender, |
| ); |
| let iface_manager_service = iface_manager::serve_iface_manager_requests( |
| iface_manager, |
| receiver, |
| defect_receiver, |
| recovery_receiver, |
| ); |
| |
| (iface_manager_sender, iface_manager_service) |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum PhyFailure { |
| IfaceCreationFailure { phy_id: u16 }, |
| IfaceDestructionFailure { phy_id: u16 }, |
| } |
| |
| #[derive(Clone, Copy, Debug)] |
| pub enum IfaceFailure { |
| CanceledScan { iface_id: u16 }, |
| FailedScan { iface_id: u16 }, |
| EmptyScanResults { iface_id: u16 }, |
| ApStartFailure { iface_id: u16 }, |
| ConnectionFailure { iface_id: u16 }, |
| } |
| |
| // Interfaces will come and go and each one will receive a different ID. The failures are |
| // ultimately all associated with a given PHY and we will be interested in tallying up how many |
| // of a given failure type a PHY has seen when making recovery decisions. As such, only the |
| // IfaceFailure variant should be considered when determining equality. The contained interface ID |
| // is useful only for associating a failure with a PHY. |
| impl PartialEq for IfaceFailure { |
| fn eq(&self, other: &Self) -> bool { |
| match (*self, *other) { |
| (IfaceFailure::CanceledScan { .. }, IfaceFailure::CanceledScan { .. }) => true, |
| (IfaceFailure::FailedScan { .. }, IfaceFailure::FailedScan { .. }) => true, |
| (IfaceFailure::EmptyScanResults { .. }, IfaceFailure::EmptyScanResults { .. }) => true, |
| (IfaceFailure::ApStartFailure { .. }, IfaceFailure::ApStartFailure { .. }) => true, |
| (IfaceFailure::ConnectionFailure { .. }, IfaceFailure::ConnectionFailure { .. }) => { |
| true |
| } |
| _ => false, |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum Defect { |
| Phy(PhyFailure), |
| Iface(IfaceFailure), |
| } |
| |
| #[derive(Debug, PartialEq)] |
| struct Event<T: PartialEq> { |
| value: T, |
| time: fasync::Time, |
| } |
| |
| impl<T: PartialEq> Event<T> { |
| fn new(value: T, time: fasync::Time) -> Self { |
| Event { value, time } |
| } |
| } |
| |
| pub struct EventHistory<T: PartialEq> { |
| events: Vec<Event<T>>, |
| retention_time: fuchsia_zircon::Duration, |
| } |
| |
| impl<T: PartialEq> EventHistory<T> { |
| fn new(retention_seconds: u32) -> Self { |
| EventHistory { |
| events: Vec::new(), |
| retention_time: fuchsia_zircon::Duration::from_seconds(retention_seconds as i64), |
| } |
| } |
| |
| fn add_event(&mut self, value: T) { |
| let curr_time = fasync::Time::now(); |
| self.events.push(Event::new(value, curr_time)); |
| self.retain_unexpired_events(curr_time); |
| } |
| |
| fn event_count(&mut self, value: T) -> usize { |
| let curr_time = fasync::Time::now(); |
| self.retain_unexpired_events(curr_time); |
| self.events.iter().filter(|event| event.value == value).count() |
| } |
| |
| fn time_since_last_event(&mut self, value: T) -> Option<fuchsia_zircon::Duration> { |
| let curr_time = fasync::Time::now(); |
| self.retain_unexpired_events(curr_time); |
| |
| for event in self.events.iter().rev() { |
| if event.value == value { |
| return Some(curr_time - event.time); |
| } |
| } |
| None |
| } |
| |
| fn retain_unexpired_events(&mut self, curr_time: fasync::Time) { |
| let oldest_allowed_time = curr_time - self.retention_time; |
| self.events.retain(|event| event.time > oldest_allowed_time) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| fuchsia_async::TestExecutor, |
| rand::Rng, |
| test_util::{assert_gt, assert_lt}, |
| }; |
| |
| #[fuchsia::test] |
| fn test_event_retention() { |
| // Allow for events to be retained for at most 1s. |
| let mut event_history = EventHistory::<()>::new(1); |
| |
| // Add events at 0, 1, 2, 2 and a little bit seconds, and 3s. |
| event_history.events = vec![ |
| Event::<()> { value: (), time: fasync::Time::from_nanos(0) }, |
| Event::<()> { value: (), time: fasync::Time::from_nanos(1_000_000_000) }, |
| Event::<()> { value: (), time: fasync::Time::from_nanos(2_000_000_000) }, |
| Event::<()> { value: (), time: fasync::Time::from_nanos(2_000_000_001) }, |
| Event::<()> { value: (), time: fasync::Time::from_nanos(3_000_000_000) }, |
| ]; |
| |
| // Retain those events within the retention window based on a current time of 3s. |
| event_history.retain_unexpired_events(fasync::Time::from_nanos(3_000_000_000)); |
| |
| // It is expected that the events at 2 and a little bit seconds and 3s are retained while |
| // the others are discarded. |
| assert_eq!( |
| event_history.events, |
| vec![ |
| Event::<()> { value: (), time: fasync::Time::from_nanos(2_000_000_001) }, |
| Event::<()> { value: (), time: fasync::Time::from_nanos(3_000_000_000) }, |
| ] |
| ); |
| } |
| |
| #[derive(Debug, PartialEq)] |
| enum TestEnum { |
| Foo, |
| Bar, |
| } |
| |
| #[fuchsia::test] |
| fn test_time_since_last_event() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| |
| // Allow events to be stored basically forever. The goal here is to ensure that the |
| // retention policy does not discard any of our events. |
| let mut event_history = EventHistory::<TestEnum>::new(u32::MAX); |
| |
| // Add some events with known timestamps. |
| let foo_time: i64 = 1_123_123_123; |
| let bar_time: i64 = 2_222_222_222; |
| event_history.events = vec![ |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(foo_time) }, |
| Event { value: TestEnum::Bar, time: fasync::Time::from_nanos(bar_time) }, |
| ]; |
| |
| // Get the time before and after the function calls were made. This allows for some slack |
| // in evaluating whether the time calculations are in the realm of accurate. |
| let start_time = fasync::Time::now().into_nanos(); |
| let time_since_foo = |
| event_history.time_since_last_event(TestEnum::Foo).expect("Foo was not retained"); |
| let time_since_bar = |
| event_history.time_since_last_event(TestEnum::Bar).expect("Bar was not retained"); |
| let end_time = fasync::Time::now().into_nanos(); |
| |
| // Make sure the returned durations are within bounds. |
| assert_lt!(time_since_foo.into_nanos(), end_time - foo_time); |
| assert_gt!(time_since_foo.into_nanos(), start_time - foo_time); |
| |
| assert_lt!(time_since_bar.into_nanos(), end_time - bar_time); |
| assert_gt!(time_since_bar.into_nanos(), start_time - bar_time); |
| } |
| |
| #[fuchsia::test] |
| fn test_time_since_last_event_retention() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| |
| // Set the retention time to slightly less than the current time. This number will be |
| // positive. Since it will occupy the positive range of i64, it is safe to cast it as u32. |
| let curr_time_seconds = fasync::Time::now().into_nanos() / 1_000_000_000; |
| let mut event_history = EventHistory::<()>::new((curr_time_seconds - 1) as u32); |
| |
| // Put in an event at time zero so that it will not be retained when querying recent |
| // events. |
| event_history.events.push(Event::<()> { value: (), time: fasync::Time::from_nanos(0) }); |
| |
| assert_eq!(event_history.time_since_last_event(()), None); |
| } |
| #[fuchsia::test] |
| fn test_add_event() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| let mut event_history = EventHistory::<()>::new(u32::MAX); |
| |
| // Add a few events |
| let num_events = 3; |
| let start_time = fasync::Time::now().into_nanos(); |
| for _ in 0..num_events { |
| event_history.add_event(()); |
| } |
| let end_time = fasync::Time::now().into_nanos(); |
| |
| // All three of the recent events should have been retained. |
| assert_eq!(event_history.events.len(), num_events); |
| |
| // Verify that all of the even timestamps are within range. |
| for event in event_history.events { |
| let event_time = event.time.into_nanos(); |
| assert_lt!(event_time, end_time); |
| assert_gt!(event_time, start_time); |
| } |
| } |
| |
| #[fuchsia::test] |
| fn test_add_event_retention() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| |
| // Set the retention time to slightly less than the current time. This number will be |
| // positive. Since it will occupy the positive range of i64, it is safe to cast it as u32. |
| let curr_time_seconds = fasync::Time::now().into_nanos() / 1_000_000_000; |
| let mut event_history = EventHistory::<()>::new((curr_time_seconds - 1) as u32); |
| |
| // Put in an event at time zero so that it will not be retained when querying recent |
| // events. |
| event_history.events.push(Event::<()> { value: (), time: fasync::Time::from_nanos(0) }); |
| |
| // Add an event and observe that the event from time 0 has been removed. |
| let start_time = fasync::Time::now().into_nanos(); |
| event_history.add_event(()); |
| assert_eq!(event_history.events.len(), 1); |
| |
| // Add a couple more events. |
| event_history.add_event(()); |
| event_history.add_event(()); |
| let end_time = fasync::Time::now().into_nanos(); |
| |
| // All three of the recent events should have been retained. |
| assert_eq!(event_history.events.len(), 3); |
| |
| // Verify that all of the even timestamps are within range. |
| for event in event_history.events { |
| let event_time = event.time.into_nanos(); |
| assert_lt!(event_time, end_time); |
| assert_gt!(event_time, start_time); |
| } |
| } |
| |
| #[fuchsia::test] |
| fn test_event_count() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| let mut event_history = EventHistory::<TestEnum>::new(u32::MAX); |
| |
| event_history.events = vec![ |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(0) }, |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(1) }, |
| Event { value: TestEnum::Bar, time: fasync::Time::from_nanos(2) }, |
| Event { value: TestEnum::Bar, time: fasync::Time::from_nanos(3) }, |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(4) }, |
| ]; |
| |
| assert_eq!(event_history.event_count(TestEnum::Foo), 3); |
| assert_eq!(event_history.event_count(TestEnum::Bar), 2); |
| } |
| |
| #[fuchsia::test] |
| fn test_event_count_retention() { |
| // An executor is required to enable querying time. |
| let _exec = TestExecutor::new(); |
| |
| // Set the retention time to slightly less than the current time. This number will be |
| // positive. Since it will occupy the positive range of i64, it is safe to cast it as u32. |
| let curr_time_seconds = fasync::Time::now().into_nanos() / 1_000_000_000; |
| let mut event_history = EventHistory::<TestEnum>::new((curr_time_seconds - 1) as u32); |
| |
| event_history.events = vec![ |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(0) }, |
| Event { value: TestEnum::Foo, time: fasync::Time::from_nanos(0) }, |
| Event { value: TestEnum::Bar, time: fasync::Time::now() }, |
| Event { value: TestEnum::Bar, time: fasync::Time::now() }, |
| Event { value: TestEnum::Foo, time: fasync::Time::now() }, |
| ]; |
| |
| assert_eq!(event_history.event_count(TestEnum::Foo), 1); |
| assert_eq!(event_history.event_count(TestEnum::Bar), 2); |
| } |
| |
| #[fuchsia::test] |
| fn test_failure_equality() { |
| let mut rng = rand::thread_rng(); |
| assert_eq!( |
| IfaceFailure::CanceledScan { iface_id: rng.gen::<u16>() }, |
| IfaceFailure::CanceledScan { iface_id: rng.gen::<u16>() } |
| ); |
| assert_eq!( |
| IfaceFailure::FailedScan { iface_id: rng.gen::<u16>() }, |
| IfaceFailure::FailedScan { iface_id: rng.gen::<u16>() } |
| ); |
| assert_eq!( |
| IfaceFailure::EmptyScanResults { iface_id: rng.gen::<u16>() }, |
| IfaceFailure::EmptyScanResults { iface_id: rng.gen::<u16>() } |
| ); |
| assert_eq!( |
| IfaceFailure::ApStartFailure { iface_id: rng.gen::<u16>() }, |
| IfaceFailure::ApStartFailure { iface_id: rng.gen::<u16>() } |
| ); |
| assert_eq!( |
| IfaceFailure::ConnectionFailure { iface_id: rng.gen::<u16>() }, |
| IfaceFailure::ConnectionFailure { iface_id: rng.gen::<u16>() } |
| ); |
| } |
| } |