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