Skip to main content

wlancfg_lib/mode_management/
mod.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::client::connection_selection::ConnectionSelectionRequester;
6use crate::client::roaming::local_roam_manager::RoamManager;
7use crate::config_management::SavedNetworksManagerApi;
8use crate::telemetry::TelemetrySender;
9use crate::util::listener;
10use anyhow::Error;
11use fuchsia_async as fasync;
12use fuchsia_inspect::Node as InspectNode;
13use fuchsia_inspect_contrib::inspect_insert;
14use fuchsia_inspect_contrib::log::WriteInspect;
15use futures::Future;
16use futures::channel::mpsc;
17use futures::lock::Mutex;
18use std::borrow::Cow;
19use std::convert::Infallible;
20use std::sync::Arc;
21use wlan_telemetry::TimeoutSource;
22
23pub mod device_monitor;
24mod iface_manager;
25pub mod iface_manager_api;
26mod iface_manager_types;
27pub mod phy_manager;
28pub mod recovery;
29
30pub const DEFECT_CHANNEL_SIZE: usize = 100;
31
32pub fn create_iface_manager(
33    phy_manager: Arc<Mutex<dyn phy_manager::PhyManagerApi>>,
34    client_update_sender: listener::ClientListenerMessageSender,
35    ap_update_sender: listener::ApListenerMessageSender,
36    dev_monitor_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
37    saved_networks: Arc<dyn SavedNetworksManagerApi>,
38    connection_selection_requester: ConnectionSelectionRequester,
39    roam_manager: RoamManager,
40    telemetry_sender: TelemetrySender,
41    defect_sender: mpsc::Sender<Defect>,
42    defect_receiver: mpsc::Receiver<Defect>,
43    recovery_receiver: recovery::RecoveryActionReceiver,
44    node: fuchsia_inspect::Node,
45    power_manager: Arc<dyn wlan_power_manager::PowerManager>,
46) -> (Arc<Mutex<iface_manager_api::IfaceManager>>, impl Future<Output = Result<Infallible, Error>>)
47{
48    let (sender, receiver) = mpsc::channel(0);
49    let iface_manager_sender = Arc::new(Mutex::new(iface_manager_api::IfaceManager { sender }));
50    let iface_manager = iface_manager::IfaceManagerService::new(
51        phy_manager,
52        client_update_sender,
53        ap_update_sender,
54        dev_monitor_proxy,
55        saved_networks,
56        connection_selection_requester,
57        roam_manager,
58        telemetry_sender,
59        defect_sender,
60        node,
61        power_manager,
62    );
63    let iface_manager_service = iface_manager::serve_iface_manager_requests(
64        iface_manager,
65        receiver,
66        defect_receiver,
67        recovery_receiver,
68    );
69
70    (iface_manager_sender, iface_manager_service)
71}
72
73#[derive(Clone, Copy, Debug, PartialEq)]
74pub enum PhyFailure {
75    IfaceCreationFailure { phy_id: u16 },
76    IfaceDestructionFailure { phy_id: u16 },
77}
78
79#[derive(Clone, Copy, Debug)]
80pub enum IfaceFailure {
81    CanceledScan { iface_id: u16 },
82    FailedScan { iface_id: u16 },
83    EmptyScanResults { iface_id: u16 },
84    ApStartFailure { iface_id: u16 },
85    ConnectionFailure { iface_id: u16 },
86    Timeout { iface_id: u16, source: TimeoutSource },
87}
88
89// Interfaces will come and go and each one will receive a different ID.  The failures are
90// ultimately all associated with a given PHY and we will be interested in tallying up how many
91// of a given failure type a PHY has seen when making recovery decisions.  As such, only the
92// IfaceFailure variant should be considered when determining equality.  The contained interface ID
93// is useful only for associating a failure with a PHY.
94impl PartialEq for IfaceFailure {
95    fn eq(&self, other: &Self) -> bool {
96        #[allow(
97            clippy::match_like_matches_macro,
98            reason = "mass allow for https://fxbug.dev/381896734"
99        )]
100        match (*self, *other) {
101            (IfaceFailure::CanceledScan { .. }, IfaceFailure::CanceledScan { .. }) => true,
102            (IfaceFailure::FailedScan { .. }, IfaceFailure::FailedScan { .. }) => true,
103            (IfaceFailure::EmptyScanResults { .. }, IfaceFailure::EmptyScanResults { .. }) => true,
104            (IfaceFailure::ApStartFailure { .. }, IfaceFailure::ApStartFailure { .. }) => true,
105            (IfaceFailure::ConnectionFailure { .. }, IfaceFailure::ConnectionFailure { .. }) => {
106                true
107            }
108            (IfaceFailure::Timeout { .. }, IfaceFailure::Timeout { .. }) => true,
109            _ => false,
110        }
111    }
112}
113
114#[derive(Clone, Copy, Debug, PartialEq)]
115pub enum Defect {
116    Phy(PhyFailure),
117    Iface(IfaceFailure),
118}
119
120impl WriteInspect for Defect {
121    fn write_inspect<'a>(&self, writer: &InspectNode, key: impl Into<Cow<'a, str>>) {
122        match self {
123            Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id }) => {
124                inspect_insert!(writer, var key: {IfaceCreationFailure: {phy_id: phy_id}})
125            }
126            Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }) => {
127                inspect_insert!(writer, var key: {IfaceDestructionFailure: {phy_id: phy_id}})
128            }
129            Defect::Iface(IfaceFailure::CanceledScan { iface_id }) => {
130                inspect_insert!(writer, var key: {CanceledScan: {iface_id: iface_id}})
131            }
132            Defect::Iface(IfaceFailure::FailedScan { iface_id }) => {
133                inspect_insert!(writer, var key: {FailedScan: {iface_id: iface_id}})
134            }
135            Defect::Iface(IfaceFailure::EmptyScanResults { iface_id }) => {
136                inspect_insert!(writer, var key: {EmptyScanResults: {iface_id: iface_id}})
137            }
138            Defect::Iface(IfaceFailure::ApStartFailure { iface_id }) => {
139                inspect_insert!(writer, var key: {ApStartFailure: {iface_id: iface_id}})
140            }
141            Defect::Iface(IfaceFailure::ConnectionFailure { iface_id }) => {
142                inspect_insert!(writer, var key: {ConnectionFailure: {iface_id: iface_id}})
143            }
144            Defect::Iface(IfaceFailure::Timeout { iface_id, .. }) => {
145                inspect_insert!(writer, var key: {Timeout: {iface_id: iface_id}})
146            }
147        }
148    }
149}
150
151#[derive(Debug, PartialEq)]
152struct Event<T: PartialEq> {
153    value: T,
154    time: fasync::MonotonicInstant,
155}
156
157impl<T: PartialEq> Event<T> {
158    fn new(value: T, time: fasync::MonotonicInstant) -> Self {
159        Event { value, time }
160    }
161}
162
163#[derive(Debug)]
164pub struct EventHistory<T: PartialEq> {
165    events: Vec<Event<T>>,
166    retention_time: zx::MonotonicDuration,
167}
168
169impl<T: PartialEq> EventHistory<T> {
170    fn new(retention_seconds: u32) -> Self {
171        EventHistory {
172            events: Vec::new(),
173            retention_time: zx::MonotonicDuration::from_seconds(retention_seconds as i64),
174        }
175    }
176
177    fn add_event(&mut self, value: T) {
178        let curr_time = fasync::MonotonicInstant::now();
179        self.events.push(Event::new(value, curr_time));
180        self.retain_unexpired_events(curr_time);
181    }
182
183    fn event_count(&mut self, value: T) -> usize {
184        let curr_time = fasync::MonotonicInstant::now();
185        self.retain_unexpired_events(curr_time);
186        self.events.iter().filter(|event| event.value == value).count()
187    }
188
189    fn time_since_last_event(&mut self, value: T) -> Option<zx::MonotonicDuration> {
190        let curr_time = fasync::MonotonicInstant::now();
191        self.retain_unexpired_events(curr_time);
192
193        for event in self.events.iter().rev() {
194            if event.value == value {
195                return Some(curr_time - event.time);
196            }
197        }
198        None
199    }
200
201    fn retain_unexpired_events(&mut self, curr_time: fasync::MonotonicInstant) {
202        let oldest_allowed_time = curr_time - self.retention_time;
203        self.events.retain(|event| event.time > oldest_allowed_time)
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    use fuchsia_async::TestExecutor;
211    use rand::RngExt as _;
212    use test_util::{assert_gt, assert_lt};
213
214    #[fuchsia::test]
215    fn test_event_retention() {
216        // Allow for events to be retained for at most 1s.
217        let mut event_history = EventHistory::<()>::new(1);
218
219        // Add events at 0, 1, 2, 2 and a little bit seconds, and 3s.
220        event_history.events = vec![
221            Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(0) },
222            Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(1_000_000_000) },
223            Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(2_000_000_000) },
224            Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(2_000_000_001) },
225            Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(3_000_000_000) },
226        ];
227
228        // Retain those events within the retention window based on a current time of 3s.
229        event_history.retain_unexpired_events(fasync::MonotonicInstant::from_nanos(3_000_000_000));
230
231        // It is expected that the events at 2 and a little bit seconds and 3s are retained while
232        // the others are discarded.
233        assert_eq!(
234            event_history.events,
235            vec![
236                Event::<()> {
237                    value: (),
238                    time: fasync::MonotonicInstant::from_nanos(2_000_000_001)
239                },
240                Event::<()> {
241                    value: (),
242                    time: fasync::MonotonicInstant::from_nanos(3_000_000_000)
243                },
244            ]
245        );
246    }
247
248    #[derive(Debug, PartialEq)]
249    enum TestEnum {
250        Foo,
251        Bar,
252    }
253
254    #[fuchsia::test]
255    fn test_time_since_last_event() {
256        // An executor is required to enable querying time.
257        let _exec = TestExecutor::new();
258
259        // Allow events to be stored basically forever.  The goal here is to ensure that the
260        // retention policy does not discard any of our events.
261        let mut event_history = EventHistory::<TestEnum>::new(u32::MAX);
262
263        // Add some events with known timestamps.
264        let foo_time: i64 = 1_123_123_123;
265        let bar_time: i64 = 2_222_222_222;
266        event_history.events = vec![
267            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(foo_time) },
268            Event { value: TestEnum::Bar, time: fasync::MonotonicInstant::from_nanos(bar_time) },
269        ];
270
271        // Get the time before and after the function calls were made.  This allows for some slack
272        // in evaluating whether the time calculations are in the realm of accurate.
273        let start_time = fasync::MonotonicInstant::now().into_nanos();
274        let time_since_foo =
275            event_history.time_since_last_event(TestEnum::Foo).expect("Foo was not retained");
276        let time_since_bar =
277            event_history.time_since_last_event(TestEnum::Bar).expect("Bar was not retained");
278        let end_time = fasync::MonotonicInstant::now().into_nanos();
279
280        // Make sure the returned durations are within bounds.
281        assert_lt!(time_since_foo.into_nanos(), end_time - foo_time);
282        assert_gt!(time_since_foo.into_nanos(), start_time - foo_time);
283
284        assert_lt!(time_since_bar.into_nanos(), end_time - bar_time);
285        assert_gt!(time_since_bar.into_nanos(), start_time - bar_time);
286    }
287
288    #[fuchsia::test]
289    fn test_time_since_last_event_retention() {
290        // An executor is required to enable querying time.
291        let _exec = TestExecutor::new();
292
293        // Set the retention time to slightly less than the current time.  This number will be
294        // positive.  Since it will occupy the positive range of i64, it is safe to cast it as u32.
295        let curr_time_seconds = fasync::MonotonicInstant::now().into_nanos() / 1_000_000_000;
296        let mut event_history = EventHistory::<()>::new((curr_time_seconds - 1) as u32);
297
298        // Put in an event at time zero so that it will not be retained when querying recent
299        // events.
300        event_history
301            .events
302            .push(Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(0) });
303
304        assert_eq!(event_history.time_since_last_event(()), None);
305    }
306    #[fuchsia::test]
307    fn test_add_event() {
308        // An executor is required to enable querying time.
309        let _exec = TestExecutor::new();
310        let mut event_history = EventHistory::<()>::new(u32::MAX);
311
312        // Add a few events
313        let num_events = 3;
314        let start_time = fasync::MonotonicInstant::now().into_nanos();
315        for _ in 0..num_events {
316            event_history.add_event(());
317        }
318        let end_time = fasync::MonotonicInstant::now().into_nanos();
319
320        // All three of the recent events should have been retained.
321        assert_eq!(event_history.events.len(), num_events);
322
323        // Verify that all of the even timestamps are within range.
324        for event in event_history.events {
325            let event_time = event.time.into_nanos();
326            assert_lt!(event_time, end_time);
327            assert_gt!(event_time, start_time);
328        }
329    }
330
331    #[fuchsia::test]
332    fn test_add_event_retention() {
333        // An executor is required to enable querying time.
334        let _exec = TestExecutor::new();
335
336        // Set the retention time to slightly less than the current time.  This number will be
337        // positive.  Since it will occupy the positive range of i64, it is safe to cast it as u32.
338        let curr_time_seconds = fasync::MonotonicInstant::now().into_nanos() / 1_000_000_000;
339        let mut event_history = EventHistory::<()>::new((curr_time_seconds - 1) as u32);
340
341        // Put in an event at time zero so that it will not be retained when querying recent
342        // events.
343        event_history
344            .events
345            .push(Event::<()> { value: (), time: fasync::MonotonicInstant::from_nanos(0) });
346
347        // Add an event and observe that the event from time 0 has been removed.
348        let start_time = fasync::MonotonicInstant::now().into_nanos();
349        event_history.add_event(());
350        assert_eq!(event_history.events.len(), 1);
351
352        // Add a couple more events.
353        event_history.add_event(());
354        event_history.add_event(());
355        let end_time = fasync::MonotonicInstant::now().into_nanos();
356
357        // All three of the recent events should have been retained.
358        assert_eq!(event_history.events.len(), 3);
359
360        // Verify that all of the even timestamps are within range.
361        for event in event_history.events {
362            let event_time = event.time.into_nanos();
363            assert_lt!(event_time, end_time);
364            assert_gt!(event_time, start_time);
365        }
366    }
367
368    #[fuchsia::test]
369    fn test_event_count() {
370        // An executor is required to enable querying time.
371        let _exec = TestExecutor::new();
372        let mut event_history = EventHistory::<TestEnum>::new(u32::MAX);
373
374        event_history.events = vec![
375            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(0) },
376            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(1) },
377            Event { value: TestEnum::Bar, time: fasync::MonotonicInstant::from_nanos(2) },
378            Event { value: TestEnum::Bar, time: fasync::MonotonicInstant::from_nanos(3) },
379            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(4) },
380        ];
381
382        assert_eq!(event_history.event_count(TestEnum::Foo), 3);
383        assert_eq!(event_history.event_count(TestEnum::Bar), 2);
384    }
385
386    #[fuchsia::test]
387    fn test_event_count_retention() {
388        // An executor is required to enable querying time.
389        let _exec = TestExecutor::new();
390
391        // Set the retention time to slightly less than the current time.  This number will be
392        // positive.  Since it will occupy the positive range of i64, it is safe to cast it as u32.
393        let curr_time_seconds = fasync::MonotonicInstant::now().into_nanos() / 1_000_000_000;
394        let mut event_history = EventHistory::<TestEnum>::new((curr_time_seconds - 1) as u32);
395
396        event_history.events = vec![
397            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(0) },
398            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::from_nanos(0) },
399            Event { value: TestEnum::Bar, time: fasync::MonotonicInstant::now() },
400            Event { value: TestEnum::Bar, time: fasync::MonotonicInstant::now() },
401            Event { value: TestEnum::Foo, time: fasync::MonotonicInstant::now() },
402        ];
403
404        assert_eq!(event_history.event_count(TestEnum::Foo), 1);
405        assert_eq!(event_history.event_count(TestEnum::Bar), 2);
406    }
407
408    #[fuchsia::test]
409    fn test_failure_equality() {
410        let mut rng = rand::rng();
411        assert_eq!(
412            IfaceFailure::CanceledScan { iface_id: rng.random() },
413            IfaceFailure::CanceledScan { iface_id: rng.random() }
414        );
415        assert_eq!(
416            IfaceFailure::FailedScan { iface_id: rng.random() },
417            IfaceFailure::FailedScan { iface_id: rng.random() }
418        );
419        assert_eq!(
420            IfaceFailure::EmptyScanResults { iface_id: rng.random() },
421            IfaceFailure::EmptyScanResults { iface_id: rng.random() }
422        );
423        assert_eq!(
424            IfaceFailure::ApStartFailure { iface_id: rng.random() },
425            IfaceFailure::ApStartFailure { iface_id: rng.random() }
426        );
427        assert_eq!(
428            IfaceFailure::ConnectionFailure { iface_id: rng.random() },
429            IfaceFailure::ConnectionFailure { iface_id: rng.random() }
430        );
431        assert_eq!(
432            IfaceFailure::Timeout { iface_id: rng.random(), source: TimeoutSource::Scan },
433            IfaceFailure::Timeout { iface_id: rng.random(), source: TimeoutSource::ApStart }
434        );
435    }
436}