1use 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 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 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 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 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 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 {
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 assert!(context.executor.run_until_stalled(&mut regulatory_fut).is_pending());
295
296 {
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 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 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 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 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 {
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 {
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 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}