wlancfg_lib/
regulatory_manager.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::mode_management::iface_manager_api::IfaceManagerApi;
6use anyhow::{Context, Error};
7use fidl_fuchsia_location_namedplace::RegulatoryRegionWatcherProxy;
8use futures::channel::oneshot;
9use futures::lock::Mutex;
10use log::{info, warn};
11use std::sync::Arc;
12
13pub struct RegulatoryManager<I: IfaceManagerApi + ?Sized> {
14    regulatory_service: RegulatoryRegionWatcherProxy,
15    iface_manager: Arc<Mutex<I>>,
16}
17
18mod country_code {
19    use anyhow::{Error, format_err};
20    use std::str::FromStr;
21    const CODE_LENGTH: usize = 2;
22
23    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
24    pub struct CountryCode([u8; CODE_LENGTH]);
25
26    impl FromStr for CountryCode {
27        type Err = Error;
28        fn from_str(region: &str) -> Result<Self, Self::Err> {
29            region
30                .as_bytes()
31                .try_into()
32                .map_err(|_| {
33                    format_err!("Invalid length: {region}, {} != {CODE_LENGTH}", region.len())
34                })
35                .map(CountryCode)
36        }
37    }
38
39    impl From<CountryCode> for [u8; CODE_LENGTH] {
40        fn from(country_code: CountryCode) -> Self {
41            country_code.0
42        }
43    }
44}
45
46pub use country_code::CountryCode;
47
48impl<I: IfaceManagerApi + ?Sized> RegulatoryManager<I> {
49    pub fn new(
50        regulatory_service: RegulatoryRegionWatcherProxy,
51        iface_manager: Arc<Mutex<I>>,
52    ) -> Self {
53        RegulatoryManager { regulatory_service, iface_manager }
54    }
55
56    pub async fn run(&self, policy_notifier: oneshot::Sender<()>) -> Result<(), Error> {
57        let mut policy_notifier = Some(policy_notifier);
58        loop {
59            let region_update = self
60                .regulatory_service
61                .get_region_update()
62                .await
63                .context("failed to get_update()")?;
64            let region_string = match region_update {
65                Some(region_string) => region_string,
66                None => {
67                    info!("No cached regulatory region is available.");
68                    if let Some(notifier) = policy_notifier.take()
69                        && notifier.send(()).is_err()
70                    {
71                        info!("Could not notify policy layer of initial region setting");
72                    };
73
74                    continue;
75                }
76            };
77
78            info!("Received regulatory region code {}", region_string);
79
80            let country_code = match region_string.parse::<CountryCode>() {
81                Err(e) => {
82                    warn!("{:?}", e.context("Failed to parse region string."));
83                    continue;
84                }
85                Ok(country_code) => country_code,
86            };
87
88            // Apply the new country code.
89            let mut iface_manager = self.iface_manager.lock().await;
90            if let Err(e) = iface_manager.set_country(Some(country_code)).await {
91                warn!("Failed to set region code: {:?}", e);
92                continue;
93            }
94
95            if let Some(notifier) = policy_notifier.take()
96                && notifier.send(()).is_err()
97            {
98                info!("Could not notify policy layer of initial region setting");
99            };
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::{Arc, IfaceManagerApi, Mutex, RegulatoryManager};
107    use crate::access_point::{state_machine as ap_fsm, types as ap_types};
108    use crate::client::types as client_types;
109    use crate::mode_management::iface_manager_api::{ConnectAttemptRequest, SmeForScan};
110    use anyhow::{Error, format_err};
111    use assert_matches::assert_matches;
112    use async_trait::async_trait;
113    use fidl::endpoints::create_proxy;
114    use fidl_fuchsia_location_namedplace::{
115        RegulatoryRegionWatcherMarker, RegulatoryRegionWatcherRequest,
116        RegulatoryRegionWatcherRequestStream,
117    };
118    use fuchsia_async as fasync;
119    use futures::channel::{mpsc, oneshot};
120    use futures::stream::{self, Stream, StreamExt};
121    use futures::task::Poll;
122    use std::pin::pin;
123    use std::unimplemented;
124
125    /// Holds all of the boilerplate required for testing RegulatoryManager.
126    struct TestContext<S: Stream<Item = Result<(), Error>> + Unpin> {
127        iface_manager: Arc<Mutex<StubIfaceManager<S>>>,
128        regulatory_manager: RegulatoryManager<StubIfaceManager<S>>,
129        regulatory_region_requests: RegulatoryRegionWatcherRequestStream,
130        regulatory_sender: oneshot::Sender<()>,
131        regulatory_receiver: oneshot::Receiver<()>,
132        // Fields are dropped in declaration order. Always drop executor last because we hold other
133        // zircon objects tied to the executor in this struct, and those can't outlive the executor.
134        //
135        // See
136        // - https://fuchsia-docs.firebaseapp.com/rust/fuchsia_async/struct.TestExecutor.html
137        // - https://doc.rust-lang.org/reference/destructors.html.
138        executor: fasync::TestExecutor,
139    }
140
141    impl<S> TestContext<S>
142    where
143        S: Stream<Item = Result<(), Error>> + Unpin,
144    {
145        fn new(
146            iface_manager: StubIfaceManager<S>,
147        ) -> TestContext<impl Stream<Item = Result<(), Error>> + Unpin> {
148            let executor = fasync::TestExecutor::new();
149            let (regulatory_region_proxy, regulatory_region_server_channel) =
150                create_proxy::<RegulatoryRegionWatcherMarker>();
151            let iface_manager = Arc::new(Mutex::new(iface_manager));
152            let regulatory_manager =
153                RegulatoryManager::new(regulatory_region_proxy, iface_manager.clone());
154            let regulatory_region_requests = regulatory_region_server_channel.into_stream();
155
156            let (regulatory_sender, regulatory_receiver) = oneshot::channel();
157            Self {
158                executor,
159                iface_manager,
160                regulatory_manager,
161                regulatory_region_requests,
162                regulatory_sender,
163                regulatory_receiver,
164            }
165        }
166    }
167
168    #[fuchsia::test]
169    fn ignore_update_with_short_region_code() {
170        let mut context = TestContext::new(make_default_stub_iface_manager());
171        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
172        let mut regulatory_fut = pin!(regulatory_fut);
173        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
174
175        let region_request_fut = &mut context.regulatory_region_requests.next();
176        let responder = assert_matches!(
177            context.executor.run_until_stalled(region_request_fut),
178            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
179        );
180        responder.send(Some("U")).expect("failed to send response");
181        assert_matches!(context.executor.run_until_stalled(&mut regulatory_fut), Poll::Pending);
182
183        assert_matches!(
184            &context.executor.run_until_stalled(&mut context.regulatory_receiver),
185            Poll::Pending
186        );
187
188        // Verify that there is a new region update request.
189        let region_request_fut = &mut context.regulatory_region_requests.next();
190        assert_matches!(
191            context.executor.run_until_stalled(region_request_fut),
192            Poll::Ready(Some(_))
193        );
194    }
195
196    #[fuchsia::test]
197    fn update_with_long_region_code_fails() {
198        let mut context = TestContext::new(make_default_stub_iface_manager());
199        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
200        let mut regulatory_fut = pin!(regulatory_fut);
201        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
202
203        let region_request_fut = &mut context.regulatory_region_requests.next();
204        let responder = assert_matches!(
205            context.executor.run_until_stalled(region_request_fut),
206            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
207        );
208        assert_matches!(responder.send(Some("USA")), Err(fidl::Error::StringTooLong { .. }));
209        assert_matches!(context.executor.run_until_stalled(&mut regulatory_fut), Poll::Pending);
210    }
211
212    #[fuchsia::test]
213    fn propagates_update_on_region_code_with_valid_length() {
214        let mut context = TestContext::new(make_default_stub_iface_manager());
215        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
216        let mut regulatory_fut = pin!(regulatory_fut);
217        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
218
219        let region_request_fut = &mut context.regulatory_region_requests.next();
220        let region_responder = assert_matches!(
221            context.executor.run_until_stalled(region_request_fut),
222            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
223        );
224        region_responder.send(Some("US")).expect("failed to send region response");
225        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
226
227        let iface_manager_fut = context.iface_manager.lock();
228        let mut iface_manager_fut = pin!(iface_manager_fut);
229        match context.executor.run_until_stalled(&mut iface_manager_fut) {
230            Poll::Ready(iface_manager) => {
231                assert_eq!(iface_manager.country_code, Some("US".parse().unwrap()))
232            }
233            Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
234        };
235    }
236
237    #[fuchsia::test]
238    fn does_not_propagate_invalid_length_region_code() {
239        let mut context = TestContext::new(make_default_stub_iface_manager());
240        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
241        let mut regulatory_fut = pin!(regulatory_fut);
242        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
243
244        let region_request_fut = &mut context.regulatory_region_requests.next();
245        let region_responder = assert_matches!(
246            context.executor.run_until_stalled(region_request_fut),
247            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
248        );
249        region_responder.send(Some("U")).expect("failed to send region response");
250
251        // Drive the RegulatoryManager until stalled, then verify that RegulatoryManager did not
252        // send a request to the IfaceManager.
253        let _ = context.executor.run_until_stalled(&mut regulatory_fut);
254
255        let iface_manager_fut = context.iface_manager.lock();
256        let mut iface_manager_fut = pin!(iface_manager_fut);
257        match context.executor.run_until_stalled(&mut iface_manager_fut) {
258            Poll::Ready(iface_manager) => assert_eq!(iface_manager.country_code, None),
259            Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
260        }
261
262        assert_matches!(
263            &context.executor.run_until_stalled(&mut context.regulatory_receiver),
264            Poll::Pending
265        );
266    }
267
268    #[fuchsia::test]
269    fn does_not_propagate_null_update() {
270        let mut context = TestContext::new(make_default_stub_iface_manager());
271        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
272        let mut regulatory_fut = pin!(regulatory_fut);
273        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
274
275        // Set the regulatory region to be non-None initially.
276        {
277            let iface_manager_fut = context.iface_manager.lock();
278            let mut iface_manager_fut = pin!(iface_manager_fut);
279            match context.executor.run_until_stalled(&mut iface_manager_fut) {
280                Poll::Ready(mut iface_manager) => {
281                    iface_manager.country_code = Some("US".parse().unwrap())
282                }
283                Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
284            }
285        }
286
287        let region_request_fut = &mut context.regulatory_region_requests.next();
288        let region_responder = assert_matches!(
289            context.executor.run_until_stalled(region_request_fut),
290            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
291        );
292        region_responder.send(None).expect("failed to send region response");
293        // Run RegulatoryManager until stalled. Getting a null update should not cause an error.
294        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
295
296        // Verify that no region change is applied to the IfaceManager.
297        {
298            let iface_manager_fut = context.iface_manager.lock();
299            let mut iface_manager_fut = pin!(iface_manager_fut);
300            match context.executor.run_until_stalled(&mut iface_manager_fut) {
301                Poll::Ready(iface_manager) => assert!(iface_manager.country_code.is_some()),
302                Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
303            }
304        }
305
306        // Verify that the policy API is instructed to begin serving.
307        assert_matches!(
308            &context.executor.run_until_stalled(&mut context.regulatory_receiver),
309            Poll::Ready(Ok(()))
310        );
311    }
312
313    #[fuchsia::test]
314    fn absorbs_iface_manager_failure() {
315        let (mut set_country_responder, set_country_response_stream) = mpsc::channel(0);
316        let mut context =
317            TestContext::new(StubIfaceManager { country_code: None, set_country_response_stream });
318        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
319        let mut regulatory_fut = pin!(regulatory_fut);
320        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
321
322        // Drive the RegulatoryManager to request an update from RegulatoryRegionWatcher,
323        // and deliver a RegulatoryRegion update.
324        let region_request_fut = &mut context.regulatory_region_requests.next();
325        let region_responder = assert_matches!(
326            context.executor.run_until_stalled(region_request_fut),
327            Poll::Ready(Some(Ok(RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
328        );
329        region_responder.send(Some("US")).expect("failed to send region response");
330        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
331
332        // Setup an error response for when the RegulatoryManager tries to set the country code.
333        set_country_responder
334            .try_send(Err(format_err!("sending a test error")))
335            .expect("internal error: failed to send fake response to StubIfaceManager");
336
337        assert_matches!(&context.executor.run_until_stalled(&mut regulatory_fut), Poll::Pending);
338
339        // Verify that there is a new region update request.
340        let region_request_fut = &mut context.regulatory_region_requests.next();
341        assert_matches!(
342            context.executor.run_until_stalled(region_request_fut),
343            Poll::Ready(Some(_))
344        );
345    }
346
347    #[fuchsia::test]
348    fn propagates_multiple_valid_region_code_updates_to_device_service() {
349        let mut context = TestContext::new(make_default_stub_iface_manager());
350        let regulatory_fut = context.regulatory_manager.run(context.regulatory_sender);
351        let mut regulatory_fut = pin!(regulatory_fut);
352
353        // Receive first `RegulatoryRegionWatcher` update, and propagate it to `IfaceManager`.
354        {
355            assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
356
357            let region_request_fut = &mut context.regulatory_region_requests.next();
358            let region_responder = assert_matches!(
359                context.executor.run_until_stalled(region_request_fut),
360                Poll::Ready(Some(Ok(
361                    RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
362            );
363            region_responder.send(Some("US")).expect("failed to send region response");
364            assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
365
366            let iface_manager_fut = context.iface_manager.lock();
367            let mut iface_manager_fut = pin!(iface_manager_fut);
368            match context.executor.run_until_stalled(&mut iface_manager_fut) {
369                Poll::Ready(iface_manager) => {
370                    assert_eq!(iface_manager.country_code, Some("US".parse().unwrap()))
371                }
372                Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
373            }
374        }
375
376        assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
377        assert_matches!(
378            &context.executor.run_until_stalled(&mut context.regulatory_receiver),
379            Poll::Ready(Ok(()))
380        );
381
382        // Receive second `RegulatoryRegionWatcher` update, and propagate it to `IfaceManager`.
383        {
384            assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
385
386            let region_request_fut = &mut context.regulatory_region_requests.next();
387            let region_responder = assert_matches!(
388                context.executor.run_until_stalled(region_request_fut),
389                Poll::Ready(Some(Ok(
390                    RegulatoryRegionWatcherRequest::GetRegionUpdate{responder}))) => responder
391            );
392            region_responder.send(Some("CA")).expect("failed to send region response");
393            assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
394
395            let iface_manager_fut = context.iface_manager.lock();
396            let mut iface_manager_fut = pin!(iface_manager_fut);
397            match context.executor.run_until_stalled(&mut iface_manager_fut) {
398                Poll::Ready(iface_manager) => {
399                    assert_eq!(iface_manager.country_code, Some("CA".parse().unwrap()))
400                }
401                Poll::Pending => panic!("Expected to be able to lock the IfaceManager."),
402            };
403        }
404    }
405
406    struct StubIfaceManager<S: Stream<Item = Result<(), Error>> + Unpin> {
407        country_code: Option<client_types::CountryCode>,
408        set_country_response_stream: S,
409    }
410
411    /// A default StubIfaceManager
412    /// * immediately returns Ok() in response to stop_client_connections(), and
413    /// * immediately returns Ok() in response to start_client_connections()
414    fn make_default_stub_iface_manager()
415    -> StubIfaceManager<impl Stream<Item = Result<(), Error>> + Unpin> {
416        StubIfaceManager {
417            country_code: None,
418            set_country_response_stream: stream::unfold((), |_| async { Some((Ok(()), ())) })
419                .boxed(),
420        }
421    }
422
423    #[async_trait(?Send)]
424    impl<S: Stream<Item = Result<(), Error>> + Unpin> IfaceManagerApi for StubIfaceManager<S> {
425        async fn disconnect(
426            &mut self,
427            _network_id: client_types::NetworkIdentifier,
428            _reason: client_types::DisconnectReason,
429        ) -> Result<(), Error> {
430            unimplemented!();
431        }
432
433        async fn connect(&mut self, _connect_req: ConnectAttemptRequest) -> Result<(), Error> {
434            unimplemented!();
435        }
436
437        async fn record_idle_client(&mut self, _iface_id: u16) -> Result<(), Error> {
438            unimplemented!();
439        }
440
441        async fn has_idle_client(&mut self) -> Result<bool, Error> {
442            unimplemented!();
443        }
444
445        async fn handle_added_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
446            unimplemented!();
447        }
448
449        async fn handle_removed_iface(&mut self, _iface_id: u16) -> Result<(), Error> {
450            unimplemented!();
451        }
452
453        async fn get_sme_proxy_for_scan(&mut self) -> Result<SmeForScan, Error> {
454            unimplemented!()
455        }
456
457        async fn stop_client_connections(
458            &mut self,
459            _reason: client_types::DisconnectReason,
460        ) -> Result<(), Error> {
461            unimplemented!()
462        }
463
464        async fn start_client_connections(&mut self) -> Result<(), Error> {
465            unimplemented!()
466        }
467
468        async fn start_ap(
469            &mut self,
470            _config: ap_fsm::ApConfig,
471        ) -> Result<oneshot::Receiver<()>, Error> {
472            unimplemented!();
473        }
474
475        async fn stop_ap(
476            &mut self,
477            _ssid: ap_types::Ssid,
478            _password: Vec<u8>,
479        ) -> Result<(), Error> {
480            unimplemented!();
481        }
482
483        async fn stop_all_aps(&mut self) -> Result<(), Error> {
484            unimplemented!()
485        }
486
487        async fn set_country(
488            &mut self,
489            country_code: Option<client_types::CountryCode>,
490        ) -> Result<(), Error> {
491            self.country_code = country_code;
492            self.set_country_response_stream
493                .next()
494                .await
495                .expect("internal error: failed to receive fake response from test case")
496        }
497    }
498}