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