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