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