1use crate::client::types as client_types;
6use crate::mode_management::recovery::{
7 self, IfaceRecoveryOperation, PhyRecoveryOperation, RecoveryAction,
8};
9use crate::mode_management::{Defect, EventHistory, IfaceFailure, PhyFailure};
10use crate::telemetry::{TelemetryEvent, TelemetrySender};
11use anyhow::{Error, format_err};
12use async_trait::async_trait;
13use fidl::endpoints::create_proxy;
14use fidl_fuchsia_power_broker as fbroker;
15use fidl_fuchsia_power_system as fsystem;
16use fidl_fuchsia_wlan_common as fidl_common;
17use fidl_fuchsia_wlan_device_service as fidl_service;
18use fidl_fuchsia_wlan_sme as fidl_sme;
19use fuchsia_inspect::{self as inspect, NumericProperty};
20use futures::StreamExt;
21use futures::lock::Mutex;
22use ieee80211::{MacAddr, MacAddrBytes, NULL_ADDR};
23use log::{error, info, warn};
24use std::collections::{HashMap, HashSet};
25use std::iter::Iterator;
26use std::sync::Arc;
27use thiserror::Error;
28use wlan_power_manager::{POWER_LEVEL_ACTIVE, POWER_LEVEL_SUSPEND, PowerManager};
29
30const DEFECT_RETENTION_SECONDS: u32 = 86400;
33
34#[derive(Clone, Debug, Error, PartialEq, Eq)]
36pub enum PhyManagerError {
37 #[error("the requested operation is not supported")]
38 Unsupported,
39 #[error("unable to query phy information")]
40 PhyQueryFailure,
41 #[error("failed to set country for new PHY")]
42 PhySetCountryFailure,
43 #[error("unable to reset PHY")]
44 PhyResetFailure,
45 #[error("unable to query iface information")]
46 IfaceQueryFailure,
47 #[error("unable to create iface")]
48 IfaceCreateFailure,
49 #[error("unable to destroy iface")]
50 IfaceDestroyFailure,
51 #[error("internal state has become inconsistent")]
52 InternalError,
53}
54
55#[derive(PartialEq)]
60pub enum CreateClientIfacesReason {
61 StartClientConnections,
62 RecoverClientIfaces,
63}
64
65pub(crate) struct PhyContainer {
67 supported_mac_roles: HashSet<fidl_common::WlanMacRole>,
68 client_ifaces: HashSet<u16>,
69 ap_ifaces: HashSet<u16>,
70 destroyed_ifaces: HashSet<u16>,
73 defects: EventHistory<Defect>,
74 recoveries: EventHistory<recovery::RecoveryAction>,
75 power_dependency_token: Option<fbroker::DependencyToken>,
76 power_dependency_lease: Option<fbroker::LeaseToken>,
77}
78
79#[async_trait(?Send)]
80pub trait PhyManagerApi {
81 async fn add_phy(&mut self, phy_id: u16) -> Result<(), PhyManagerError>;
84
85 fn remove_phy(&mut self, phy_id: u16);
87
88 async fn on_iface_added(&mut self, iface_id: u16) -> Result<(), PhyManagerError>;
96
97 fn on_iface_removed(&mut self, iface_id: u16);
99
100 async fn create_all_client_ifaces(
105 &mut self,
106 reason: CreateClientIfacesReason,
107 ) -> HashMap<u16, Result<Vec<u16>, PhyManagerError>>;
108
109 fn client_connections_enabled(&self) -> bool;
113
114 async fn destroy_all_client_ifaces(&mut self) -> Result<(), PhyManagerError>;
117
118 fn get_client(&mut self) -> Option<u16>;
120
121 async fn create_or_get_ap_iface(&mut self) -> Result<Option<u16>, PhyManagerError>;
126
127 async fn destroy_ap_iface(&mut self, iface_id: u16) -> Result<(), PhyManagerError>;
129
130 async fn destroy_all_ap_ifaces(&mut self) -> Result<(), PhyManagerError>;
132
133 fn suggest_ap_mac(&mut self, mac: MacAddr);
135
136 fn get_phy_ids(&self) -> Vec<u16>;
138
139 fn log_phy_add_failure(&mut self);
141
142 async fn set_country_code(
145 &mut self,
146 country_code: Option<client_types::CountryCode>,
147 ) -> Result<(), PhyManagerError>;
148
149 fn record_defect(&mut self, defect: Defect);
151
152 async fn perform_recovery(&mut self, summary: recovery::RecoverySummary);
154
155 async fn on_before_suspend(&mut self);
157
158 async fn on_after_resume(&mut self);
160}
161
162pub struct PhyManager {
164 phys: HashMap<u16, PhyContainer>,
165 recovery_profile: recovery::RecoveryProfile,
166 recovery_enabled: bool,
167 device_monitor: fidl_service::DeviceMonitorProxy,
168 client_connections_enabled: bool,
169 suggested_ap_mac: Option<MacAddr>,
170 saved_country_code: Option<client_types::CountryCode>,
171 _node: inspect::Node,
172 telemetry_sender: TelemetrySender,
173 recovery_action_sender: recovery::RecoveryActionSender,
174 phy_add_fail_count: inspect::UintProperty,
175 power_manager: Arc<dyn PowerManager>,
176}
177
178impl PhyContainer {
179 pub fn new(supported_mac_roles: Vec<fidl_common::WlanMacRole>) -> Self {
182 PhyContainer {
183 supported_mac_roles: supported_mac_roles.into_iter().collect(),
184 client_ifaces: HashSet::new(),
185 ap_ifaces: HashSet::new(),
186 destroyed_ifaces: HashSet::new(),
187 defects: EventHistory::<Defect>::new(DEFECT_RETENTION_SECONDS),
188 recoveries: EventHistory::<RecoveryAction>::new(DEFECT_RETENTION_SECONDS),
189 power_dependency_token: None,
190 power_dependency_lease: None,
191 }
192 }
193}
194
195impl PhyManager {
199 pub fn new(
202 device_monitor: fidl_service::DeviceMonitorProxy,
203 recovery_profile: recovery::RecoveryProfile,
204 recovery_enabled: bool,
205 node: inspect::Node,
206 telemetry_sender: TelemetrySender,
207 recovery_action_sender: recovery::RecoveryActionSender,
208 power_manager: Arc<dyn PowerManager>,
209 ) -> Self {
210 let phy_add_fail_count = node.create_uint("phy_add_fail_count", 0);
211 PhyManager {
212 phys: HashMap::new(),
213 recovery_profile,
214 recovery_enabled,
215 device_monitor,
216 client_connections_enabled: false,
217 suggested_ap_mac: None,
218 saved_country_code: None,
219 _node: node,
220 telemetry_sender,
221 recovery_action_sender,
222 phy_add_fail_count,
223 power_manager,
224 }
225 }
226 async fn ensure_phy(&mut self, phy_id: u16) -> Result<&mut PhyContainer, PhyManagerError> {
228 if !self.phys.contains_key(&phy_id) {
229 self.add_phy(phy_id).await?;
230 }
231
232 Ok(self.phys.get_mut(&phy_id).ok_or_else(|| {
235 error!("Phy ID did not exist in self.phys");
236 PhyManagerError::InternalError
237 }))?
238 }
239
240 async fn query_iface(
242 &self,
243 iface_id: u16,
244 ) -> Result<Option<fidl_service::QueryIfaceResponse>, PhyManagerError> {
245 match self.device_monitor.query_iface(iface_id).await {
246 Ok(Ok(response)) => Ok(Some(response)),
247 Ok(Err(zx::sys::ZX_ERR_NOT_FOUND)) => Ok(None),
248 _ => Err(PhyManagerError::IfaceQueryFailure),
249 }
250 }
251
252 fn phys_for_role(&self, role: fidl_common::WlanMacRole) -> Vec<u16> {
254 self.phys
255 .iter()
256 .filter_map(|(k, v)| {
257 if v.supported_mac_roles.contains(&role) {
258 return Some(*k);
259 }
260 None
261 })
262 .collect()
263 }
264
265 fn log_recovery_action(&mut self, summary: recovery::RecoverySummary) {
267 let affected_phy_id = match summary.action {
268 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id }) => {
269 if let Some(container) = self.phys.get_mut(&phy_id) {
270 container.recoveries.add_event(summary.action);
271 Some(phy_id)
272 } else {
273 None
274 }
275 }
276 RecoveryAction::PhyRecovery(PhyRecoveryOperation::DestroyIface { iface_id })
277 | RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::Disconnect { iface_id })
278 | RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::StopAp { iface_id }) => {
279 let mut affected_phy_id = None;
280 for (phy_id, phy_info) in self.phys.iter_mut() {
281 if phy_info.ap_ifaces.contains(&iface_id)
282 || phy_info.client_ifaces.contains(&iface_id)
283 {
284 phy_info.recoveries.add_event(summary.action);
285 affected_phy_id = Some(*phy_id);
286 }
287 }
288
289 affected_phy_id
290 }
291 };
292
293 if let Some(phy_id) = affected_phy_id {
294 warn!("Recovery has been recommended for PHY {}: {:?}", phy_id, summary.action);
295 }
296
297 if let Some(recovery_summary) = summary.as_recovery_reason() {
298 self.telemetry_sender.send(TelemetryEvent::RecoveryEvent { reason: recovery_summary });
299 }
300 }
301
302 async fn create_iface(
305 &mut self,
306 phy_id: u16,
307 role: fidl_common::WlanMacRole,
308 sta_addr: MacAddr,
309 ) -> Result<u16, PhyManagerError> {
310 let request = fidl_service::DeviceMonitorCreateIfaceRequest {
311 phy_id: Some(phy_id),
312 role: Some(role),
313 sta_address: Some(sta_addr.to_array()),
314 ..Default::default()
315 };
316
317 let response = self.device_monitor.create_iface(&request).await;
318 let result = match response {
319 Err(e) => {
320 warn!("Failed to create iface: phy {}, FIDL error {:?}", phy_id, e);
321 Err(PhyManagerError::IfaceCreateFailure)
322 }
323 Ok(Err(e)) => {
324 warn!("Failed to create iface: phy {}, error {:?}", phy_id, e);
325 Err(PhyManagerError::IfaceCreateFailure)
326 }
327 Ok(Ok(fidl_service::DeviceMonitorCreateIfaceResponse { iface_id: None, .. })) => {
328 warn!("Failed to create iface. No iface ID received: phy {}", phy_id);
329 Err(PhyManagerError::IfaceCreateFailure)
330 }
331 Ok(Ok(fidl_service::DeviceMonitorCreateIfaceResponse {
332 iface_id: Some(iface_id),
333 ..
334 })) => Ok(iface_id),
335 };
336
337 self.telemetry_sender.send(TelemetryEvent::IfaceCreationResult {
338 role,
339 result: result.as_ref().map_err(|_| ()).copied(),
340 });
341 if result.is_err() {
342 self.record_defect(Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id }));
343 }
344 result
345 }
346
347 async fn set_phy_power_element_lease(
348 power_manager: &dyn PowerManager,
349 phy_id: u16,
350 phy_container: &mut PhyContainer,
351 level: u8,
352 ) {
353 let token = match &phy_container.power_dependency_token {
354 Some(t) => t,
355 None => {
356 return;
358 }
359 };
360 let token_dup = match token.duplicate_handle(zx::Rights::SAME_RIGHTS) {
361 Ok(t) => t,
362 Err(e) => {
363 warn!("Failed to duplicate power element dep token: {:?}", e);
364 return;
365 }
366 };
367 let lease_name = format!("wlancfg-phy-{}-level-{}-dependency", phy_id, level);
368 match power_manager.power_element_lease(&lease_name, token_dup, level).await {
369 Ok(lease) => {
370 phy_container.power_dependency_lease = Some(lease);
371 }
372 Err(e) => {
373 error!(
374 "Failed to acquire power dependency lease {:?} at level {} for phy {}: {:?}",
375 lease_name, level, phy_id, e
376 );
377 }
378 }
379 }
380}
381
382#[async_trait(?Send)]
383impl PhyManagerApi for PhyManager {
384 async fn add_phy(&mut self, phy_id: u16) -> Result<(), PhyManagerError> {
385 let mut phy_container = PhyContainer::new(vec![]);
386
387 phy_container.power_dependency_token = self
389 .device_monitor
390 .get_power_element_dependency_token(phy_id)
391 .await
392 .map_err(|e| {
393 warn!("Failed to get power dependency token for phy {}: {}", phy_id, e);
394 PhyManagerError::PhyQueryFailure
395 })?
396 .ok();
397 Self::set_phy_power_element_lease(
398 &*self.power_manager,
399 phy_id,
400 &mut phy_container,
401 POWER_LEVEL_ACTIVE,
402 )
403 .await;
404
405 let supported_mac_roles = self
407 .device_monitor
408 .get_supported_mac_roles(phy_id)
409 .await
410 .map_err(|e| {
411 warn!("Failed to communicate with monitor service: {:?}", e);
412 PhyManagerError::PhyQueryFailure
413 })?
414 .map_err(|e| {
415 warn!("Unable to get supported MAC roles: {:?}", e);
416 PhyManagerError::PhyQueryFailure
417 })?;
418 phy_container.supported_mac_roles = HashSet::from_iter(supported_mac_roles);
419
420 let set_country_result = match self.saved_country_code {
422 Some(country_code) => {
423 set_phy_country_code(&self.device_monitor, phy_id, country_code).await
424 }
425 None => Ok(()),
426 };
427
428 if set_country_result.is_err() {
432 clear_phy_country_code(&self.device_monitor, phy_id).await?;
433 }
434
435 if self.client_connections_enabled
436 && phy_container.supported_mac_roles.contains(&fidl_common::WlanMacRole::Client)
437 {
438 let iface_id =
439 self.create_iface(phy_id, fidl_common::WlanMacRole::Client, NULL_ADDR).await?;
440 let _ = phy_container.client_ifaces.insert(iface_id);
441 }
442
443 info!("adding PHY ID #{}", phy_id);
444 if self.phys.insert(phy_id, phy_container).is_some() {
445 warn!("Unexpectedly replaced existing phy information for id {}", phy_id);
446 };
447
448 Ok(())
449 }
450
451 fn remove_phy(&mut self, phy_id: u16) {
452 if self.phys.remove(&phy_id).is_none() {
453 warn!("Attempted to remove non-existed phy {}", phy_id);
454 };
455 }
456
457 async fn on_iface_added(&mut self, iface_id: u16) -> Result<(), PhyManagerError> {
458 if let Some(query_iface_response) = self.query_iface(iface_id).await? {
459 let iface_id = query_iface_response.id;
460 let phy = self.ensure_phy(query_iface_response.phy_id).await?;
461
462 match query_iface_response.role {
463 fidl_common::WlanMacRole::Client => {
464 if phy.client_ifaces.insert(iface_id) {
465 warn!(
467 "Detected an unexpected client iface id {} created outside of PhyManager",
468 iface_id
469 );
470 }
471 }
472 fidl_common::WlanMacRole::Ap => {
473 if phy.ap_ifaces.insert(iface_id) {
474 warn!("Detected an unexpected AP iface created outside of PhyManager");
476 }
477 }
478 fidl_common::WlanMacRole::Mesh => {
479 return Err(PhyManagerError::Unsupported);
480 }
481 fidl_common::WlanMacRoleUnknown!() => {
482 error!("Unknown WlanMacRole type {:?}", query_iface_response.role);
483 return Err(PhyManagerError::Unsupported);
484 }
485 }
486 }
487 Ok(())
488 }
489
490 fn on_iface_removed(&mut self, iface_id: u16) {
491 for phy_info in self.phys.values_mut() {
492 let _ = phy_info.client_ifaces.remove(&iface_id);
496 let _ = phy_info.ap_ifaces.remove(&iface_id);
497 }
498 }
499
500 async fn create_all_client_ifaces(
501 &mut self,
502 reason: CreateClientIfacesReason,
503 ) -> HashMap<u16, Result<Vec<u16>, PhyManagerError>> {
504 if reason == CreateClientIfacesReason::StartClientConnections {
505 self.client_connections_enabled = true;
506 }
507
508 let mut available_iface_ids = HashMap::new();
509 if self.client_connections_enabled {
510 let client_capable_phy_ids = self.phys_for_role(fidl_common::WlanMacRole::Client);
511
512 for phy_id in client_capable_phy_ids.iter().copied() {
513 let phy_container = match self.phys.get_mut(&phy_id) {
514 Some(phy_container) => phy_container,
515 None => {
516 let _ = available_iface_ids
517 .insert(phy_id, Err(PhyManagerError::PhyQueryFailure));
518 continue;
519 }
520 };
521
522 if phy_container.client_ifaces.is_empty() {
525 let iface_id = match self
526 .create_iface(phy_id, fidl_common::WlanMacRole::Client, NULL_ADDR)
527 .await
528 {
529 Ok(iface_id) => iface_id,
530 Err(e) => {
531 warn!("Failed to recover iface for PHY {}: {:?}", phy_id, e);
532 let _ = available_iface_ids.insert(phy_id, Err(e));
533 continue;
534 }
535 };
536
537 #[expect(clippy::unwrap_used)]
540 let phy_container = self.phys.get_mut(&phy_id).unwrap();
541 let _ = phy_container.client_ifaces.insert(iface_id);
542
543 let _ = available_iface_ids.insert(phy_id, Ok(vec![iface_id]));
546 } else {
547 let _ = available_iface_ids
548 .insert(phy_id, Ok(phy_container.client_ifaces.iter().copied().collect()));
549 }
550 }
551 }
552
553 available_iface_ids
554 }
555
556 fn client_connections_enabled(&self) -> bool {
557 self.client_connections_enabled
558 }
559
560 async fn destroy_all_client_ifaces(&mut self) -> Result<(), PhyManagerError> {
561 self.client_connections_enabled = false;
562
563 let client_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Client);
564 let mut result = Ok(());
565 let mut failing_phys = Vec::new();
566
567 for client_phy in client_capable_phys.iter() {
568 let phy_container =
569 self.phys.get_mut(client_phy).ok_or(PhyManagerError::PhyQueryFailure)?;
570
571 let mut lingering_ifaces = HashSet::new();
573
574 for iface_id in phy_container.client_ifaces.drain() {
575 match destroy_iface(
576 &self.device_monitor,
577 iface_id,
578 fidl_common::WlanMacRole::Client,
579 &self.telemetry_sender,
580 )
581 .await
582 {
583 Ok(()) => {
584 let _ = phy_container.destroyed_ifaces.insert(iface_id);
585 }
586 Err(e) => {
587 result = Err(e);
588 failing_phys.push(*client_phy);
589 if !lingering_ifaces.insert(iface_id) {
590 warn!("Unexpected duplicate lingering iface for id {}", iface_id);
591 };
592 }
593 }
594 }
595 phy_container.client_ifaces = lingering_ifaces;
596 }
597
598 if result.is_err() {
599 for phy_id in failing_phys {
600 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }))
601 }
602 }
603
604 result
605 }
606
607 fn get_client(&mut self) -> Option<u16> {
608 if !self.client_connections_enabled {
609 return None;
610 }
611
612 let client_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Client);
613
614 let first_client_capable_phy = client_capable_phys.first()?;
616 let phy = self.phys.get_mut(first_client_capable_phy)?;
617 phy.client_ifaces.iter().next().copied()
618 }
619
620 async fn create_or_get_ap_iface(&mut self) -> Result<Option<u16>, PhyManagerError> {
621 let ap_capable_phy_ids = self.phys_for_role(fidl_common::WlanMacRole::Ap);
622
623 for ap_phy_id in ap_capable_phy_ids.iter() {
625 let phy_container =
626 self.phys.get_mut(ap_phy_id).ok_or(PhyManagerError::PhyQueryFailure)?;
627 if phy_container.ap_ifaces.is_empty() {
628 let mac = match self.suggested_ap_mac {
629 Some(mac) => mac,
630 None => NULL_ADDR,
631 };
632 let iface_id =
633 self.create_iface(*ap_phy_id, fidl_common::WlanMacRole::Ap, mac).await?;
634
635 #[expect(clippy::unwrap_used)]
639 let phy_container = self.phys.get_mut(ap_phy_id).unwrap();
640 let _ = phy_container.ap_ifaces.insert(iface_id);
641 return Ok(Some(iface_id));
642 }
643 }
644
645 let Some(first_ap_capable_phy) = ap_capable_phy_ids.first() else {
649 return Ok(None);
650 };
651 let phy = match self.phys.get_mut(first_ap_capable_phy) {
652 Some(phy_container) => phy_container,
653 None => return Ok(None),
654 };
655 match phy.ap_ifaces.iter().next() {
656 Some(iface_id) => Ok(Some(*iface_id)),
657 None => Ok(None),
658 }
659 }
660
661 async fn destroy_ap_iface(&mut self, iface_id: u16) -> Result<(), PhyManagerError> {
662 let mut result = Ok(());
663 let mut failing_phy = None;
664
665 for (phy_id, phy_container) in self.phys.iter_mut() {
668 if phy_container.ap_ifaces.remove(&iface_id) {
669 match destroy_iface(
670 &self.device_monitor,
671 iface_id,
672 fidl_common::WlanMacRole::Ap,
673 &self.telemetry_sender,
674 )
675 .await
676 {
677 Ok(()) => {
678 let _ = phy_container.destroyed_ifaces.insert(iface_id);
679 }
680 Err(e) => {
681 let _ = phy_container.ap_ifaces.insert(iface_id);
682 result = Err(e);
683 failing_phy = Some(*phy_id);
684 }
685 }
686 break;
687 }
688 }
689
690 if let (Err(_), Some(phy_id)) = (result.as_ref(), failing_phy) {
691 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }))
692 }
693
694 result
695 }
696
697 async fn destroy_all_ap_ifaces(&mut self) -> Result<(), PhyManagerError> {
698 let ap_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Ap);
699 let mut result = Ok(());
700 let mut failing_phys = Vec::new();
701
702 for ap_phy in ap_capable_phys.iter() {
703 let phy_container =
704 self.phys.get_mut(ap_phy).ok_or(PhyManagerError::PhyQueryFailure)?;
705
706 let mut lingering_ifaces = HashSet::new();
708 for iface_id in phy_container.ap_ifaces.drain() {
709 match destroy_iface(
710 &self.device_monitor,
711 iface_id,
712 fidl_common::WlanMacRole::Ap,
713 &self.telemetry_sender,
714 )
715 .await
716 {
717 Ok(()) => {
718 let _ = phy_container.destroyed_ifaces.insert(iface_id);
719 }
720 Err(e) => {
721 result = Err(e);
722 failing_phys.push(ap_phy);
723 let _ = lingering_ifaces.insert(iface_id);
724 }
725 }
726 }
727 phy_container.ap_ifaces = lingering_ifaces;
728 }
729
730 if result.is_err() {
731 for phy_id in failing_phys {
732 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure {
733 phy_id: *phy_id,
734 }))
735 }
736 }
737
738 result
739 }
740
741 fn suggest_ap_mac(&mut self, mac: MacAddr) {
742 self.suggested_ap_mac = Some(mac);
743 }
744
745 fn get_phy_ids(&self) -> Vec<u16> {
746 self.phys.keys().cloned().collect()
747 }
748
749 fn log_phy_add_failure(&mut self) {
750 let _ = self.phy_add_fail_count.add(1);
751 }
752
753 async fn set_country_code(
754 &mut self,
755 country_code: Option<client_types::CountryCode>,
756 ) -> Result<(), PhyManagerError> {
757 self.saved_country_code = country_code;
758
759 match country_code {
760 Some(country_code) => {
761 for phy_id in self.phys.keys() {
762 set_phy_country_code(&self.device_monitor, *phy_id, country_code).await?;
763 }
764 }
765 None => {
766 for phy_id in self.phys.keys() {
767 clear_phy_country_code(&self.device_monitor, *phy_id).await?;
768 }
769 }
770 }
771
772 Ok(())
773 }
774
775 fn record_defect(&mut self, defect: Defect) {
776 let mut recovery_action = None;
777
778 match defect {
779 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id }) => {
780 if let Some(container) = self.phys.get_mut(&phy_id) {
781 container.defects.add_event(defect);
782 recovery_action = (self.recovery_profile)(
783 phy_id,
784 &mut container.defects,
785 &mut container.recoveries,
786 defect,
787 )
788 }
789 }
790 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }) => {
791 if let Some(container) = self.phys.get_mut(&phy_id) {
792 container.defects.add_event(defect);
793 recovery_action = (self.recovery_profile)(
794 phy_id,
795 &mut container.defects,
796 &mut container.recoveries,
797 defect,
798 )
799 }
800 }
801 Defect::Iface(IfaceFailure::CanceledScan { iface_id })
802 | Defect::Iface(IfaceFailure::FailedScan { iface_id })
803 | Defect::Iface(IfaceFailure::EmptyScanResults { iface_id })
804 | Defect::Iface(IfaceFailure::ConnectionFailure { iface_id }) => {
805 for (phy_id, phy_info) in self.phys.iter_mut() {
806 if phy_info.client_ifaces.contains(&iface_id)
807 || phy_info.destroyed_ifaces.contains(&iface_id)
808 {
809 phy_info.defects.add_event(defect);
810
811 recovery_action = (self.recovery_profile)(
812 *phy_id,
813 &mut phy_info.defects,
814 &mut phy_info.recoveries,
815 defect,
816 );
817
818 break;
819 }
820 }
821 }
822 Defect::Iface(IfaceFailure::ApStartFailure { iface_id }) => {
823 for (phy_id, phy_info) in self.phys.iter_mut() {
824 if phy_info.ap_ifaces.contains(&iface_id)
825 || phy_info.destroyed_ifaces.contains(&iface_id)
826 {
827 phy_info.defects.add_event(defect);
828
829 recovery_action = (self.recovery_profile)(
830 *phy_id,
831 &mut phy_info.defects,
832 &mut phy_info.recoveries,
833 defect,
834 );
835
836 break;
837 }
838 }
839 }
840 Defect::Iface(IfaceFailure::Timeout { iface_id, source }) => {
841 self.telemetry_sender.send(TelemetryEvent::SmeTimeout { source });
842
843 for (phy_id, phy_info) in self.phys.iter_mut() {
844 if phy_info.ap_ifaces.contains(&iface_id)
845 || phy_info.client_ifaces.contains(&iface_id)
846 || phy_info.destroyed_ifaces.contains(&iface_id)
847 {
848 phy_info.defects.add_event(defect);
849
850 recovery_action = (self.recovery_profile)(
851 *phy_id,
852 &mut phy_info.defects,
853 &mut phy_info.recoveries,
854 defect,
855 );
856
857 break;
858 }
859 }
860 }
861 }
862
863 if let Some(recovery_action) = recovery_action.take()
864 && let Err(e) = self
865 .recovery_action_sender
866 .try_send(recovery::RecoverySummary::new(defect, recovery_action))
867 {
868 warn!("Unable to suggest recovery action {:?}: {:?}", recovery_action, e);
869 }
870 }
871
872 async fn perform_recovery(&mut self, summary: recovery::RecoverySummary) {
873 self.log_recovery_action(summary);
874
875 if self.recovery_enabled {
876 match summary.action {
877 RecoveryAction::PhyRecovery(PhyRecoveryOperation::DestroyIface { iface_id }) => {
878 for phy_container in self.phys.values_mut() {
879 if phy_container.ap_ifaces.remove(&iface_id) {
880 #[allow(
881 clippy::redundant_pattern_matching,
882 reason = "mass allow for https://fxbug.dev/381896734"
883 )]
884 if let Err(_) = destroy_iface(
885 &self.device_monitor,
886 iface_id,
887 fidl_common::WlanMacRole::Ap,
888 &self.telemetry_sender,
889 )
890 .await
891 {
892 let _ = phy_container.ap_ifaces.insert(iface_id);
893 } else {
894 let _ = phy_container.destroyed_ifaces.insert(iface_id);
895 }
896
897 return;
898 }
899
900 if phy_container.client_ifaces.remove(&iface_id) {
901 if destroy_iface(
902 &self.device_monitor,
903 iface_id,
904 fidl_common::WlanMacRole::Client,
905 &self.telemetry_sender,
906 )
907 .await
908 .is_err()
909 {
910 let _ = phy_container.client_ifaces.insert(iface_id);
911 } else {
912 let _ = phy_container.destroyed_ifaces.insert(iface_id);
913 }
914
915 return;
916 }
917 }
918
919 warn!(
920 "Recovery suggested destroying iface {}, but no record was found.",
921 iface_id
922 );
923 }
924 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id }) => {
925 for recorded_phy_id in self.phys.keys() {
926 if phy_id == *recorded_phy_id {
927 if let Err(e) = reset_phy(&self.device_monitor, phy_id).await {
928 warn!("Resetting PHY {} failed: {:?}", phy_id, e);
929 }
930
931 if let Some(country_code) = self.saved_country_code {
933 info!("Setting country code after phy reset");
934 if let Err(e) =
935 set_phy_country_code(&self.device_monitor, phy_id, country_code)
936 .await
937 {
938 warn!(
939 "Proceeding with default country code because we failed to set the cached one: {}",
940 e
941 );
942 }
943 };
944
945 return;
946 }
947 }
948 }
949 RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::Disconnect { iface_id }) => {
950 if let Err(e) = disconnect(&self.device_monitor, iface_id).await {
951 warn!("Disconnecting client {} failed: {:?}", iface_id, e);
952 }
953 }
954 RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::StopAp { iface_id }) => {
955 if let Err(e) = stop_ap(&self.device_monitor, iface_id).await {
956 warn!("Stopping AP {} failed: {:?}", iface_id, e);
957 }
958 }
959 }
960 }
961 }
962
963 async fn on_before_suspend(&mut self) {
964 info!("Modifying WLAN driver lease to 'suspend' level");
965 for (phy_id, phy_container) in self.phys.iter_mut() {
966 Self::set_phy_power_element_lease(
967 &*self.power_manager,
968 *phy_id,
969 phy_container,
970 POWER_LEVEL_SUSPEND,
971 )
972 .await;
973 }
974 }
975
976 async fn on_after_resume(&mut self) {
977 info!("Modifying WLAN driver lease to 'on' level");
978 for (phy_id, phy_container) in self.phys.iter_mut() {
979 Self::set_phy_power_element_lease(
980 &*self.power_manager,
981 *phy_id,
982 phy_container,
983 POWER_LEVEL_ACTIVE,
984 )
985 .await;
986 }
987 }
988}
989
990pub async fn serve_suspend_blocker(
991 phy_manager: Arc<Mutex<dyn PhyManagerApi>>,
992 mut requests: fsystem::SuspendBlockerRequestStream,
993) -> Result<(), Error> {
994 while let Some(req) = requests.next().await {
995 match req {
996 Ok(fsystem::SuspendBlockerRequest::BeforeSuspend { responder }) => {
997 let mut phy_manager = phy_manager.lock().await;
998 phy_manager.on_before_suspend().await;
999 if let Err(e) = responder.send() {
1000 warn!("Failed to respond to BeforeSuspend: {}", e);
1001 }
1002 }
1003 Ok(fsystem::SuspendBlockerRequest::AfterResume { responder }) => {
1004 let mut phy_manager = phy_manager.lock().await;
1005 phy_manager.on_after_resume().await;
1006 if let Err(e) = responder.send() {
1007 warn!("Failed to respond to AfterResume: {}", e);
1008 }
1009 }
1010 Ok(fsystem::SuspendBlockerRequest::_UnknownMethod { ordinal, .. }) => {
1011 warn!("Unknown SuspendBlocker method: {}", ordinal);
1012 }
1013 Err(e) => {
1014 error!("SuspendBlocker request stream error: {}", e);
1015 return Err(e.into());
1016 }
1017 }
1018 }
1019 Ok(())
1020}
1021
1022async fn destroy_iface(
1024 proxy: &fidl_service::DeviceMonitorProxy,
1025 iface_id: u16,
1026 role: fidl_common::WlanMacRole,
1027 telemetry_sender: &TelemetrySender,
1028) -> Result<(), PhyManagerError> {
1029 let request = fidl_service::DestroyIfaceRequest { iface_id };
1030 let (destroy_iface_response, metric) = match proxy.destroy_iface(&request).await {
1031 Ok(status) => match status {
1032 zx::sys::ZX_OK => (Ok(()), Some(Ok(iface_id))),
1033 zx::sys::ZX_ERR_NOT_FOUND => {
1034 info!("Interface not found, assuming it is already destroyed");
1035 (Ok(()), None)
1037 }
1038 e => {
1039 warn!("failed to destroy iface {}: {}", iface_id, e);
1040 (Err(PhyManagerError::IfaceDestroyFailure), Some(Err(())))
1041 }
1042 },
1043 Err(e) => {
1044 warn!("failed to send destroy iface {}: {}", iface_id, e);
1045 (Err(PhyManagerError::IfaceDestroyFailure), Some(Err(())))
1046 }
1047 };
1048
1049 if let Some(result) = metric {
1050 telemetry_sender.send(TelemetryEvent::IfaceDestructionResult { role, result });
1051 }
1052
1053 destroy_iface_response
1054}
1055
1056async fn reset_phy(
1057 proxy: &fidl_service::DeviceMonitorProxy,
1058 phy_id: u16,
1059) -> Result<(), PhyManagerError> {
1060 let result = proxy.reset(phy_id).await.map_err(|e| {
1061 warn!("Request to reset PHY {} failed: {:?}", phy_id, e);
1062 PhyManagerError::InternalError
1063 })?;
1064
1065 result.map_err(|e| {
1066 warn!("Failed to reset PHY {}: {:?}", phy_id, e);
1067 PhyManagerError::PhyResetFailure
1068 })
1069}
1070
1071async fn set_phy_country_code(
1072 proxy: &fidl_service::DeviceMonitorProxy,
1073 phy_id: u16,
1074 country_code: client_types::CountryCode,
1075) -> Result<(), PhyManagerError> {
1076 let status = proxy
1077 .set_country(&fidl_service::SetCountryRequest { phy_id, alpha2: country_code.into() })
1078 .await
1079 .map_err(|e| {
1080 error!("Failed to set country code for PHY {}: {:?}", phy_id, e);
1081 PhyManagerError::PhySetCountryFailure
1082 })?;
1083
1084 zx::ok(status).map_err(|e| {
1085 error!("Received bad status when setting country code for PHY {}: {}", phy_id, e);
1086 PhyManagerError::PhySetCountryFailure
1087 })
1088}
1089
1090async fn clear_phy_country_code(
1091 proxy: &fidl_service::DeviceMonitorProxy,
1092 phy_id: u16,
1093) -> Result<(), PhyManagerError> {
1094 let status =
1095 proxy.clear_country(&fidl_service::ClearCountryRequest { phy_id }).await.map_err(|e| {
1096 error!("Failed to clear country code for PHY {}: {:?}", phy_id, e);
1097 PhyManagerError::PhySetCountryFailure
1098 })?;
1099
1100 zx::ok(status).map_err(|e| {
1101 error!("Received bad status when clearing country code for PHY {}: {}", phy_id, e);
1102 PhyManagerError::PhySetCountryFailure
1103 })
1104}
1105
1106async fn disconnect(
1107 dev_monitor_proxy: &fidl_service::DeviceMonitorProxy,
1108 iface_id: u16,
1109) -> Result<(), Error> {
1110 let (sme_proxy, remote) = create_proxy();
1111 dev_monitor_proxy.get_client_sme(iface_id, remote).await?.map_err(zx::Status::err_from_raw)?;
1112
1113 sme_proxy
1114 .disconnect(fidl_sme::UserDisconnectReason::Recovery)
1115 .await
1116 .map_err(|e| format_err!("Disconnect failed: {:?}", e))
1117}
1118
1119async fn stop_ap(
1120 dev_monitor_proxy: &fidl_service::DeviceMonitorProxy,
1121 iface_id: u16,
1122) -> Result<(), Error> {
1123 let (sme_proxy, remote) = create_proxy();
1124 dev_monitor_proxy.get_ap_sme(iface_id, remote).await?.map_err(zx::Status::err_from_raw)?;
1125
1126 match sme_proxy.stop().await {
1127 Ok(result) => match result {
1128 fidl_sme::StopApResultCode::Success => Ok(()),
1129 err => Err(format_err!("Stop AP failed: {:?}", err)),
1130 },
1131 Err(e) => Err(format_err!("Stop AP request failed: {:?}", e)),
1132 }
1133}
1134
1135#[cfg(test)]
1136mod tests {
1137 use super::*;
1138 use crate::telemetry;
1139 use crate::util::testing::{poll_ap_sme_req, poll_sme_req};
1140 use assert_matches::assert_matches;
1141 use diagnostics_assertions::assert_data_tree;
1142 use fidl::endpoints;
1143 use fidl_fuchsia_wlan_device_service as fidl_service;
1144 use fidl_fuchsia_wlan_sme as fidl_sme;
1145 use fuchsia_async::TestExecutor;
1146 use fuchsia_inspect as inspect;
1147 use futures::channel::mpsc;
1148 use futures::stream::StreamExt;
1149 use futures::task::Poll;
1150 use std::pin::pin;
1151 use test_case::test_case;
1152 use wlan_power_manager_testing::TestPowerManager;
1153 use zx::sys::{ZX_ERR_NOT_FOUND, ZX_OK};
1154
1155 struct TestValues {
1158 monitor_proxy: fidl_service::DeviceMonitorProxy,
1159 monitor_stream: fidl_service::DeviceMonitorRequestStream,
1160 inspector: inspect::Inspector,
1161 node: inspect::Node,
1162 telemetry_sender: TelemetrySender,
1163 telemetry_receiver: mpsc::Receiver<TelemetryEvent>,
1164 recovery_sender: recovery::RecoveryActionSender,
1165 recovery_receiver: recovery::RecoveryActionReceiver,
1166 power_manager: Arc<TestPowerManager>,
1167 }
1168
1169 fn test_setup() -> TestValues {
1171 let (monitor_proxy, monitor_requests) =
1172 endpoints::create_proxy::<fidl_service::DeviceMonitorMarker>();
1173 let monitor_stream = monitor_requests.into_stream();
1174
1175 let inspector = inspect::Inspector::default();
1176 let node = inspector.root().create_child("phy_manager");
1177 let (sender, telemetry_receiver) = mpsc::channel::<TelemetryEvent>(100);
1178 let telemetry_sender = TelemetrySender::new(sender);
1179 let (recovery_sender, recovery_receiver) =
1180 mpsc::channel::<recovery::RecoverySummary>(recovery::RECOVERY_SUMMARY_CHANNEL_CAPACITY);
1181 let power_manager = Arc::new(TestPowerManager::new());
1182
1183 TestValues {
1184 monitor_proxy,
1185 monitor_stream,
1186 inspector,
1187 node,
1188 telemetry_sender,
1189 telemetry_receiver,
1190 recovery_sender,
1191 recovery_receiver,
1192 power_manager,
1193 }
1194 }
1195
1196 fn send_get_power_element_dependency_token_response(
1197 exec: &mut TestExecutor,
1198 server: &mut fidl_service::DeviceMonitorRequestStream,
1199 response: Result<fbroker::DependencyToken, zx::sys::zx_status_t>,
1200 ) {
1201 let _ = assert_matches!(
1202 exec.run_until_stalled(&mut server.next()),
1203 Poll::Ready(Some(Ok(
1204 fidl_service::DeviceMonitorRequest::GetPowerElementDependencyToken {
1205 phy_id: _,
1206 responder,
1207 }
1208 ))) => {
1209 responder.send(response.as_ref().map_err(|status| *status).map(|token| {
1210 token.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("failed to duplicate token")
1211 }))
1212 }
1213 );
1214 }
1215
1216 fn send_get_supported_mac_roles_response(
1219 exec: &mut TestExecutor,
1220 server: &mut fidl_service::DeviceMonitorRequestStream,
1221 supported_mac_roles: Result<&[fidl_common::WlanMacRole], zx::sys::zx_status_t>,
1222 ) {
1223 let _ = assert_matches!(
1224 exec.run_until_stalled(&mut server.next()),
1225 Poll::Ready(Some(Ok(
1226 fidl_service::DeviceMonitorRequest::GetSupportedMacRoles {
1227 responder, ..
1228 }
1229 ))) => {
1230 responder.send(supported_mac_roles)
1231 }
1232 );
1233 }
1234
1235 #[track_caller]
1237 fn send_query_iface_response(
1238 exec: &mut TestExecutor,
1239 server: &mut fidl_service::DeviceMonitorRequestStream,
1240 iface_info: Option<fidl_service::QueryIfaceResponse>,
1241 ) {
1242 let response = iface_info.as_ref().ok_or(ZX_ERR_NOT_FOUND);
1243 assert_matches!(
1244 exec.run_until_stalled(&mut server.next()),
1245 Poll::Ready(Some(Ok(
1246 fidl_service::DeviceMonitorRequest::QueryIface {
1247 iface_id: _,
1248 responder,
1249 }
1250 ))) => {
1251 responder.send(response).expect("sending fake iface info");
1252 }
1253 );
1254 }
1255
1256 #[track_caller]
1259 fn send_create_iface_response(
1260 exec: &mut TestExecutor,
1261 server: &mut fidl_service::DeviceMonitorRequestStream,
1262 iface_id: Option<u16>,
1263 ) {
1264 assert_matches!(
1265 exec.run_until_stalled(&mut server.next()),
1266 Poll::Ready(Some(Ok(
1267 fidl_service::DeviceMonitorRequest::CreateIface {
1268 responder,
1269 ..
1270 }
1271 ))) => {
1272 match iface_id {
1273 Some(iface_id) => responder.send(
1274 Ok(&fidl_service::DeviceMonitorCreateIfaceResponse {
1275 iface_id: Some(iface_id),
1276 ..Default::default()
1277 })
1278 )
1279 .expect("sending fake iface id"),
1280 None => responder.send(Err(fidl_service::DeviceMonitorError::unknown())).expect("sending fake response with none")
1281 }
1282 }
1283 );
1284 }
1285
1286 fn send_destroy_iface_response(
1289 exec: &mut TestExecutor,
1290 server: &mut fidl_service::DeviceMonitorRequestStream,
1291 return_status: zx::sys::zx_status_t,
1292 ) {
1293 assert_matches!(
1294 exec.run_until_stalled(&mut server.next()),
1295 Poll::Ready(Some(Ok(
1296 fidl_service::DeviceMonitorRequest::DestroyIface {
1297 req: _,
1298 responder,
1299 }
1300 ))) => {
1301 responder
1302 .send(return_status)
1303 .unwrap_or_else(|e| panic!("sending fake response: {return_status}: {e:?}"));
1304 }
1305 );
1306 }
1307
1308 fn create_iface_response(
1310 role: fidl_common::WlanMacRole,
1311 id: u16,
1312 phy_id: u16,
1313 phy_assigned_id: u16,
1314 sta_addr: [u8; 6],
1315 factory_addr: [u8; 6],
1316 ) -> fidl_service::QueryIfaceResponse {
1317 fidl_service::QueryIfaceResponse {
1318 role,
1319 id,
1320 phy_id,
1321 phy_assigned_id,
1322 sta_addr,
1323 factory_addr,
1324 }
1325 }
1326
1327 #[fuchsia::test]
1332 fn add_valid_phy() {
1333 let mut exec = TestExecutor::new();
1334 let mut test_values = test_setup();
1335
1336 let fake_phy_id = 0;
1337 let fake_mac_roles = vec![];
1338
1339 let mut phy_manager = PhyManager::new(
1340 test_values.monitor_proxy,
1341 recovery::lookup_recovery_profile(""),
1342 false,
1343 test_values.node,
1344 test_values.telemetry_sender,
1345 test_values.recovery_sender,
1346 test_values.power_manager.clone(),
1347 );
1348 {
1349 let add_phy_fut = phy_manager.add_phy(0);
1350 let mut add_phy_fut = pin!(add_phy_fut);
1351 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1352
1353 send_get_power_element_dependency_token_response(
1354 &mut exec,
1355 &mut test_values.monitor_stream,
1356 Ok(zx::Event::create()),
1357 );
1358
1359 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1360
1361 send_get_supported_mac_roles_response(
1362 &mut exec,
1363 &mut test_values.monitor_stream,
1364 Ok(&fake_mac_roles),
1365 );
1366
1367 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1368 }
1369
1370 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1371 assert_eq!(
1372 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1373 fake_mac_roles.into_iter().collect()
1374 );
1375 }
1376
1377 #[fuchsia::test]
1381 fn add_invalid_phy() {
1382 let mut exec = TestExecutor::new();
1383 let mut test_values = test_setup();
1384 let mut phy_manager = PhyManager::new(
1385 test_values.monitor_proxy,
1386 recovery::lookup_recovery_profile(""),
1387 false,
1388 test_values.node,
1389 test_values.telemetry_sender,
1390 test_values.recovery_sender,
1391 test_values.power_manager.clone(),
1392 );
1393
1394 {
1395 let add_phy_fut = phy_manager.add_phy(1);
1396 let mut add_phy_fut = pin!(add_phy_fut);
1397 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1398
1399 send_get_power_element_dependency_token_response(
1400 &mut exec,
1401 &mut test_values.monitor_stream,
1402 Ok(zx::Event::create()),
1403 );
1404
1405 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1406
1407 send_get_supported_mac_roles_response(
1408 &mut exec,
1409 &mut test_values.monitor_stream,
1410 Err(zx::sys::ZX_ERR_NOT_FOUND),
1411 );
1412
1413 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1414 }
1415 assert!(phy_manager.phys.is_empty());
1416 }
1417
1418 #[fuchsia::test]
1422 fn add_duplicate_phy() {
1423 let mut exec = TestExecutor::new();
1424 let mut test_values = test_setup();
1425 let mut phy_manager = PhyManager::new(
1426 test_values.monitor_proxy,
1427 recovery::lookup_recovery_profile(""),
1428 false,
1429 test_values.node,
1430 test_values.telemetry_sender,
1431 test_values.recovery_sender,
1432 test_values.power_manager.clone(),
1433 );
1434
1435 let fake_phy_id = 0;
1436 let fake_mac_roles = vec![];
1437
1438 {
1439 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1440 let mut add_phy_fut = pin!(add_phy_fut);
1441 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1442
1443 send_get_power_element_dependency_token_response(
1444 &mut exec,
1445 &mut test_values.monitor_stream,
1446 Ok(zx::Event::create()),
1447 );
1448
1449 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1450
1451 send_get_supported_mac_roles_response(
1452 &mut exec,
1453 &mut test_values.monitor_stream,
1454 Ok(&fake_mac_roles),
1455 );
1456
1457 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1458 }
1459
1460 {
1461 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1462 assert_eq!(
1463 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1464 fake_mac_roles.clone().into_iter().collect()
1465 );
1466 }
1467
1468 {
1470 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1471 let mut add_phy_fut = pin!(add_phy_fut);
1472 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1473
1474 send_get_power_element_dependency_token_response(
1475 &mut exec,
1476 &mut test_values.monitor_stream,
1477 Ok(zx::Event::create()),
1478 );
1479
1480 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1481
1482 send_get_supported_mac_roles_response(
1483 &mut exec,
1484 &mut test_values.monitor_stream,
1485 Ok(&fake_mac_roles),
1486 );
1487
1488 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1489 }
1490
1491 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1492 assert_eq!(
1493 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1494 fake_mac_roles.into_iter().collect()
1495 );
1496 }
1497
1498 #[fuchsia::test]
1499 fn create_all_client_ifaces_after_phys_added() {
1500 let mut exec = TestExecutor::new();
1501 let mut test_values = test_setup();
1502 let mut phy_manager = PhyManager::new(
1503 test_values.monitor_proxy,
1504 recovery::lookup_recovery_profile(""),
1505 false,
1506 test_values.node,
1507 test_values.telemetry_sender,
1508 test_values.recovery_sender,
1509 test_values.power_manager.clone(),
1510 );
1511
1512 for phy_id in 0..2 {
1513 {
1514 let add_phy_fut = phy_manager.add_phy(phy_id);
1515 let mut add_phy_fut = pin!(add_phy_fut);
1516
1517 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1518
1519 send_get_power_element_dependency_token_response(
1520 &mut exec,
1521 &mut test_values.monitor_stream,
1522 Ok(zx::Event::create()),
1523 );
1524
1525 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1526
1527 send_get_supported_mac_roles_response(
1528 &mut exec,
1529 &mut test_values.monitor_stream,
1530 Ok(&[fidl_common::WlanMacRole::Client]),
1531 );
1532
1533 assert_matches!(exec.run_until_stalled(&mut add_phy_fut), Poll::Ready(Ok(())));
1534 }
1535 assert!(phy_manager.phys.contains_key(&phy_id));
1536 }
1537
1538 {
1539 let start_connections_fut = phy_manager
1540 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1541 let mut start_connections_fut = pin!(start_connections_fut);
1542
1543 for iface_id in [10, 20] {
1546 assert!(exec.run_until_stalled(&mut start_connections_fut).is_pending());
1547
1548 send_create_iface_response(
1549 &mut exec,
1550 &mut test_values.monitor_stream,
1551 Some(iface_id),
1552 );
1553 }
1554
1555 assert_matches!(exec.run_until_stalled(&mut start_connections_fut),
1556 Poll::Ready(iface_ids) => {
1557 assert!(iface_ids.values().all(Result::is_ok));
1558 assert!(iface_ids.contains_key(&0));
1559 assert!(iface_ids.contains_key(&1));
1560 let iface_ids: HashSet<_> = iface_ids.into_values().flat_map(Result::unwrap).collect();
1561 assert_eq!(iface_ids, HashSet::from([10, 20]));
1562 }
1563 );
1564 }
1565
1566 let mut iface_ids = HashSet::new();
1567 for phy_id in 0..2 {
1568 let phy_container = phy_manager.phys.get(&phy_id).unwrap();
1569 assert_eq!(phy_container.client_ifaces.len(), 1);
1572 phy_container.client_ifaces.iter().for_each(|iface_id| {
1573 assert!(iface_ids.insert(*iface_id));
1574 });
1575 assert!(phy_container.defects.events.is_empty());
1576 }
1577 assert_eq!(iface_ids, HashSet::from([10, 20]));
1578 }
1579
1580 #[fuchsia::test]
1584 fn add_phy_after_create_all_client_ifaces() {
1585 let mut exec = TestExecutor::new();
1586 let mut test_values = test_setup();
1587 let mut phy_manager = PhyManager::new(
1588 test_values.monitor_proxy,
1589 recovery::lookup_recovery_profile(""),
1590 false,
1591 test_values.node,
1592 test_values.telemetry_sender,
1593 test_values.recovery_sender,
1594 test_values.power_manager.clone(),
1595 );
1596
1597 let fake_iface_id = 1;
1598 let fake_phy_id = 1;
1599 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1600
1601 {
1602 let start_connections_fut = phy_manager
1603 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1604 let mut start_connections_fut = pin!(start_connections_fut);
1605 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1606 }
1607
1608 {
1611 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1612 let mut add_phy_fut = pin!(add_phy_fut);
1613 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1614
1615 send_get_power_element_dependency_token_response(
1616 &mut exec,
1617 &mut test_values.monitor_stream,
1618 Ok(zx::Event::create()),
1619 );
1620
1621 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1622
1623 send_get_supported_mac_roles_response(
1624 &mut exec,
1625 &mut test_values.monitor_stream,
1626 Ok(&fake_mac_roles),
1627 );
1628
1629 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1630
1631 send_create_iface_response(
1632 &mut exec,
1633 &mut test_values.monitor_stream,
1634 Some(fake_iface_id),
1635 );
1636
1637 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1638 }
1639
1640 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1641 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1642 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1643 assert!(phy_container.defects.events.is_empty());
1644 }
1645
1646 #[fuchsia::test]
1651 fn add_phy_with_iface_creation_failure() {
1652 let mut exec = TestExecutor::new();
1653 let mut test_values = test_setup();
1654 let mut phy_manager = PhyManager::new(
1655 test_values.monitor_proxy,
1656 recovery::lookup_recovery_profile(""),
1657 false,
1658 test_values.node,
1659 test_values.telemetry_sender,
1660 test_values.recovery_sender,
1661 test_values.power_manager.clone(),
1662 );
1663
1664 let fake_phy_id = 1;
1665 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1666
1667 {
1668 let start_connections_fut = phy_manager
1669 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1670 let mut start_connections_fut = pin!(start_connections_fut);
1671 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1672 }
1673
1674 {
1677 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1678 let mut add_phy_fut = pin!(add_phy_fut);
1679 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1680
1681 send_get_power_element_dependency_token_response(
1682 &mut exec,
1683 &mut test_values.monitor_stream,
1684 Ok(zx::Event::create()),
1685 );
1686
1687 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1688
1689 send_get_supported_mac_roles_response(
1690 &mut exec,
1691 &mut test_values.monitor_stream,
1692 Ok(&fake_mac_roles),
1693 );
1694
1695 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1696
1697 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
1699
1700 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1701 }
1702
1703 assert!(!phy_manager.phys.contains_key(&fake_phy_id));
1704 }
1705
1706 #[fuchsia::test]
1708 fn test_add_phy_after_setting_country_code() {
1709 let mut exec = TestExecutor::new();
1710 let mut test_values = test_setup();
1711
1712 let fake_phy_id = 1;
1713 let fake_mac_roles = vec![];
1714
1715 let mut phy_manager = PhyManager::new(
1716 test_values.monitor_proxy,
1717 recovery::lookup_recovery_profile(""),
1718 false,
1719 test_values.node,
1720 test_values.telemetry_sender,
1721 test_values.recovery_sender,
1722 test_values.power_manager.clone(),
1723 );
1724
1725 {
1726 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
1727 let mut set_country_fut = pin!(set_country_fut);
1728 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
1729 }
1730
1731 {
1732 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1733 let mut add_phy_fut = pin!(add_phy_fut);
1734 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1735
1736 send_get_power_element_dependency_token_response(
1737 &mut exec,
1738 &mut test_values.monitor_stream,
1739 Ok(zx::Event::create()),
1740 );
1741
1742 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1743
1744 send_get_supported_mac_roles_response(
1745 &mut exec,
1746 &mut test_values.monitor_stream,
1747 Ok(&fake_mac_roles),
1748 );
1749
1750 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1751
1752 assert_matches!(
1753 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
1754 Poll::Ready(Some(Ok(
1755 fidl_service::DeviceMonitorRequest::SetCountry {
1756 req: fidl_service::SetCountryRequest {
1757 phy_id: 1,
1758 alpha2: [b'U', b'S'],
1759 },
1760 responder,
1761 }
1762 ))) => {
1763 responder.send(ZX_OK).expect("sending fake set country response");
1764 }
1765 );
1766
1767 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1768 }
1769
1770 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1771 assert_eq!(
1772 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1773 fake_mac_roles.into_iter().collect()
1774 );
1775 }
1776
1777 #[fuchsia::test]
1778 async fn remove_valid_phy() {
1779 let test_values = test_setup();
1780 let mut phy_manager = PhyManager::new(
1781 test_values.monitor_proxy,
1782 recovery::lookup_recovery_profile(""),
1783 false,
1784 test_values.node,
1785 test_values.telemetry_sender,
1786 test_values.recovery_sender,
1787 test_values.power_manager.clone(),
1788 );
1789
1790 let fake_phy_id = 1;
1791 let fake_mac_roles = vec![];
1792
1793 let phy_container = PhyContainer::new(fake_mac_roles);
1794 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1795 phy_manager.remove_phy(fake_phy_id);
1796 assert!(phy_manager.phys.is_empty());
1797 }
1798
1799 #[fuchsia::test]
1804 async fn remove_nonexistent_phy() {
1805 let test_values = test_setup();
1806 let mut phy_manager = PhyManager::new(
1807 test_values.monitor_proxy,
1808 recovery::lookup_recovery_profile(""),
1809 false,
1810 test_values.node,
1811 test_values.telemetry_sender,
1812 test_values.recovery_sender,
1813 test_values.power_manager.clone(),
1814 );
1815
1816 let fake_phy_id = 1;
1817 let fake_mac_roles = vec![];
1818
1819 let phy_container = PhyContainer::new(fake_mac_roles);
1820 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1821 phy_manager.remove_phy(2);
1822 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1823 }
1824
1825 #[fuchsia::test]
1829 fn on_iface_added() {
1830 let mut exec = TestExecutor::new();
1831 let mut test_values = test_setup();
1832 let mut phy_manager = PhyManager::new(
1833 test_values.monitor_proxy,
1834 recovery::lookup_recovery_profile(""),
1835 false,
1836 test_values.node,
1837 test_values.telemetry_sender,
1838 test_values.recovery_sender,
1839 test_values.power_manager.clone(),
1840 );
1841
1842 let fake_phy_id = 1;
1845 let fake_mac_roles = vec![];
1846
1847 let phy_container = PhyContainer::new(fake_mac_roles);
1848
1849 let fake_role = fidl_common::WlanMacRole::Client;
1851 let fake_iface_id = 1;
1852 let fake_phy_assigned_id = 1;
1853 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1854 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1855 let iface_response = create_iface_response(
1856 fake_role,
1857 fake_iface_id,
1858 fake_phy_id,
1859 fake_phy_assigned_id,
1860 fake_sta_addr,
1861 fake_factory_addr,
1862 );
1863
1864 {
1865 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1867
1868 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1870 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1871 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1872
1873 send_query_iface_response(
1874 &mut exec,
1875 &mut test_values.monitor_stream,
1876 Some(iface_response),
1877 );
1878
1879 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1881 }
1882
1883 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1886 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1887 }
1888
1889 #[fuchsia::test]
1890 fn on_iface_added_unknown_role_is_unsupported() {
1891 let mut exec = TestExecutor::new();
1892 let mut test_values = test_setup();
1893 let mut phy_manager = PhyManager::new(
1894 test_values.monitor_proxy,
1895 recovery::lookup_recovery_profile(""),
1896 false,
1897 test_values.node,
1898 test_values.telemetry_sender,
1899 test_values.recovery_sender,
1900 test_values.power_manager.clone(),
1901 );
1902
1903 let fake_phy_id = 1;
1906 let fake_mac_roles = vec![];
1907
1908 let phy_container = PhyContainer::new(fake_mac_roles);
1909
1910 let fake_role = fidl_common::WlanMacRole::unknown();
1912 let fake_iface_id = 1;
1913 let fake_phy_assigned_id = 1;
1914 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1915 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1916 let iface_response = create_iface_response(
1917 fake_role,
1918 fake_iface_id,
1919 fake_phy_id,
1920 fake_phy_assigned_id,
1921 fake_sta_addr,
1922 fake_factory_addr,
1923 );
1924
1925 {
1926 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1928
1929 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1931 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1932 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1933
1934 send_query_iface_response(
1935 &mut exec,
1936 &mut test_values.monitor_stream,
1937 Some(iface_response),
1938 );
1939
1940 assert_matches!(
1942 exec.run_until_stalled(&mut on_iface_added_fut),
1943 Poll::Ready(Err(PhyManagerError::Unsupported))
1944 );
1945 }
1946
1947 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1950 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
1951 }
1952
1953 #[fuchsia::test]
1958 fn on_iface_added_missing_phy() {
1959 let mut exec = TestExecutor::new();
1960 let mut test_values = test_setup();
1961 let mut phy_manager = PhyManager::new(
1962 test_values.monitor_proxy,
1963 recovery::lookup_recovery_profile(""),
1964 false,
1965 test_values.node,
1966 test_values.telemetry_sender,
1967 test_values.recovery_sender,
1968 test_values.power_manager.clone(),
1969 );
1970
1971 let fake_phy_id = 1;
1974 let fake_mac_roles = vec![];
1975
1976 let fake_role = fidl_common::WlanMacRole::Client;
1978 let fake_iface_id = 1;
1979 let fake_phy_assigned_id = 1;
1980 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1981 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1982 let iface_response = create_iface_response(
1983 fake_role,
1984 fake_iface_id,
1985 fake_phy_id,
1986 fake_phy_assigned_id,
1987 fake_sta_addr,
1988 fake_factory_addr,
1989 );
1990
1991 {
1992 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1994 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1995
1996 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
2001
2002 send_query_iface_response(
2003 &mut exec,
2004 &mut test_values.monitor_stream,
2005 Some(iface_response),
2006 );
2007
2008 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
2010
2011 send_get_power_element_dependency_token_response(
2012 &mut exec,
2013 &mut test_values.monitor_stream,
2014 Ok(zx::Event::create()),
2015 );
2016
2017 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
2018
2019 send_get_supported_mac_roles_response(
2020 &mut exec,
2021 &mut test_values.monitor_stream,
2022 Ok(&fake_mac_roles),
2023 );
2024
2025 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
2027 }
2028
2029 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2032
2033 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2034 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2035 }
2036
2037 #[fuchsia::test]
2041 fn add_duplicate_iface() {
2042 let mut exec = TestExecutor::new();
2043 let mut test_values = test_setup();
2044 let mut phy_manager = PhyManager::new(
2045 test_values.monitor_proxy,
2046 recovery::lookup_recovery_profile(""),
2047 false,
2048 test_values.node,
2049 test_values.telemetry_sender,
2050 test_values.recovery_sender,
2051 test_values.power_manager.clone(),
2052 );
2053
2054 let fake_phy_id = 1;
2057 let fake_mac_roles = vec![];
2058
2059 let phy_container = PhyContainer::new(fake_mac_roles);
2061 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2062
2063 let fake_role = fidl_common::WlanMacRole::Client;
2065 let fake_iface_id = 1;
2066 let fake_phy_assigned_id = 1;
2067 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
2068 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
2069 let iface_response = create_iface_response(
2070 fake_role,
2071 fake_iface_id,
2072 fake_phy_id,
2073 fake_phy_assigned_id,
2074 fake_sta_addr,
2075 fake_factory_addr,
2076 );
2077
2078 for _ in 0..2 {
2080 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
2082 let mut on_iface_added_fut = pin!(on_iface_added_fut);
2083 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
2084
2085 send_query_iface_response(
2086 &mut exec,
2087 &mut test_values.monitor_stream,
2088 Some(iface_response),
2089 );
2090
2091 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
2093 }
2094
2095 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2098 assert_eq!(phy_container.client_ifaces.len(), 1);
2099 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2100 }
2101
2102 #[fuchsia::test]
2106 fn add_nonexistent_iface() {
2107 let mut exec = TestExecutor::new();
2108 let mut test_values = test_setup();
2109 let mut phy_manager = PhyManager::new(
2110 test_values.monitor_proxy,
2111 recovery::lookup_recovery_profile(""),
2112 false,
2113 test_values.node,
2114 test_values.telemetry_sender,
2115 test_values.recovery_sender,
2116 test_values.power_manager.clone(),
2117 );
2118
2119 {
2120 let on_iface_added_fut = phy_manager.on_iface_added(1);
2122 let mut on_iface_added_fut = pin!(on_iface_added_fut);
2123 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
2124
2125 send_query_iface_response(&mut exec, &mut test_values.monitor_stream, None);
2126
2127 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
2129 }
2130
2131 assert!(phy_manager.phys.is_empty());
2134 }
2135
2136 #[fuchsia::test]
2140 async fn test_on_iface_removed() {
2141 let test_values = test_setup();
2142 let mut phy_manager = PhyManager::new(
2143 test_values.monitor_proxy,
2144 recovery::lookup_recovery_profile(""),
2145 false,
2146 test_values.node,
2147 test_values.telemetry_sender,
2148 test_values.recovery_sender,
2149 test_values.power_manager.clone(),
2150 );
2151
2152 let fake_phy_id = 1;
2155 let fake_mac_roles = vec![];
2156
2157 let mut phy_container = PhyContainer::new(fake_mac_roles);
2159 let fake_iface_id = 1;
2160 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2161
2162 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2163
2164 phy_manager.on_iface_removed(fake_iface_id);
2165
2166 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2168 assert!(phy_container.client_ifaces.is_empty());
2169 }
2170
2171 #[fuchsia::test]
2175 async fn remove_missing_iface() {
2176 let test_values = test_setup();
2177 let mut phy_manager = PhyManager::new(
2178 test_values.monitor_proxy,
2179 recovery::lookup_recovery_profile(""),
2180 false,
2181 test_values.node,
2182 test_values.telemetry_sender,
2183 test_values.recovery_sender,
2184 test_values.power_manager.clone(),
2185 );
2186
2187 let fake_phy_id = 1;
2190 let fake_mac_roles = vec![];
2191
2192 let present_iface_id = 1;
2193 let removed_iface_id = 2;
2194
2195 let mut phy_container = PhyContainer::new(fake_mac_roles);
2197 let _ = phy_container.client_ifaces.insert(present_iface_id);
2198 let _ = phy_container.client_ifaces.insert(removed_iface_id);
2199 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2200 phy_manager.on_iface_removed(removed_iface_id);
2201
2202 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2204 assert_eq!(phy_container.client_ifaces.len(), 1);
2205 assert!(phy_container.client_ifaces.contains(&present_iface_id));
2206 }
2207
2208 #[fuchsia::test]
2211 async fn get_client_no_phys() {
2212 let test_values = test_setup();
2213 let mut phy_manager = PhyManager::new(
2214 test_values.monitor_proxy,
2215 recovery::lookup_recovery_profile(""),
2216 false,
2217 test_values.node,
2218 test_values.telemetry_sender,
2219 test_values.recovery_sender,
2220 test_values.power_manager.clone(),
2221 );
2222
2223 let client = phy_manager.get_client();
2224 assert!(client.is_none());
2225 }
2226
2227 #[fuchsia::test]
2231 async fn get_unconfigured_client() {
2232 let test_values = test_setup();
2233 let mut phy_manager = PhyManager::new(
2234 test_values.monitor_proxy,
2235 recovery::lookup_recovery_profile(""),
2236 false,
2237 test_values.node,
2238 test_values.telemetry_sender,
2239 test_values.recovery_sender,
2240 test_values.power_manager.clone(),
2241 );
2242
2243 let fake_phy_id = 1;
2246 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2247 let phy_container = PhyContainer::new(fake_mac_roles);
2248
2249 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2250
2251 let client = phy_manager.get_client();
2253 assert!(client.is_none());
2254 }
2255
2256 #[fuchsia::test]
2260 async fn get_configured_client() {
2261 let test_values = test_setup();
2262 let mut phy_manager = PhyManager::new(
2263 test_values.monitor_proxy,
2264 recovery::lookup_recovery_profile(""),
2265 false,
2266 test_values.node,
2267 test_values.telemetry_sender,
2268 test_values.recovery_sender,
2269 test_values.power_manager.clone(),
2270 );
2271 phy_manager.client_connections_enabled = true;
2272
2273 let fake_phy_id = 1;
2276 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2277 let phy_container = PhyContainer::new(fake_mac_roles);
2278
2279 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2280
2281 let fake_iface_id = 1;
2283 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2284 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2285
2286 let client = phy_manager.get_client();
2288 assert_eq!(client.unwrap(), fake_iface_id)
2289 }
2290
2291 #[fuchsia::test]
2295 async fn get_client_no_compatible_phys() {
2296 let test_values = test_setup();
2297 let mut phy_manager = PhyManager::new(
2298 test_values.monitor_proxy,
2299 recovery::lookup_recovery_profile(""),
2300 false,
2301 test_values.node,
2302 test_values.telemetry_sender,
2303 test_values.recovery_sender,
2304 test_values.power_manager.clone(),
2305 );
2306
2307 let fake_iface_id = 1;
2310 let fake_phy_id = 1;
2311 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2312 let mut phy_container = PhyContainer::new(fake_mac_roles);
2313 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2314 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2315
2316 let client = phy_manager.get_client();
2318 assert!(client.is_none());
2319 }
2320
2321 #[fuchsia::test]
2324 fn get_client_while_stopped() {
2325 let _exec = TestExecutor::new();
2326 let test_values = test_setup();
2327
2328 let mut phy_manager = PhyManager::new(
2330 test_values.monitor_proxy,
2331 recovery::lookup_recovery_profile(""),
2332 false,
2333 test_values.node,
2334 test_values.telemetry_sender,
2335 test_values.recovery_sender,
2336 test_values.power_manager.clone(),
2337 );
2338 assert!(!phy_manager.client_connections_enabled);
2339
2340 let fake_phy_id = 1;
2342 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2343 let mut phy_container = PhyContainer::new(fake_mac_roles);
2344 let _ = phy_container.client_ifaces.insert(1);
2345 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2346
2347 assert_eq!(phy_manager.get_client(), None);
2350 }
2351
2352 #[fuchsia::test]
2356 fn destroy_all_client_ifaces() {
2357 let mut exec = TestExecutor::new();
2358 let mut test_values = test_setup();
2359 let mut phy_manager = PhyManager::new(
2360 test_values.monitor_proxy,
2361 recovery::lookup_recovery_profile(""),
2362 false,
2363 test_values.node,
2364 test_values.telemetry_sender,
2365 test_values.recovery_sender,
2366 test_values.power_manager.clone(),
2367 );
2368
2369 let fake_iface_id = 1;
2372 let fake_phy_id = 1;
2373 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2374 let phy_container = PhyContainer::new(fake_mac_roles);
2375
2376 {
2377 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2378
2379 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2381 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2382
2383 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2385 let mut stop_clients_future = pin!(stop_clients_future);
2386
2387 assert!(exec.run_until_stalled(&mut stop_clients_future).is_pending());
2388
2389 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2390
2391 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2392 }
2393
2394 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2396
2397 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2398 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
2399
2400 assert!(!phy_manager.client_connections_enabled);
2402
2403 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2405 }
2406
2407 #[fuchsia::test]
2410 fn destroy_all_client_ifaces_no_clients() {
2411 let mut exec = TestExecutor::new();
2412 let test_values = test_setup();
2413 let mut phy_manager = PhyManager::new(
2414 test_values.monitor_proxy,
2415 recovery::lookup_recovery_profile(""),
2416 false,
2417 test_values.node,
2418 test_values.telemetry_sender,
2419 test_values.recovery_sender,
2420 test_values.power_manager.clone(),
2421 );
2422
2423 let fake_iface_id = 1;
2426 let fake_phy_id = 1;
2427 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2428 let phy_container = PhyContainer::new(fake_mac_roles);
2429
2430 {
2432 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2433
2434 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2436 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2437
2438 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2440 let mut stop_clients_future = pin!(stop_clients_future);
2441
2442 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2443 }
2444
2445 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2447
2448 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2449 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2450 }
2451
2452 #[fuchsia::test]
2454 fn destroy_all_client_ifaces_fails() {
2455 let mut exec = TestExecutor::new();
2456 let test_values = test_setup();
2457 let mut phy_manager = PhyManager::new(
2458 test_values.monitor_proxy,
2459 recovery::lookup_recovery_profile(""),
2460 false,
2461 test_values.node,
2462 test_values.telemetry_sender,
2463 test_values.recovery_sender,
2464 test_values.power_manager.clone(),
2465 );
2466
2467 drop(test_values.monitor_stream);
2469
2470 let fake_iface_id = 1;
2473 let fake_phy_id = 1;
2474 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2475 let mut phy_container = PhyContainer::new(fake_mac_roles);
2476
2477 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2480
2481 {
2482 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2483
2484 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2486 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2487
2488 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2490 let mut stop_clients_future = pin!(stop_clients_future);
2491 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2492 }
2493
2494 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2496
2497 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2498 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2499 assert_eq!(phy_container.defects.events.len(), 1);
2500 assert_eq!(
2501 phy_container.defects.events[0].value,
2502 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2503 );
2504 }
2505
2506 #[fuchsia::test]
2509 fn get_ap_no_phys() {
2510 let mut exec = TestExecutor::new();
2511 let test_values = test_setup();
2512 let mut phy_manager = PhyManager::new(
2513 test_values.monitor_proxy,
2514 recovery::lookup_recovery_profile(""),
2515 false,
2516 test_values.node,
2517 test_values.telemetry_sender,
2518 test_values.recovery_sender,
2519 test_values.power_manager.clone(),
2520 );
2521
2522 let get_ap_future = phy_manager.create_or_get_ap_iface();
2523
2524 let mut get_ap_future = pin!(get_ap_future);
2525 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2526 }
2527
2528 #[fuchsia::test]
2532 fn get_unconfigured_ap() {
2533 let mut exec = TestExecutor::new();
2534 let mut test_values = test_setup();
2535 let mut phy_manager = PhyManager::new(
2536 test_values.monitor_proxy,
2537 recovery::lookup_recovery_profile(""),
2538 false,
2539 test_values.node,
2540 test_values.telemetry_sender,
2541 test_values.recovery_sender,
2542 test_values.power_manager.clone(),
2543 );
2544
2545 let fake_phy_id = 1;
2548 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2549 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2550
2551 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2552
2553 let fake_iface_id = 1;
2555 {
2556 let get_ap_future = phy_manager.create_or_get_ap_iface();
2557
2558 let mut get_ap_future = pin!(get_ap_future);
2559 assert!(exec.run_until_stalled(&mut get_ap_future).is_pending());
2560
2561 send_create_iface_response(
2562 &mut exec,
2563 &mut test_values.monitor_stream,
2564 Some(fake_iface_id),
2565 );
2566 assert_matches!(
2567 exec.run_until_stalled(&mut get_ap_future),
2568 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2569 );
2570 }
2571
2572 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.contains(&fake_iface_id));
2573 }
2574
2575 #[fuchsia::test]
2577 fn get_ap_iface_creation_fails() {
2578 let mut exec = TestExecutor::new();
2579 let test_values = test_setup();
2580 let mut phy_manager = PhyManager::new(
2581 test_values.monitor_proxy,
2582 recovery::lookup_recovery_profile(""),
2583 false,
2584 test_values.node,
2585 test_values.telemetry_sender,
2586 test_values.recovery_sender,
2587 test_values.power_manager.clone(),
2588 );
2589
2590 drop(test_values.monitor_stream);
2592
2593 let fake_phy_id = 1;
2596 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2597 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2598
2599 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2602
2603 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2604
2605 {
2606 let get_ap_future = phy_manager.create_or_get_ap_iface();
2607
2608 let mut get_ap_future = pin!(get_ap_future);
2609 assert!(exec.run_until_stalled(&mut get_ap_future).is_ready());
2610 }
2611
2612 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.is_empty());
2613 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
2614 assert_eq!(
2615 phy_manager.phys[&fake_phy_id].defects.events[0].value,
2616 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
2617 );
2618 }
2619
2620 #[fuchsia::test]
2624 fn get_configured_ap() {
2625 let mut exec = TestExecutor::new();
2626 let test_values = test_setup();
2627 let mut phy_manager = PhyManager::new(
2628 test_values.monitor_proxy,
2629 recovery::lookup_recovery_profile(""),
2630 false,
2631 test_values.node,
2632 test_values.telemetry_sender,
2633 test_values.recovery_sender,
2634 test_values.power_manager.clone(),
2635 );
2636
2637 let fake_phy_id = 1;
2640 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2641 let phy_container = PhyContainer::new(fake_mac_roles);
2642
2643 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2644
2645 let fake_iface_id = 1;
2647 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2648 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2649
2650 let get_ap_future = phy_manager.create_or_get_ap_iface();
2652 let mut get_ap_future = pin!(get_ap_future);
2653 assert_matches!(
2654 exec.run_until_stalled(&mut get_ap_future),
2655 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2656 );
2657 }
2658
2659 #[fuchsia::test]
2662 fn get_ap_no_compatible_phys() {
2663 let mut exec = TestExecutor::new();
2664 let test_values = test_setup();
2665 let mut phy_manager = PhyManager::new(
2666 test_values.monitor_proxy,
2667 recovery::lookup_recovery_profile(""),
2668 false,
2669 test_values.node,
2670 test_values.telemetry_sender,
2671 test_values.recovery_sender,
2672 test_values.power_manager.clone(),
2673 );
2674
2675 let fake_phy_id = 1;
2678 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2679 let phy_container = PhyContainer::new(fake_mac_roles);
2680
2681 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2682
2683 let get_ap_future = phy_manager.create_or_get_ap_iface();
2685 let mut get_ap_future = pin!(get_ap_future);
2686 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2687 }
2688
2689 #[fuchsia::test]
2692 fn stop_valid_ap_iface() {
2693 let mut exec = TestExecutor::new();
2694 let mut test_values = test_setup();
2695 let mut phy_manager = PhyManager::new(
2696 test_values.monitor_proxy,
2697 recovery::lookup_recovery_profile(""),
2698 false,
2699 test_values.node,
2700 test_values.telemetry_sender,
2701 test_values.recovery_sender,
2702 test_values.power_manager.clone(),
2703 );
2704
2705 let fake_iface_id = 1;
2708 let fake_phy_id = 1;
2709 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2710
2711 {
2712 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2713
2714 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2715
2716 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2718 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2719
2720 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2722 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2723 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2724 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2725
2726 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2727 }
2728
2729 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2730
2731 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2732 assert!(!phy_container.ap_ifaces.contains(&fake_iface_id));
2733 assert!(phy_container.defects.events.is_empty());
2734 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2735 }
2736
2737 #[fuchsia::test]
2740 fn stop_invalid_ap_iface() {
2741 let mut exec = TestExecutor::new();
2742 let test_values = test_setup();
2743 let mut phy_manager = PhyManager::new(
2744 test_values.monitor_proxy,
2745 recovery::lookup_recovery_profile(""),
2746 false,
2747 test_values.node,
2748 test_values.telemetry_sender,
2749 test_values.recovery_sender,
2750 test_values.power_manager.clone(),
2751 );
2752
2753 let fake_iface_id = 1;
2756 let fake_phy_id = 1;
2757 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2758
2759 {
2760 let phy_container = PhyContainer::new(fake_mac_roles);
2761
2762 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2763
2764 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2766 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2767
2768 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(2);
2770 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2771 assert_matches!(
2772 exec.run_until_stalled(&mut destroy_ap_iface_future),
2773 Poll::Ready(Ok(()))
2774 );
2775 }
2776
2777 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2778
2779 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2780 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2781 assert!(phy_container.defects.events.is_empty());
2782 }
2783
2784 #[fuchsia::test]
2787 fn stop_ap_iface_fails() {
2788 let mut exec = TestExecutor::new();
2789 let test_values = test_setup();
2790 let mut phy_manager = PhyManager::new(
2791 test_values.monitor_proxy,
2792 recovery::lookup_recovery_profile(""),
2793 false,
2794 test_values.node,
2795 test_values.telemetry_sender,
2796 test_values.recovery_sender,
2797 test_values.power_manager.clone(),
2798 );
2799
2800 drop(test_values.monitor_stream);
2802
2803 let fake_iface_id = 1;
2806 let fake_phy_id = 1;
2807 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2808
2809 {
2810 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2811
2812 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2815
2816 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2817
2818 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2820 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2821
2822 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2824 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2825 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2826 }
2827
2828 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2829
2830 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2831 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2832 assert_eq!(phy_container.defects.events.len(), 1);
2833 assert_eq!(
2834 phy_container.defects.events[0].value,
2835 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2836 );
2837 }
2838
2839 #[fuchsia::test]
2844 fn stop_all_ap_ifaces() {
2845 let mut exec = TestExecutor::new();
2846 let mut test_values = test_setup();
2847 let mut phy_manager = PhyManager::new(
2848 test_values.monitor_proxy,
2849 recovery::lookup_recovery_profile(""),
2850 false,
2851 test_values.node,
2852 test_values.telemetry_sender,
2853 test_values.recovery_sender,
2854 test_values.power_manager.clone(),
2855 );
2856
2857 let fake_phy_id = 1;
2860 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2861
2862 {
2863 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2864
2865 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2866
2867 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2869 let _ = phy_container.ap_ifaces.insert(0);
2870 let _ = phy_container.ap_ifaces.insert(1);
2871
2872 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2874 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2875
2876 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2877 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2878
2879 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2880 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2881
2882 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2883 }
2884
2885 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2886
2887 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2888 assert!(phy_container.ap_ifaces.is_empty());
2889 assert!(phy_container.destroyed_ifaces.contains(&0));
2890 assert!(phy_container.destroyed_ifaces.contains(&1));
2891 assert!(phy_container.defects.events.is_empty());
2892 }
2893
2894 #[fuchsia::test]
2898 fn stop_all_ap_ifaces_with_client() {
2899 let mut exec = TestExecutor::new();
2900 let test_values = test_setup();
2901 let mut phy_manager = PhyManager::new(
2902 test_values.monitor_proxy,
2903 recovery::lookup_recovery_profile(""),
2904 false,
2905 test_values.node,
2906 test_values.telemetry_sender,
2907 test_values.recovery_sender,
2908 test_values.power_manager.clone(),
2909 );
2910
2911 let fake_iface_id = 1;
2914 let fake_phy_id = 1;
2915 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2916
2917 {
2918 let phy_container = PhyContainer::new(fake_mac_roles);
2919
2920 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2921
2922 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2924 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2925
2926 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2928 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2929 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2930 }
2931
2932 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2933
2934 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2935 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2936 assert!(phy_container.defects.events.is_empty());
2937 }
2938
2939 #[fuchsia::test]
2941 fn stop_all_ap_ifaces_fails() {
2942 let mut exec = TestExecutor::new();
2943 let test_values = test_setup();
2944 let mut phy_manager = PhyManager::new(
2945 test_values.monitor_proxy,
2946 recovery::lookup_recovery_profile(""),
2947 false,
2948 test_values.node,
2949 test_values.telemetry_sender,
2950 test_values.recovery_sender,
2951 test_values.power_manager.clone(),
2952 );
2953
2954 drop(test_values.monitor_stream);
2956
2957 let fake_phy_id = 1;
2960 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2961
2962 {
2963 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2964
2965 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2968
2969 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2970
2971 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2973 let _ = phy_container.ap_ifaces.insert(0);
2974 let _ = phy_container.ap_ifaces.insert(1);
2975
2976 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2978 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2979 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2980 }
2981
2982 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2983
2984 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2985 assert_eq!(phy_container.ap_ifaces.len(), 2);
2986 assert_eq!(phy_container.defects.events.len(), 2);
2987 assert_eq!(
2988 phy_container.defects.events[0].value,
2989 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2990 );
2991 assert_eq!(
2992 phy_container.defects.events[1].value,
2993 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2994 );
2995 }
2996
2997 #[fuchsia::test]
3001 fn test_suggest_ap_mac() {
3002 let mut exec = TestExecutor::new();
3003 let mut test_values = test_setup();
3004 let mut phy_manager = PhyManager::new(
3005 test_values.monitor_proxy,
3006 recovery::lookup_recovery_profile(""),
3007 false,
3008 test_values.node,
3009 test_values.telemetry_sender,
3010 test_values.recovery_sender,
3011 test_values.power_manager.clone(),
3012 );
3013
3014 let fake_iface_id = 1;
3017 let fake_phy_id = 1;
3018 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
3019 let phy_container = PhyContainer::new(fake_mac_roles.clone());
3020
3021 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3022
3023 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
3025 let _ = phy_container.client_ifaces.insert(fake_iface_id);
3026
3027 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
3029 phy_manager.suggest_ap_mac(mac);
3030
3031 let get_ap_future = phy_manager.create_or_get_ap_iface();
3032 let mut get_ap_future = pin!(get_ap_future);
3033 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Pending);
3034
3035 assert_matches!(
3037 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3038 Poll::Ready(Some(Ok(
3039 fidl_service::DeviceMonitorRequest::CreateIface {
3040 payload,
3041 responder,
3042 }
3043 ))) => {
3044 let requested_mac: MacAddr = payload.sta_address.unwrap().into();
3045 assert_eq!(requested_mac, mac);
3046 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3047 iface_id: Some(fake_iface_id),
3048 ..Default::default()
3049 };
3050 responder.send(Ok(&response)).expect("sending fake iface id");
3051 }
3052 );
3053 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(_));
3054 }
3055
3056 #[fuchsia::test]
3057 fn test_suggested_mac_does_not_apply_to_client() {
3058 let mut exec = TestExecutor::new();
3059 let mut test_values = test_setup();
3060 let mut phy_manager = PhyManager::new(
3061 test_values.monitor_proxy,
3062 recovery::lookup_recovery_profile(""),
3063 false,
3064 test_values.node,
3065 test_values.telemetry_sender,
3066 test_values.recovery_sender,
3067 test_values.power_manager.clone(),
3068 );
3069
3070 let fake_iface_id = 1;
3073 let fake_phy_id = 1;
3074 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3075 let phy_container = PhyContainer::new(fake_mac_roles.clone());
3076
3077 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3078
3079 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
3081 phy_manager.suggest_ap_mac(mac);
3082
3083 let start_client_future =
3085 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3086 let mut start_client_future = pin!(start_client_future);
3087 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Pending);
3088
3089 assert_matches!(
3091 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3092 Poll::Ready(Some(Ok(
3093 fidl_service::DeviceMonitorRequest::CreateIface {
3094 payload,
3095 responder,
3096 }
3097 ))) => {
3098 assert_eq!(payload.sta_address, Some(ieee80211::NULL_ADDR.to_array()));
3099 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3100 iface_id: Some(fake_iface_id),
3101 ..Default::default()
3102 };
3103 responder.send(Ok(&response)).expect("sending fake iface id");
3104 }
3105 );
3106 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Ready(_));
3107 }
3108
3109 #[fuchsia::test]
3111 fn test_iface_creation_fails_during_start_client_connections() {
3112 let mut exec = TestExecutor::new();
3113 let test_values = test_setup();
3114 let mut phy_manager = PhyManager::new(
3115 test_values.monitor_proxy,
3116 recovery::lookup_recovery_profile(""),
3117 false,
3118 test_values.node,
3119 test_values.telemetry_sender,
3120 test_values.recovery_sender,
3121 test_values.power_manager.clone(),
3122 );
3123
3124 drop(test_values.monitor_stream);
3126
3127 let fake_phy_id = 1;
3130 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3131 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
3132
3133 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
3136
3137 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3138
3139 {
3140 let start_client_future = phy_manager
3142 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3143 let mut start_client_future = pin!(start_client_future);
3144 assert!(exec.run_until_stalled(&mut start_client_future).is_ready());
3145 }
3146
3147 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
3149 assert_eq!(
3150 phy_manager.phys[&fake_phy_id].defects.events[0].value,
3151 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
3152 );
3153 }
3154
3155 #[fuchsia::test]
3156 fn test_all_iface_creation_failures_retained_across_multiple_phys() {
3157 let mut exec = TestExecutor::new();
3158 let test_values = test_setup();
3159 let mut phy_manager = PhyManager::new(
3160 test_values.monitor_proxy,
3161 recovery::lookup_recovery_profile(""),
3162 false,
3163 test_values.node,
3164 test_values.telemetry_sender,
3165 test_values.recovery_sender,
3166 test_values.power_manager.clone(),
3167 );
3168
3169 drop(test_values.monitor_stream);
3171
3172 for fake_phy_id in 0..2 {
3175 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3176 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
3177
3178 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
3181
3182 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3183 }
3184
3185 let start_client_future =
3186 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3187 let mut start_client_future = pin!(start_client_future);
3188 assert_matches!(exec.run_until_stalled(&mut start_client_future),
3189 Poll::Ready(iface_ids) => {
3190 assert_eq!(iface_ids.len(), 2);
3191 assert_eq!(iface_ids[&0], Err(PhyManagerError::IfaceCreateFailure));
3192 assert_eq!(iface_ids[&1], Err(PhyManagerError::IfaceCreateFailure));
3193 }
3194 );
3195 }
3196
3197 #[fuchsia::test]
3201 async fn get_phy_ids_no_phys() {
3202 let test_values = test_setup();
3203 let phy_manager = PhyManager::new(
3204 test_values.monitor_proxy,
3205 recovery::lookup_recovery_profile(""),
3206 false,
3207 test_values.node,
3208 test_values.telemetry_sender,
3209 test_values.recovery_sender,
3210 test_values.power_manager.clone(),
3211 );
3212 assert_eq!(phy_manager.get_phy_ids(), Vec::<u16>::new());
3213 }
3214
3215 #[fuchsia::test]
3218 fn get_phy_ids_single_phy() {
3219 let mut exec = TestExecutor::new();
3220 let mut test_values = test_setup();
3221 let mut phy_manager = PhyManager::new(
3222 test_values.monitor_proxy,
3223 recovery::lookup_recovery_profile(""),
3224 false,
3225 test_values.node,
3226 test_values.telemetry_sender,
3227 test_values.recovery_sender,
3228 test_values.power_manager.clone(),
3229 );
3230
3231 {
3232 let add_phy_fut = phy_manager.add_phy(1);
3233 let mut add_phy_fut = pin!(add_phy_fut);
3234 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3235 send_get_power_element_dependency_token_response(
3236 &mut exec,
3237 &mut test_values.monitor_stream,
3238 Ok(zx::Event::create()),
3239 );
3240 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3241 send_get_supported_mac_roles_response(
3242 &mut exec,
3243 &mut test_values.monitor_stream,
3244 Ok(&[]),
3245 );
3246 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
3247 }
3248
3249 assert_eq!(phy_manager.get_phy_ids(), vec![1]);
3250 }
3251
3252 #[fuchsia::test]
3255 fn get_phy_ids_two_phys() {
3256 let mut exec = TestExecutor::new();
3257 let mut test_values = test_setup();
3258 let mut phy_manager = PhyManager::new(
3259 test_values.monitor_proxy,
3260 recovery::lookup_recovery_profile(""),
3261 false,
3262 test_values.node,
3263 test_values.telemetry_sender,
3264 test_values.recovery_sender,
3265 test_values.power_manager.clone(),
3266 );
3267
3268 {
3269 let add_phy_fut = phy_manager.add_phy(1);
3270 let mut add_phy_fut = pin!(add_phy_fut);
3271 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3272 send_get_power_element_dependency_token_response(
3273 &mut exec,
3274 &mut test_values.monitor_stream,
3275 Ok(zx::Event::create()),
3276 );
3277 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3278 send_get_supported_mac_roles_response(
3279 &mut exec,
3280 &mut test_values.monitor_stream,
3281 Ok(&[]),
3282 );
3283 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
3284 }
3285
3286 {
3287 let add_phy_fut = phy_manager.add_phy(2);
3288 let mut add_phy_fut = pin!(add_phy_fut);
3289 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3290 send_get_power_element_dependency_token_response(
3291 &mut exec,
3292 &mut test_values.monitor_stream,
3293 Ok(zx::Event::create()),
3294 );
3295 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
3296 send_get_supported_mac_roles_response(
3297 &mut exec,
3298 &mut test_values.monitor_stream,
3299 Ok(&[]),
3300 );
3301 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
3302 }
3303
3304 let phy_ids = phy_manager.get_phy_ids();
3305 assert!(phy_ids.contains(&1), "expected phy_ids to contain `1`, but phy_ids={phy_ids:?}");
3306 assert!(phy_ids.contains(&2), "expected phy_ids to contain `2`, but phy_ids={phy_ids:?}");
3307 }
3308
3309 #[fuchsia::test]
3311 async fn log_phy_add_failure() {
3312 let test_values = test_setup();
3313 let mut phy_manager = PhyManager::new(
3314 test_values.monitor_proxy,
3315 recovery::lookup_recovery_profile(""),
3316 false,
3317 test_values.node,
3318 test_values.telemetry_sender,
3319 test_values.recovery_sender,
3320 test_values.power_manager.clone(),
3321 );
3322
3323 assert_data_tree!(test_values.inspector, root: {
3324 phy_manager: {
3325 phy_add_fail_count: 0u64,
3326 },
3327 });
3328
3329 phy_manager.log_phy_add_failure();
3330 assert_data_tree!(test_values.inspector, root: {
3331 phy_manager: {
3332 phy_add_fail_count: 1u64,
3333 },
3334 });
3335 }
3336
3337 #[fuchsia::test]
3340 fn test_set_country_code() {
3341 let mut exec = TestExecutor::new();
3342 let mut test_values = test_setup();
3343 let mut phy_manager = PhyManager::new(
3344 test_values.monitor_proxy,
3345 recovery::lookup_recovery_profile(""),
3346 false,
3347 test_values.node,
3348 test_values.telemetry_sender,
3349 test_values.recovery_sender,
3350 test_values.power_manager.clone(),
3351 );
3352
3353 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3355 let _ = phy_manager.phys.insert(1, PhyContainer::new(vec![]));
3356
3357 assert!(phy_manager.saved_country_code.is_none());
3359
3360 {
3362 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3363 let mut set_country_fut = pin!(set_country_fut);
3364
3365 for _ in 0..2 {
3367 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3368 assert_matches!(
3369 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3370 Poll::Ready(Some(Ok(
3371 fidl_service::DeviceMonitorRequest::SetCountry {
3372 req: fidl_service::SetCountryRequest {
3373 phy_id: _,
3374 alpha2: [b'U', b'S'],
3375 },
3376 responder,
3377 }
3378 ))) => {
3379 responder.send(ZX_OK).expect("sending fake set country response");
3380 }
3381 );
3382 }
3383
3384 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3385 }
3386 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3387
3388 {
3391 let set_country_fut = phy_manager.set_country_code(None);
3392 let mut set_country_fut = pin!(set_country_fut);
3393
3394 for _ in 0..2 {
3396 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3397 assert_matches!(
3398 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3399 Poll::Ready(Some(Ok(
3400 fidl_service::DeviceMonitorRequest::ClearCountry {
3401 req: fidl_service::ClearCountryRequest {
3402 phy_id: _,
3403 },
3404 responder,
3405 }
3406 ))) => {
3407 responder.send(ZX_OK).expect("sending fake clear country response");
3408 }
3409 );
3410 }
3411
3412 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3413 }
3414 assert_eq!(phy_manager.saved_country_code, None);
3415 }
3416
3417 #[fuchsia::test]
3419 fn test_setting_country_code_fails() {
3420 let mut exec = TestExecutor::new();
3421 let mut test_values = test_setup();
3422 let mut phy_manager = PhyManager::new(
3423 test_values.monitor_proxy,
3424 recovery::lookup_recovery_profile(""),
3425 false,
3426 test_values.node,
3427 test_values.telemetry_sender,
3428 test_values.recovery_sender,
3429 test_values.power_manager.clone(),
3430 );
3431
3432 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3434
3435 assert!(phy_manager.saved_country_code.is_none());
3437
3438 {
3440 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3441 let mut set_country_fut = pin!(set_country_fut);
3442
3443 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3444 assert_matches!(
3445 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3446 Poll::Ready(Some(Ok(
3447 fidl_service::DeviceMonitorRequest::SetCountry {
3448 req: fidl_service::SetCountryRequest {
3449 phy_id: 0,
3450 alpha2: [b'U', b'S'],
3451 },
3452 responder,
3453 }
3454 ))) => {
3455 responder
3457 .send(zx::sys::ZX_ERR_NOT_SUPPORTED)
3458 .expect("sending fake set country response");
3459 }
3460 );
3461
3462 assert_matches!(
3463 exec.run_until_stalled(&mut set_country_fut),
3464 Poll::Ready(Err(PhyManagerError::PhySetCountryFailure))
3465 );
3466 }
3467 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3468 }
3469
3470 #[fuchsia::test]
3472 fn test_recover_client_interfaces_succeeds() {
3473 let mut exec = TestExecutor::new();
3474 let mut test_values = test_setup();
3475 let mut phy_manager = PhyManager::new(
3476 test_values.monitor_proxy,
3477 recovery::lookup_recovery_profile(""),
3478 false,
3479 test_values.node,
3480 test_values.telemetry_sender,
3481 test_values.recovery_sender,
3482 test_values.power_manager.clone(),
3483 );
3484 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3485
3486 phy_manager.client_connections_enabled = true;
3488
3489 for phy_id in 0..4 {
3492 let fake_mac_roles = fake_mac_roles.clone();
3493 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3494
3495 if phy_id.is_multiple_of(2) {
3497 let phy_container = phy_manager.phys.get_mut(&phy_id).expect("missing PHY");
3498 let _ = phy_container.client_ifaces.insert(phy_id);
3499 }
3500 }
3501
3502 {
3506 let recovery_fut =
3507 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3508 let mut recovery_fut = pin!(recovery_fut);
3509 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3510
3511 loop {
3512 match exec.run_until_stalled(&mut recovery_fut) {
3516 Poll::Pending => {}
3517 Poll::Ready(iface_ids) => {
3518 if iface_ids.values().any(Result::is_err) {
3519 panic!("recovery failed unexpectedly");
3520 }
3521 let iface_ids: Vec<_> =
3522 iface_ids.into_values().flat_map(Result::unwrap).collect();
3523 assert!(iface_ids.contains(&1));
3524 assert!(iface_ids.contains(&3));
3525 break;
3526 }
3527 }
3528
3529 assert_matches!(
3532 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3533 Poll::Ready(Some(Ok(
3534 fidl_service::DeviceMonitorRequest::CreateIface {
3535 payload,
3536 responder,
3537 }
3538 ))) => {
3539 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3540 iface_id: Some(payload.phy_id.unwrap()),
3541 ..Default::default()
3542 };
3543 responder.send(Ok(&response)).expect("sending fake iface id");
3544 }
3545 );
3546 }
3547 }
3548
3549 for phy_id in phy_manager.phys.keys() {
3552 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3553 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3554 }
3555 }
3556
3557 #[fuchsia::test]
3559 fn test_recover_client_interfaces_fails() {
3560 let mut exec = TestExecutor::new();
3561 let mut test_values = test_setup();
3562 let mut phy_manager = PhyManager::new(
3563 test_values.monitor_proxy,
3564 recovery::lookup_recovery_profile(""),
3565 false,
3566 test_values.node,
3567 test_values.telemetry_sender,
3568 test_values.recovery_sender,
3569 test_values.power_manager.clone(),
3570 );
3571 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3572
3573 phy_manager.client_connections_enabled = true;
3575
3576 for phy_id in 0..3 {
3581 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3582 }
3583
3584 {
3586 let recovery_fut =
3587 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3588 let mut recovery_fut = pin!(recovery_fut);
3589 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3590
3591 loop {
3592 match exec.run_until_stalled(&mut recovery_fut) {
3593 Poll::Pending => {}
3594 Poll::Ready(iface_ids) => {
3595 assert!(iface_ids.values().any(Result::is_err));
3596 let iface_ids: Vec<_> =
3597 iface_ids.into_values().filter_map(Result::ok).flatten().collect();
3598 assert_eq!(iface_ids, vec![1]);
3599 break;
3600 }
3601 }
3602
3603 assert_matches!(
3606 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3607 Poll::Ready(Some(Ok(
3608 fidl_service::DeviceMonitorRequest::CreateIface {
3609 payload,
3610 responder,
3611 }
3612 ))) => {
3613 let iface_id = payload.phy_id.unwrap();
3614 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3615 iface_id: Some(iface_id),
3616 ..Default::default()
3617 };
3618
3619 match payload.phy_id.unwrap() {
3622 1 => responder.send(Ok(&response)).expect("sending fake iface id"),
3623 _ => responder
3624 .send(Err(fidl_service::DeviceMonitorError::unknown()))
3625 .expect("sending fake iface id"),
3626 };
3627 }
3628 );
3629 }
3630 }
3631
3632 for phy_id in phy_manager.phys.keys() {
3634 match phy_id {
3635 1 => {
3636 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3637 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3638 }
3639 _ => assert!(phy_manager.phys[phy_id].client_ifaces.is_empty()),
3640 }
3641 }
3642 }
3643
3644 #[fuchsia::test]
3647 fn test_recover_client_interfaces_while_disabled() {
3648 let mut exec = TestExecutor::new();
3649 let test_values = test_setup();
3650 let mut phy_manager = PhyManager::new(
3651 test_values.monitor_proxy,
3652 recovery::lookup_recovery_profile(""),
3653 false,
3654 test_values.node,
3655 test_values.telemetry_sender,
3656 test_values.recovery_sender,
3657 test_values.power_manager.clone(),
3658 );
3659
3660 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3663 let _ = phy_manager.phys.insert(0, PhyContainer::new(fake_mac_roles));
3664
3665 {
3668 let recovery_fut =
3669 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3670 let mut recovery_fut = pin!(recovery_fut);
3671 assert_matches!(
3672 exec.run_until_stalled(&mut recovery_fut),
3673 Poll::Ready(recovered_ifaces) => {
3674 assert!(recovered_ifaces.is_empty());
3675 }
3676 );
3677 }
3678
3679 for (_, phy_container) in phy_manager.phys {
3681 assert!(phy_container.client_ifaces.is_empty());
3682 }
3683 }
3684
3685 #[fuchsia::test]
3688 fn test_start_after_unsuccessful_stop() {
3689 let mut exec = TestExecutor::new();
3690 let test_values = test_setup();
3691 let mut phy_manager = PhyManager::new(
3692 test_values.monitor_proxy,
3693 recovery::lookup_recovery_profile(""),
3694 false,
3695 test_values.node,
3696 test_values.telemetry_sender,
3697 test_values.recovery_sender,
3698 test_values.power_manager.clone(),
3699 );
3700
3701 assert!(!phy_manager.client_connections_enabled);
3703
3704 let fake_phy_id = 1;
3706 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3707 let mut phy_container = PhyContainer::new(fake_mac_roles);
3708 let fake_iface_id = 1;
3710 let _ = phy_container.client_ifaces.insert(fake_iface_id);
3711 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3712
3713 {
3716 let start_client_future =
3717 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3718 let mut start_client_future = pin!(start_client_future);
3719 assert_matches!(
3720 exec.run_until_stalled(&mut start_client_future),
3721 Poll::Ready(v) => {
3722 assert!(v.is_empty())
3723 });
3724 }
3725
3726 {
3729 let start_client_future = phy_manager
3730 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3731 let mut start_client_future = pin!(start_client_future);
3732 assert_matches!(
3733 exec.run_until_stalled(&mut start_client_future),
3734 Poll::Ready(iface_ids) => {
3735 assert_eq!(iface_ids.into_values().collect::<Vec<_>>(), vec![Ok(vec![1])]);
3736 }
3737 );
3738 }
3739 }
3740
3741 #[fuchsia::test]
3743 fn test_client_connections_enabled_when_enabled() {
3744 let _exec = TestExecutor::new();
3745 let test_values = test_setup();
3746 let mut phy_manager = PhyManager::new(
3747 test_values.monitor_proxy,
3748 recovery::lookup_recovery_profile(""),
3749 false,
3750 test_values.node,
3751 test_values.telemetry_sender,
3752 test_values.recovery_sender,
3753 test_values.power_manager.clone(),
3754 );
3755
3756 phy_manager.client_connections_enabled = true;
3757 assert!(phy_manager.client_connections_enabled());
3758 }
3759
3760 #[fuchsia::test]
3762 fn test_client_connections_enabled_when_disabled() {
3763 let _exec = TestExecutor::new();
3764 let test_values = test_setup();
3765 let mut phy_manager = PhyManager::new(
3766 test_values.monitor_proxy,
3767 recovery::lookup_recovery_profile(""),
3768 false,
3769 test_values.node,
3770 test_values.telemetry_sender,
3771 test_values.recovery_sender,
3772 test_values.power_manager.clone(),
3773 );
3774
3775 phy_manager.client_connections_enabled = false;
3776 assert!(!phy_manager.client_connections_enabled());
3777 }
3778
3779 #[fuchsia::test]
3780 fn test_create_iface_succeeds() {
3781 let mut exec = TestExecutor::new();
3782 let mut test_values = test_setup();
3783 let mut phy_manager = PhyManager::new(
3784 test_values.monitor_proxy,
3785 recovery::lookup_recovery_profile(""),
3786 false,
3787 test_values.node,
3788 test_values.telemetry_sender,
3789 test_values.recovery_sender,
3790 test_values.power_manager.clone(),
3791 );
3792
3793 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3795 let mut fut = pin!(fut);
3796
3797 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3799
3800 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, Some(0));
3802
3803 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(0)));
3805
3806 assert_matches!(
3808 test_values.telemetry_receiver.try_recv(),
3809 Ok(TelemetryEvent::IfaceCreationResult {
3810 role: fidl_common::WlanMacRole::Client,
3811 result: Ok(0),
3812 })
3813 )
3814 }
3815
3816 #[fuchsia::test]
3817 fn test_create_iface_fails() {
3818 let mut exec = TestExecutor::new();
3819 let mut test_values = test_setup();
3820 let mut phy_manager = PhyManager::new(
3821 test_values.monitor_proxy,
3822 recovery::lookup_recovery_profile(""),
3823 false,
3824 test_values.node,
3825 test_values.telemetry_sender,
3826 test_values.recovery_sender,
3827 test_values.power_manager.clone(),
3828 );
3829 let mut phy_container = PhyContainer::new(vec![]);
3830 let _ = phy_container.client_ifaces.insert(0);
3831 let _ = phy_manager.phys.insert(0, phy_container);
3832
3833 {
3834 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3836 let mut fut = pin!(fut);
3837
3838 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3840
3841 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
3843
3844 assert_matches!(
3846 exec.run_until_stalled(&mut fut),
3847 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3848 );
3849
3850 assert_matches!(
3852 test_values.telemetry_receiver.try_recv(),
3853 Ok(TelemetryEvent::IfaceCreationResult {
3854 role: fidl_common::WlanMacRole::Client,
3855 result: Err(()),
3856 })
3857 );
3858 }
3859
3860 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3862 assert_eq!(
3863 phy_manager.phys[&0].defects.events[0].value,
3864 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3865 );
3866 }
3867
3868 #[fuchsia::test]
3869 fn test_create_iface_request_fails() {
3870 let mut exec = TestExecutor::new();
3871 let mut test_values = test_setup();
3872 let mut phy_manager = PhyManager::new(
3873 test_values.monitor_proxy,
3874 recovery::lookup_recovery_profile(""),
3875 false,
3876 test_values.node,
3877 test_values.telemetry_sender,
3878 test_values.recovery_sender,
3879 test_values.power_manager.clone(),
3880 );
3881 let mut phy_container = PhyContainer::new(vec![]);
3882 let _ = phy_container.client_ifaces.insert(0);
3883 let _ = phy_manager.phys.insert(0, phy_container);
3884
3885 drop(test_values.monitor_stream);
3886
3887 {
3888 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3890 let mut fut = pin!(fut);
3891
3892 assert_matches!(
3894 exec.run_until_stalled(&mut fut),
3895 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3896 );
3897
3898 assert_matches!(
3900 test_values.telemetry_receiver.try_recv(),
3901 Ok(TelemetryEvent::IfaceCreationResult {
3902 role: fidl_common::WlanMacRole::Client,
3903 result: Err(()),
3904 })
3905 );
3906 }
3907
3908 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3910 assert_eq!(
3911 phy_manager.phys[&0].defects.events[0].value,
3912 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3913 );
3914 }
3915
3916 #[fuchsia::test]
3917 fn test_destroy_iface_succeeds() {
3918 let mut exec = TestExecutor::new();
3919 let mut test_values = test_setup();
3920
3921 let fut = destroy_iface(
3923 &test_values.monitor_proxy,
3924 0,
3925 fidl_common::WlanMacRole::Client,
3926 &test_values.telemetry_sender,
3927 );
3928 let mut fut = pin!(fut);
3929
3930 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3932
3933 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
3935
3936 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3938
3939 assert_matches!(
3941 test_values.telemetry_receiver.try_recv(),
3942 Ok(TelemetryEvent::IfaceDestructionResult {
3943 role: fidl_common::WlanMacRole::Client,
3944 result: Ok(0),
3945 })
3946 )
3947 }
3948
3949 #[fuchsia::test]
3950 fn test_destroy_iface_not_found() {
3951 let mut exec = TestExecutor::new();
3952 let mut test_values = test_setup();
3953
3954 let fut = destroy_iface(
3956 &test_values.monitor_proxy,
3957 0,
3958 fidl_common::WlanMacRole::Client,
3959 &test_values.telemetry_sender,
3960 );
3961 let mut fut = pin!(fut);
3962
3963 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3965
3966 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_ERR_NOT_FOUND);
3968
3969 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3971
3972 assert_matches!(test_values.telemetry_receiver.try_recv(), Err(_))
3974 }
3975
3976 #[fuchsia::test]
3977 fn test_destroy_iface_fails() {
3978 let mut exec = TestExecutor::new();
3979 let mut test_values = test_setup();
3980
3981 let fut = destroy_iface(
3983 &test_values.monitor_proxy,
3984 0,
3985 fidl_common::WlanMacRole::Client,
3986 &test_values.telemetry_sender,
3987 );
3988 let mut fut = pin!(fut);
3989
3990 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3992
3993 send_destroy_iface_response(
3995 &mut exec,
3996 &mut test_values.monitor_stream,
3997 zx::sys::ZX_ERR_NO_RESOURCES,
3998 );
3999
4000 assert_matches!(
4002 exec.run_until_stalled(&mut fut),
4003 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
4004 );
4005
4006 assert_matches!(
4008 test_values.telemetry_receiver.try_recv(),
4009 Ok(TelemetryEvent::IfaceDestructionResult {
4010 role: fidl_common::WlanMacRole::Client,
4011 result: Err(()),
4012 })
4013 )
4014 }
4015
4016 #[fuchsia::test]
4017 fn test_destroy_iface_request_fails() {
4018 let mut exec = TestExecutor::new();
4019 let mut test_values = test_setup();
4020
4021 drop(test_values.monitor_stream);
4022
4023 let fut = destroy_iface(
4025 &test_values.monitor_proxy,
4026 0,
4027 fidl_common::WlanMacRole::Client,
4028 &test_values.telemetry_sender,
4029 );
4030 let mut fut = pin!(fut);
4031
4032 assert_matches!(
4034 exec.run_until_stalled(&mut fut),
4035 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
4036 );
4037
4038 assert_matches!(
4040 test_values.telemetry_receiver.try_recv(),
4041 Ok(TelemetryEvent::IfaceDestructionResult {
4042 role: fidl_common::WlanMacRole::Client,
4043 result: Err(()),
4044 })
4045 )
4046 }
4047
4048 #[fuchsia::test]
4050 fn test_record_iface_event() {
4051 let _exec = TestExecutor::new();
4052 let test_values = test_setup();
4053
4054 let mut phy_manager = PhyManager::new(
4055 test_values.monitor_proxy,
4056 recovery::lookup_recovery_profile(""),
4057 false,
4058 test_values.node,
4059 test_values.telemetry_sender,
4060 test_values.recovery_sender,
4061 test_values.power_manager.clone(),
4062 );
4063
4064 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
4066 let _ = phy_manager.phys.insert(1, PhyContainer::new(vec![]));
4067 let _ = phy_manager.phys.insert(2, PhyContainer::new(vec![]));
4068 let _ = phy_manager.phys.insert(3, PhyContainer::new(vec![]));
4069
4070 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
4072 let _ = phy_manager.phys.get_mut(&1).expect("missing PHY").client_ifaces.insert(456);
4073 let _ = phy_manager.phys.get_mut(&2).expect("missing PHY").client_ifaces.insert(789);
4074 let _ = phy_manager.phys.get_mut(&3).expect("missing PHY").ap_ifaces.insert(246);
4075
4076 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4078 phy_manager.phys.get_mut(&1).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4079 phy_manager.phys.get_mut(&2).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4080 phy_manager.phys.get_mut(&3).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4081
4082 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
4084 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }));
4085 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 }));
4086 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
4087
4088 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 }));
4090
4091 assert_eq!(phy_manager.phys[&0].defects.events.len(), 2);
4093 assert_eq!(
4094 phy_manager.phys[&0].defects.events[0].value,
4095 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 })
4096 );
4097 assert_eq!(
4098 phy_manager.phys[&0].defects.events[1].value,
4099 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 })
4100 );
4101 assert_eq!(phy_manager.phys[&1].defects.events.len(), 1);
4102 assert_eq!(
4103 phy_manager.phys[&1].defects.events[0].value,
4104 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 })
4105 );
4106 assert_eq!(phy_manager.phys[&2].defects.events.len(), 1);
4107 assert_eq!(
4108 phy_manager.phys[&2].defects.events[0].value,
4109 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 })
4110 );
4111 assert_eq!(phy_manager.phys[&3].defects.events.len(), 1);
4112 assert_eq!(
4113 phy_manager.phys[&3].defects.events[0].value,
4114 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 })
4115 );
4116 }
4117
4118 #[fuchsia::test]
4120 fn test_aps_do_not_record_client_defects() {
4121 let _exec = TestExecutor::new();
4122 let test_values = test_setup();
4123
4124 let mut phy_manager = PhyManager::new(
4125 test_values.monitor_proxy,
4126 recovery::lookup_recovery_profile(""),
4127 false,
4128 test_values.node,
4129 test_values.telemetry_sender,
4130 test_values.recovery_sender,
4131 test_values.power_manager.clone(),
4132 );
4133
4134 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
4136
4137 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").ap_ifaces.insert(123);
4139
4140 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4142
4143 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
4145 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }));
4146 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 123 }));
4147 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
4148
4149 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
4151 }
4152
4153 #[fuchsia::test]
4155 fn test_clients_do_not_record_ap_defects() {
4156 let _exec = TestExecutor::new();
4157 let test_values = test_setup();
4158
4159 let mut phy_manager = PhyManager::new(
4160 test_values.monitor_proxy,
4161 recovery::lookup_recovery_profile(""),
4162 false,
4163 test_values.node,
4164 test_values.telemetry_sender,
4165 test_values.recovery_sender,
4166 test_values.power_manager.clone(),
4167 );
4168
4169 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
4171
4172 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
4174
4175 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
4177
4178 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }));
4180
4181 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
4183 }
4184
4185 fn aggressive_test_recovery_profile(
4186 _phy_id: u16,
4187 _defect_history: &mut EventHistory<Defect>,
4188 _recovery_history: &mut EventHistory<RecoveryAction>,
4189 _latest_defect: Defect,
4190 ) -> Option<RecoveryAction> {
4191 Some(RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 }))
4192 }
4193
4194 #[test_case(
4195 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
4196 "recommend AP start recovery"
4197 )]
4198 #[test_case(
4199 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
4200 "recommend connection failure recovery"
4201 )]
4202 #[test_case(
4203 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
4204 "recommend empty scan recovery"
4205 )]
4206 #[test_case(
4207 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
4208 "recommend failed scan recovery"
4209 )]
4210 #[test_case(
4211 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
4212 "recommend canceled scan recovery"
4213 )]
4214 #[test_case(
4215 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
4216 "recommend iface destruction recovery"
4217 )]
4218 #[test_case(
4219 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
4220 "recommend iface creation recovery"
4221 )]
4222 #[fuchsia::test(add_test_attr = false)]
4223 fn test_recovery_action_sent_from_record_defect(defect: Defect) {
4224 let _exec = TestExecutor::new();
4225 let mut test_values = test_setup();
4226 let mut phy_manager = PhyManager::new(
4227 test_values.monitor_proxy,
4228 recovery::lookup_recovery_profile(""),
4229 false,
4230 test_values.node,
4231 test_values.telemetry_sender,
4232 test_values.recovery_sender,
4233 test_values.power_manager.clone(),
4234 );
4235
4236 let mut phy_container = PhyContainer::new(vec![]);
4238 let _ = phy_container.ap_ifaces.insert(123);
4239 let _ = phy_container.client_ifaces.insert(456);
4240 let _ = phy_manager.phys.insert(0, phy_container);
4241
4242 phy_manager.recovery_profile = aggressive_test_recovery_profile;
4244
4245 phy_manager.record_defect(defect);
4247
4248 let recovery_action = test_values.recovery_receiver.try_recv().unwrap();
4250 assert_eq!(recovery_action.defect, defect);
4251 assert_eq!(
4252 recovery_action.action,
4253 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4254 );
4255 }
4256
4257 #[test_case(
4258 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
4259 "do not recommend AP start recovery"
4260 )]
4261 #[test_case(
4262 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
4263 "do not recommend connection failure recovery"
4264 )]
4265 #[test_case(
4266 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
4267 "do not recommend empty scan recovery"
4268 )]
4269 #[test_case(
4270 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
4271 "do not recommend failed scan recovery"
4272 )]
4273 #[test_case(
4274 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
4275 "do not recommend canceled scan recovery"
4276 )]
4277 #[test_case(
4278 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
4279 "do not recommend iface destruction recovery"
4280 )]
4281 #[test_case(
4282 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
4283 "do not recommend iface creation recovery"
4284 )]
4285 #[fuchsia::test(add_test_attr = false)]
4286 fn test_no_recovery_when_defect_contains_bad_ids(defect: Defect) {
4287 let _exec = TestExecutor::new();
4288 let mut test_values = test_setup();
4289
4290 let mut phy_manager = PhyManager::new(
4292 test_values.monitor_proxy,
4293 recovery::lookup_recovery_profile(""),
4294 false,
4295 test_values.node,
4296 test_values.telemetry_sender,
4297 test_values.recovery_sender,
4298 test_values.power_manager.clone(),
4299 );
4300
4301 phy_manager.recovery_profile = aggressive_test_recovery_profile;
4303
4304 phy_manager.record_defect(defect);
4306
4307 assert!(test_values.recovery_receiver.try_recv().is_err());
4309 }
4310
4311 #[test_case(
4312 recovery::RecoverySummary {
4313 defect: Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 0 }),
4314 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4315 },
4316 telemetry::RecoveryReason::ScanResultsEmpty(
4317 telemetry::ClientRecoveryMechanism::PhyReset
4318 ) ;
4319 "PHY reset for empty scan results"
4320 )]
4321 #[test_case(
4322 recovery::RecoverySummary {
4323 defect: Defect::Iface(IfaceFailure::CanceledScan { iface_id: 0 }),
4324 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4325 },
4326 telemetry::RecoveryReason::ScanCancellation(
4327 telemetry::ClientRecoveryMechanism::PhyReset
4328 ) ;
4329 "PHY reset for scan cancellation"
4330 )]
4331 #[test_case(
4332 recovery::RecoverySummary {
4333 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 0 }),
4334 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4335 },
4336 telemetry::RecoveryReason::ScanFailure(
4337 telemetry::ClientRecoveryMechanism::PhyReset
4338 ) ;
4339 "PHY reset for scan failure"
4340 )]
4341 #[test_case(
4342 recovery::RecoverySummary {
4343 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 0 }),
4344 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4345 },
4346 telemetry::RecoveryReason::StartApFailure(
4347 telemetry::ApRecoveryMechanism::ResetPhy
4348 ) ;
4349 "PHY reset for start AP failure"
4350 )]
4351 #[test_case(
4352 recovery::RecoverySummary {
4353 defect: Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 0 }),
4354 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4355 },
4356 telemetry::RecoveryReason::ConnectFailure(
4357 telemetry::ClientRecoveryMechanism::PhyReset
4358 ) ;
4359 "PHY reset for connection failure"
4360 )]
4361 #[test_case(
4362 recovery::RecoverySummary {
4363 defect: Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }),
4364 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4365 },
4366 telemetry::RecoveryReason::DestroyIfaceFailure(
4367 telemetry::PhyRecoveryMechanism::PhyReset
4368 ) ;
4369 "PHY reset for iface destruction failure"
4370 )]
4371 #[test_case(
4372 recovery::RecoverySummary {
4373 defect: Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }),
4374 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4375 },
4376 telemetry::RecoveryReason::CreateIfaceFailure(
4377 telemetry::PhyRecoveryMechanism::PhyReset
4378 ) ;
4379 "PHY reset for iface creation failure"
4380 )]
4381 #[fuchsia::test(add_test_attr = false)]
4382 fn test_log_recovery_action_sends_metrics(
4383 summary: recovery::RecoverySummary,
4384 expected_reason: telemetry::RecoveryReason,
4385 ) {
4386 let _exec = TestExecutor::new();
4387 let mut test_values = test_setup();
4388 let mut phy_manager = PhyManager::new(
4389 test_values.monitor_proxy,
4390 recovery::lookup_recovery_profile(""),
4391 false,
4392 test_values.node,
4393 test_values.telemetry_sender,
4394 test_values.recovery_sender,
4395 test_values.power_manager.clone(),
4396 );
4397
4398 phy_manager.log_recovery_action(summary);
4400 assert_matches!(
4401 test_values.telemetry_receiver.try_recv(),
4402 Ok(TelemetryEvent::RecoveryEvent { reason } ) => {
4403 assert_eq!(reason, expected_reason);
4404 })
4405 }
4406
4407 #[test_case(
4408 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 456 }) ;
4409 "recommend AP start recovery"
4410 )]
4411 #[test_case(
4412 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
4413 "recommend connection failure recovery"
4414 )]
4415 #[test_case(
4416 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
4417 "recommend empty scan recovery"
4418 )]
4419 #[test_case(
4420 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
4421 "recommend failed scan recovery"
4422 )]
4423 #[test_case(
4424 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
4425 "recommend canceled scan recovery"
4426 )]
4427 #[fuchsia::test(add_test_attr = false)]
4428 fn log_defect_for_destroyed_iface(defect: Defect) {
4429 let _exec = TestExecutor::new();
4430 let test_values = test_setup();
4431
4432 let fake_phy_id = 123;
4433 let fake_iface_id = 456;
4434
4435 let mut phy_manager = PhyManager::new(
4438 test_values.monitor_proxy,
4439 recovery::lookup_recovery_profile(""),
4440 false,
4441 test_values.node,
4442 test_values.telemetry_sender,
4443 test_values.recovery_sender,
4444 test_values.power_manager.clone(),
4445 );
4446 let mut phy_container = PhyContainer::new(vec![]);
4447 let _ = phy_container.destroyed_ifaces.insert(fake_iface_id);
4448 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
4449
4450 phy_manager.record_defect(defect);
4452 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
4453 }
4454
4455 #[fuchsia::test]
4456 fn test_reset_request_fails() {
4457 let mut exec = TestExecutor::new();
4458 let test_values = test_setup();
4459
4460 drop(test_values.monitor_stream);
4462
4463 let fut = reset_phy(&test_values.monitor_proxy, 0);
4465 let mut fut = pin!(fut);
4466 assert_matches!(
4467 exec.run_until_stalled(&mut fut),
4468 Poll::Ready(Err(PhyManagerError::InternalError))
4469 );
4470 }
4471
4472 #[fuchsia::test]
4473 fn test_reset_fails() {
4474 let mut exec = TestExecutor::new();
4475 let mut test_values = test_setup();
4476
4477 let fut = reset_phy(&test_values.monitor_proxy, 0);
4479 let mut fut = pin!(fut);
4480 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4481
4482 assert_matches!(
4484 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4485 Poll::Ready(Some(Ok(
4486 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4487 ))) => {
4488 responder.send(Err(ZX_ERR_NOT_FOUND)).expect("sending fake reset response");
4489 }
4490 );
4491
4492 assert_matches!(
4494 exec.run_until_stalled(&mut fut),
4495 Poll::Ready(Err(PhyManagerError::PhyResetFailure))
4496 );
4497 }
4498
4499 #[fuchsia::test]
4500 fn test_reset_succeeds() {
4501 let mut exec = TestExecutor::new();
4502 let mut test_values = test_setup();
4503
4504 let fut = reset_phy(&test_values.monitor_proxy, 0);
4506 let mut fut = pin!(fut);
4507 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4508
4509 assert_matches!(
4511 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4512 Poll::Ready(Some(Ok(
4513 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4514 ))) => {
4515 responder.send(Ok(())).expect("sending fake reset response");
4516 }
4517 );
4518
4519 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4521 }
4522
4523 #[fuchsia::test]
4524 fn test_disconnect_request_fails() {
4525 let mut exec = TestExecutor::new();
4526 let mut test_values = test_setup();
4527
4528 let fut = disconnect(&test_values.monitor_proxy, 0);
4530 let mut fut = pin!(fut);
4531 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4532
4533 let sme_server = assert_matches!(
4535 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4536 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4537 iface_id: 0, sme_server, responder
4538 }))) => {
4539 assert!(responder.send(Ok(())).is_ok());
4541 sme_server
4542 }
4543 );
4544
4545 drop(sme_server);
4547
4548 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4550 }
4551
4552 #[fuchsia::test]
4553 fn test_disconnect_succeeds() {
4554 let mut exec = TestExecutor::new();
4555 let mut test_values = test_setup();
4556
4557 let fut = disconnect(&test_values.monitor_proxy, 0);
4559 let mut fut = pin!(fut);
4560 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4561
4562 let sme_server = assert_matches!(
4564 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4565 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4566 iface_id: 0, sme_server, responder
4567 }))) => {
4568 assert!(responder.send(Ok(())).is_ok());
4570 sme_server
4571 }
4572 );
4573
4574 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4576 let mut sme_stream = sme_server.into_stream().into_future();
4577 assert_matches!(
4578 poll_sme_req(&mut exec, &mut sme_stream),
4579 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
4580 responder,
4581 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
4582 }) => {
4583 responder.send().expect("Failed to send disconnect response")
4584 }
4585 );
4586
4587 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4589 }
4590
4591 #[fuchsia::test]
4592 fn test_disconnect_cannot_get_sme() {
4593 let mut exec = TestExecutor::new();
4594 let test_values = test_setup();
4595
4596 drop(test_values.monitor_stream);
4598
4599 let fut = disconnect(&test_values.monitor_proxy, 0);
4601 let mut fut = pin!(fut);
4602 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4603 }
4604
4605 #[fuchsia::test]
4606 fn test_stop_ap_request_fails() {
4607 let mut exec = TestExecutor::new();
4608 let mut test_values = test_setup();
4609
4610 let fut = stop_ap(&test_values.monitor_proxy, 0);
4612 let mut fut = pin!(fut);
4613 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4614
4615 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4617 let sme_server = assert_matches!(
4618 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4619 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4620 iface_id: 0, sme_server, responder
4621 }))) => {
4622 assert!(responder.send(Ok(())).is_ok());
4624 sme_server
4625 }
4626 );
4627
4628 drop(sme_server);
4630
4631 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4633 }
4634
4635 #[fuchsia::test]
4636 fn test_stop_ap_fails() {
4637 let mut exec = TestExecutor::new();
4638 let mut test_values = test_setup();
4639
4640 let fut = stop_ap(&test_values.monitor_proxy, 0);
4642 let mut fut = pin!(fut);
4643 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4644
4645 let sme_server = assert_matches!(
4647 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4648 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4649 iface_id: 0, sme_server, responder
4650 }))) => {
4651 assert!(responder.send(Ok(())).is_ok());
4653 sme_server
4654 }
4655 );
4656
4657 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4659 let mut sme_stream = sme_server.into_stream().into_future();
4660 assert_matches!(
4661 poll_ap_sme_req(&mut exec, &mut sme_stream),
4662 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4663 responder,
4664 }) => {
4665 responder.send(fidl_sme::StopApResultCode::InternalError).expect("Failed to send stop AP response")
4666 }
4667 );
4668
4669 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4671 }
4672
4673 #[fuchsia::test]
4674 fn test_stop_ap_succeeds() {
4675 let mut exec = TestExecutor::new();
4676 let mut test_values = test_setup();
4677
4678 let fut = stop_ap(&test_values.monitor_proxy, 0);
4680 let mut fut = pin!(fut);
4681 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4682
4683 let sme_server = assert_matches!(
4685 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4686 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4687 iface_id: 0, sme_server, responder
4688 }))) => {
4689 assert!(responder.send(Ok(())).is_ok());
4691 sme_server
4692 }
4693 );
4694
4695 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4697 let mut sme_stream = sme_server.into_stream().into_future();
4698 assert_matches!(
4699 poll_ap_sme_req(&mut exec, &mut sme_stream),
4700 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4701 responder,
4702 }) => {
4703 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
4704 }
4705 );
4706
4707 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4709 }
4710
4711 #[fuchsia::test]
4712 fn test_stop_ap_cannot_get_sme() {
4713 let mut exec = TestExecutor::new();
4714 let test_values = test_setup();
4715
4716 drop(test_values.monitor_stream);
4718
4719 let fut = stop_ap(&test_values.monitor_proxy, 0);
4721 let mut fut = pin!(fut);
4722 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4723 }
4724
4725 fn phy_manager_for_recovery_test(
4726 device_monitor: fidl_service::DeviceMonitorProxy,
4727 node: inspect::Node,
4728 telemetry_sender: TelemetrySender,
4729 recovery_action_sender: recovery::RecoveryActionSender,
4730 power_manager: Arc<TestPowerManager>,
4731 ) -> PhyManager {
4732 let mut phy_manager = PhyManager::new(
4733 device_monitor,
4734 recovery::lookup_recovery_profile("thresholded_recovery"),
4735 true,
4736 node,
4737 telemetry_sender,
4738 recovery_action_sender,
4739 power_manager,
4740 );
4741
4742 let mut phy_container =
4744 PhyContainer::new(vec![fidl_common::WlanMacRole::Client, fidl_common::WlanMacRole::Ap]);
4745 assert!(phy_container.client_ifaces.insert(1,));
4746 assert!(phy_container.ap_ifaces.insert(2));
4747 assert!(phy_manager.phys.insert(0, phy_container).is_none());
4748
4749 phy_manager
4750 }
4751
4752 #[fuchsia::test]
4753 fn test_perform_recovery_destroy_nonexistent_iface() {
4754 let mut exec = TestExecutor::new();
4755 let mut test_values = test_setup();
4756 let mut phy_manager = phy_manager_for_recovery_test(
4757 test_values.monitor_proxy,
4758 test_values.node,
4759 test_values.telemetry_sender,
4760 test_values.recovery_sender,
4761 test_values.power_manager.clone(),
4762 );
4763
4764 let summary = recovery::RecoverySummary {
4766 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }),
4767 action: recovery::RecoveryAction::PhyRecovery(
4768 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 123 },
4769 ),
4770 };
4771
4772 {
4774 let fut = phy_manager.perform_recovery(summary);
4775 let mut fut = pin!(fut);
4776 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4777 }
4778
4779 assert_matches!(
4781 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4782 Poll::Pending
4783 );
4784 }
4785
4786 #[fuchsia::test]
4787 fn test_perform_recovery_destroy_client_iface_fails() {
4788 let mut exec = TestExecutor::new();
4789 let test_values = test_setup();
4790 let mut phy_manager = phy_manager_for_recovery_test(
4791 test_values.monitor_proxy,
4792 test_values.node,
4793 test_values.telemetry_sender,
4794 test_values.recovery_sender,
4795 test_values.power_manager.clone(),
4796 );
4797
4798 drop(test_values.monitor_stream);
4800
4801 let summary = recovery::RecoverySummary {
4803 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4804 action: recovery::RecoveryAction::PhyRecovery(
4805 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4806 ),
4807 };
4808
4809 {
4810 let fut = phy_manager.perform_recovery(summary);
4811 let mut fut = pin!(fut);
4812 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4813 }
4814
4815 assert!(phy_manager.phys[&0].client_ifaces.contains(&1));
4817 }
4818
4819 #[fuchsia::test]
4820 fn test_perform_recovery_destroy_client_iface_succeeds() {
4821 let mut exec = TestExecutor::new();
4822 let mut test_values = test_setup();
4823 let mut phy_manager = phy_manager_for_recovery_test(
4824 test_values.monitor_proxy,
4825 test_values.node,
4826 test_values.telemetry_sender,
4827 test_values.recovery_sender,
4828 test_values.power_manager.clone(),
4829 );
4830
4831 let summary = recovery::RecoverySummary {
4833 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4834 action: recovery::RecoveryAction::PhyRecovery(
4835 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4836 ),
4837 };
4838
4839 {
4840 let fut = phy_manager.perform_recovery(summary);
4841 let mut fut = pin!(fut);
4842 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4843
4844 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4846
4847 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4849 }
4850
4851 assert!(!phy_manager.phys[&0].client_ifaces.contains(&1));
4853
4854 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&1));
4856 }
4857
4858 #[fuchsia::test]
4859 fn test_perform_recovery_destroy_ap_iface_fails() {
4860 let mut exec = TestExecutor::new();
4861 let test_values = test_setup();
4862 let mut phy_manager = phy_manager_for_recovery_test(
4863 test_values.monitor_proxy,
4864 test_values.node,
4865 test_values.telemetry_sender,
4866 test_values.recovery_sender,
4867 test_values.power_manager.clone(),
4868 );
4869
4870 drop(test_values.monitor_stream);
4872
4873 let summary = recovery::RecoverySummary {
4875 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4876 action: recovery::RecoveryAction::PhyRecovery(
4877 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4878 ),
4879 };
4880
4881 {
4882 let fut = phy_manager.perform_recovery(summary);
4883 let mut fut = pin!(fut);
4884 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4885 }
4886
4887 assert!(phy_manager.phys[&0].ap_ifaces.contains(&2));
4889 }
4890
4891 #[fuchsia::test]
4892 fn test_perform_recovery_destroy_ap_iface_succeeds() {
4893 let mut exec = TestExecutor::new();
4894 let mut test_values = test_setup();
4895 let mut phy_manager = phy_manager_for_recovery_test(
4896 test_values.monitor_proxy,
4897 test_values.node,
4898 test_values.telemetry_sender,
4899 test_values.recovery_sender,
4900 test_values.power_manager.clone(),
4901 );
4902
4903 let summary = recovery::RecoverySummary {
4905 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4906 action: recovery::RecoveryAction::PhyRecovery(
4907 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4908 ),
4909 };
4910
4911 {
4912 let fut = phy_manager.perform_recovery(summary);
4913 let mut fut = pin!(fut);
4914 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4915
4916 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4918
4919 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4921 }
4922
4923 assert!(!phy_manager.phys[&0].ap_ifaces.contains(&2));
4925
4926 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&2));
4928 }
4929
4930 #[ignore]
4932 #[test_case(Some("US".parse().unwrap()); "Cached country code")]
4933 #[test_case(None; "No cached country code")]
4934 #[fuchsia::test(add_test_attr = false)]
4935 fn test_perform_recovery_reset_requests_phy_reset(
4936 cached_country_code: Option<client_types::CountryCode>,
4937 ) {
4938 let mut exec = TestExecutor::new();
4939 let mut test_values = test_setup();
4940 let mut phy_manager = phy_manager_for_recovery_test(
4941 test_values.monitor_proxy,
4942 test_values.node,
4943 test_values.telemetry_sender,
4944 test_values.recovery_sender,
4945 test_values.power_manager.clone(),
4946 );
4947
4948 phy_manager.saved_country_code = cached_country_code;
4950
4951 let summary = recovery::RecoverySummary {
4953 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4954 action: recovery::RecoveryAction::PhyRecovery(
4955 recovery::PhyRecoveryOperation::ResetPhy { phy_id: 0 },
4956 ),
4957 };
4958
4959 let fut = phy_manager.perform_recovery(summary);
4960 let mut fut = pin!(fut);
4961 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4962
4963 assert_matches!(
4965 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4966 Poll::Ready(Some(Ok(
4967 fidl_service::DeviceMonitorRequest::Reset {
4968 phy_id: 0,
4969 responder,
4970 }
4971 ))) => {
4972 responder
4973 .send(Ok(()))
4974 .expect("failed to send reset response.");
4975 }
4976 );
4977
4978 if let Some(cached_cc) = cached_country_code {
4980 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4981 assert_matches!(
4982 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4983 Poll::Ready(Some(Ok(
4984 fidl_service::DeviceMonitorRequest::SetCountry {
4985 req: fidl_service::SetCountryRequest {
4986 phy_id: 0,
4987 alpha2: cc_in_req,
4988 },
4989 responder,
4990 }
4991 ))) => {
4992 assert_eq!(cc_in_req, <[u8; 2]>::from(cached_cc));
4993 responder
4994 .send(zx::sys::ZX_OK)
4995 .expect("failed to send setCountry response.");
4996 }
4997 );
4998 }
4999
5000 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
5002 }
5003
5004 #[fuchsia::test]
5005 fn test_perform_recovery_disconnect_issues_request() {
5006 let mut exec = TestExecutor::new();
5007 let mut test_values = test_setup();
5008 let mut phy_manager = phy_manager_for_recovery_test(
5009 test_values.monitor_proxy,
5010 test_values.node,
5011 test_values.telemetry_sender,
5012 test_values.recovery_sender,
5013 test_values.power_manager.clone(),
5014 );
5015
5016 let summary = recovery::RecoverySummary {
5018 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
5019 action: recovery::RecoveryAction::IfaceRecovery(
5020 recovery::IfaceRecoveryOperation::Disconnect { iface_id: 1 },
5021 ),
5022 };
5023
5024 let fut = phy_manager.perform_recovery(summary);
5025 let mut fut = pin!(fut);
5026 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
5027
5028 let sme_server = assert_matches!(
5031 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
5032 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
5033 iface_id: 1, sme_server, responder
5034 }))) => {
5035 assert!(responder.send(Ok(())).is_ok());
5037 sme_server
5038 }
5039 );
5040
5041 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
5043 let mut sme_stream = sme_server.into_stream().into_future();
5044 assert_matches!(
5045 poll_sme_req(&mut exec, &mut sme_stream),
5046 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
5047 responder,
5048 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
5049 }) => {
5050 responder.send().expect("Failed to send disconnect response")
5051 }
5052 );
5053
5054 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
5056 }
5057
5058 #[fuchsia::test]
5059 fn test_perform_recovery_stop_ap_issues_request() {
5060 let mut exec = TestExecutor::new();
5061 let mut test_values = test_setup();
5062 let mut phy_manager = phy_manager_for_recovery_test(
5063 test_values.monitor_proxy,
5064 test_values.node,
5065 test_values.telemetry_sender,
5066 test_values.recovery_sender,
5067 test_values.power_manager.clone(),
5068 );
5069
5070 let summary = recovery::RecoverySummary {
5072 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
5073 action: recovery::RecoveryAction::IfaceRecovery(
5074 recovery::IfaceRecoveryOperation::StopAp { iface_id: 2 },
5075 ),
5076 };
5077
5078 let fut = phy_manager.perform_recovery(summary);
5079 let mut fut = pin!(fut);
5080 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
5081
5082 let sme_server = assert_matches!(
5085 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
5086 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
5087 iface_id: 2, sme_server, responder
5088 }))) => {
5089 assert!(responder.send(Ok(())).is_ok());
5091 sme_server
5092 }
5093 );
5094
5095 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
5097 let mut sme_stream = sme_server.into_stream().into_future();
5098 assert_matches!(
5099 poll_ap_sme_req(&mut exec, &mut sme_stream),
5100 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
5101 responder,
5102 }) => {
5103 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
5104 }
5105 );
5106
5107 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
5109 }
5110
5111 #[fuchsia::test]
5112 fn test_log_timeout_defect() {
5113 let _exec = TestExecutor::new();
5114 let mut test_values = test_setup();
5115 let mut phy_manager = phy_manager_for_recovery_test(
5116 test_values.monitor_proxy,
5117 test_values.node,
5118 test_values.telemetry_sender,
5119 test_values.recovery_sender,
5120 test_values.power_manager.clone(),
5121 );
5122
5123 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
5125
5126 phy_manager.record_defect(Defect::Iface(IfaceFailure::Timeout {
5128 iface_id: 1,
5129 source: wlan_telemetry::TimeoutSource::Scan,
5130 }));
5131
5132 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
5134
5135 assert_matches!(
5137 test_values.telemetry_receiver.try_recv(),
5138 Ok(TelemetryEvent::SmeTimeout { source: wlan_telemetry::TimeoutSource::Scan })
5139 )
5140 }
5141
5142 #[fuchsia::test]
5143 fn test_power_leases_stop_client_connections() {
5144 let mut exec = TestExecutor::new();
5145 let mut test_values = test_setup();
5146 let mut phy_manager = PhyManager::new(
5147 test_values.monitor_proxy,
5148 recovery::lookup_recovery_profile(""),
5149 false,
5150 test_values.node,
5151 test_values.telemetry_sender,
5152 test_values.recovery_sender,
5153 test_values.power_manager.clone(),
5154 );
5155
5156 let phy_id = 0;
5157 let mut phy_container = PhyContainer::new(vec![fidl_common::WlanMacRole::Client]);
5158 let token = zx::Event::create();
5159 let (lease_token, _) = zx::EventPair::create();
5160 phy_container.power_dependency_token = Some(token);
5161 phy_container.power_dependency_lease = Some(lease_token);
5162 let _ = phy_container.client_ifaces.insert(10);
5163 let _ = phy_manager.phys.insert(phy_id, phy_container);
5164 phy_manager.client_connections_enabled = true;
5165
5166 {
5167 let destroy_fut = phy_manager.destroy_all_client_ifaces();
5168 let mut destroy_fut = pin!(destroy_fut);
5169 assert!(exec.run_until_stalled(&mut destroy_fut).is_pending());
5170
5171 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
5172
5173 assert_matches!(exec.run_until_stalled(&mut destroy_fut), Poll::Ready(Ok(())));
5174 }
5175
5176 let container = phy_manager.phys.get(&phy_id).unwrap();
5178 assert!(container.power_dependency_token.is_some());
5179 assert!(container.power_dependency_lease.is_some());
5180 }
5181
5182 #[fuchsia::test]
5183 fn test_power_leases_add_phy() {
5184 let mut exec = TestExecutor::new();
5185 let mut test_values = test_setup();
5186 let mut phy_manager = PhyManager::new(
5187 test_values.monitor_proxy,
5188 recovery::lookup_recovery_profile(""),
5189 false,
5190 test_values.node,
5191 test_values.telemetry_sender,
5192 test_values.recovery_sender,
5193 test_values.power_manager.clone(),
5194 );
5195
5196 let phy_id = 1;
5197 {
5198 let add_phy_fut = phy_manager.add_phy(phy_id);
5199 let mut add_phy_fut = pin!(add_phy_fut);
5200 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
5201
5202 let token = zx::Event::create();
5203 send_get_power_element_dependency_token_response(
5204 &mut exec,
5205 &mut test_values.monitor_stream,
5206 Ok(token),
5207 );
5208
5209 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
5210
5211 send_get_supported_mac_roles_response(
5212 &mut exec,
5213 &mut test_values.monitor_stream,
5214 Ok(&[fidl_common::WlanMacRole::Client]),
5215 );
5216
5217 assert_matches!(exec.run_until_stalled(&mut add_phy_fut), Poll::Ready(Ok(())));
5218 }
5219
5220 let calls = test_values.power_manager.calls.lock();
5221 assert!(calls.contains(&"wlancfg-phy-1-level-2-dependency".to_string()));
5222
5223 let container = phy_manager.phys.get(&phy_id).unwrap();
5224 assert!(container.power_dependency_token.is_some());
5225 assert!(container.power_dependency_lease.is_some());
5226 }
5227
5228 #[fuchsia::test]
5229 fn test_power_leases_suspend_and_resume() {
5230 let mut exec = TestExecutor::new();
5231 let test_values = test_setup();
5232 let mut phy_manager = PhyManager::new(
5233 test_values.monitor_proxy,
5234 recovery::lookup_recovery_profile(""),
5235 false,
5236 test_values.node,
5237 test_values.telemetry_sender,
5238 test_values.recovery_sender,
5239 test_values.power_manager.clone(),
5240 );
5241
5242 let phy_id = 0;
5243 let mut phy_container = PhyContainer::new(vec![fidl_common::WlanMacRole::Client]);
5244 let token = zx::Event::create();
5245 let (lease_token, _) = zx::EventPair::create();
5246 phy_container.power_dependency_token = Some(token);
5247 phy_container.power_dependency_lease = Some(lease_token);
5248 let _ = phy_manager.phys.insert(phy_id, phy_container);
5249
5250 {
5252 let suspend_fut = phy_manager.on_before_suspend();
5253 let mut suspend_fut = pin!(suspend_fut);
5254 assert_matches!(exec.run_until_stalled(&mut suspend_fut), Poll::Ready(()));
5255 }
5256
5257 {
5258 let calls = test_values.power_manager.calls.lock();
5259 assert!(calls.contains(&"wlancfg-phy-0-level-1-dependency".to_string()));
5260 }
5261
5262 {
5264 let resume_fut = phy_manager.on_after_resume();
5265 let mut resume_fut = pin!(resume_fut);
5266 assert_matches!(exec.run_until_stalled(&mut resume_fut), Poll::Ready(()));
5267 }
5268
5269 {
5270 let calls = test_values.power_manager.calls.lock();
5271 assert!(calls.contains(&"wlancfg-phy-0-level-2-dependency".to_string()));
5272 }
5273 }
5274
5275 #[fuchsia::test]
5276 fn test_power_leases_skipped_when_dependency_token_unavailable() {
5277 let mut exec = TestExecutor::new();
5278 let mut test_values = test_setup();
5279 let mut phy_manager = PhyManager::new(
5280 test_values.monitor_proxy,
5281 recovery::lookup_recovery_profile(""),
5282 false,
5283 test_values.node,
5284 test_values.telemetry_sender,
5285 test_values.recovery_sender,
5286 test_values.power_manager.clone(),
5287 );
5288 phy_manager.client_connections_enabled = true;
5289
5290 let phy_id = 0;
5291 {
5292 let add_phy_fut = phy_manager.add_phy(phy_id);
5293 let mut add_phy_fut = pin!(add_phy_fut);
5294 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
5295
5296 send_get_power_element_dependency_token_response(
5297 &mut exec,
5298 &mut test_values.monitor_stream,
5299 Err(ZX_ERR_NOT_FOUND),
5300 );
5301
5302 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
5303
5304 send_get_supported_mac_roles_response(
5305 &mut exec,
5306 &mut test_values.monitor_stream,
5307 Ok(&[fidl_common::WlanMacRole::Client]),
5308 );
5309
5310 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
5311 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, Some(10));
5312
5313 assert_matches!(exec.run_until_stalled(&mut add_phy_fut), Poll::Ready(Ok(())));
5314 }
5315
5316 let container = phy_manager.phys.get(&phy_id).unwrap();
5318 assert!(container.client_ifaces.contains(&10));
5319 assert!(container.power_dependency_token.is_none());
5320 assert!(container.power_dependency_lease.is_none());
5321 }
5322}