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 fuchsia_inspect::{self as inspect, NumericProperty};
15use ieee80211::{MacAddr, MacAddrBytes, NULL_ADDR};
16use log::{error, info, warn};
17use std::collections::{HashMap, HashSet};
18use std::iter::Iterator;
19use thiserror::Error;
20use {
21 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_device_service as fidl_service,
22 fidl_fuchsia_wlan_sme as fidl_sme,
23};
24
25const DEFECT_RETENTION_SECONDS: u32 = 86400;
28
29#[derive(Clone, Debug, Error, PartialEq, Eq)]
31pub enum PhyManagerError {
32 #[error("the requested operation is not supported")]
33 Unsupported,
34 #[error("unable to query phy information")]
35 PhyQueryFailure,
36 #[error("failed to set country for new PHY")]
37 PhySetCountryFailure,
38 #[error("unable to reset PHY")]
39 PhyResetFailure,
40 #[error("unable to query iface information")]
41 IfaceQueryFailure,
42 #[error("unable to create iface")]
43 IfaceCreateFailure,
44 #[error("unable to destroy iface")]
45 IfaceDestroyFailure,
46 #[error("internal state has become inconsistent")]
47 InternalError,
48}
49
50#[derive(PartialEq)]
55pub enum CreateClientIfacesReason {
56 StartClientConnections,
57 RecoverClientIfaces,
58}
59
60pub(crate) struct PhyContainer {
62 supported_mac_roles: HashSet<fidl_common::WlanMacRole>,
63 client_ifaces: HashSet<u16>,
64 ap_ifaces: HashSet<u16>,
65 destroyed_ifaces: HashSet<u16>,
68 defects: EventHistory<Defect>,
69 recoveries: EventHistory<recovery::RecoveryAction>,
70}
71
72#[async_trait(?Send)]
73pub trait PhyManagerApi {
74 async fn add_phy(&mut self, phy_id: u16) -> Result<(), PhyManagerError>;
77
78 fn remove_phy(&mut self, phy_id: u16);
80
81 async fn on_iface_added(&mut self, iface_id: u16) -> Result<(), PhyManagerError>;
89
90 fn on_iface_removed(&mut self, iface_id: u16);
92
93 async fn create_all_client_ifaces(
98 &mut self,
99 reason: CreateClientIfacesReason,
100 ) -> HashMap<u16, Result<Vec<u16>, PhyManagerError>>;
101
102 fn client_connections_enabled(&self) -> bool;
106
107 async fn destroy_all_client_ifaces(&mut self) -> Result<(), PhyManagerError>;
110
111 fn get_client(&mut self) -> Option<u16>;
113
114 async fn create_or_get_ap_iface(&mut self) -> Result<Option<u16>, PhyManagerError>;
119
120 async fn destroy_ap_iface(&mut self, iface_id: u16) -> Result<(), PhyManagerError>;
122
123 async fn destroy_all_ap_ifaces(&mut self) -> Result<(), PhyManagerError>;
125
126 fn suggest_ap_mac(&mut self, mac: MacAddr);
128
129 fn get_phy_ids(&self) -> Vec<u16>;
131
132 fn log_phy_add_failure(&mut self);
134
135 async fn set_country_code(
138 &mut self,
139 country_code: Option<client_types::CountryCode>,
140 ) -> Result<(), PhyManagerError>;
141
142 fn record_defect(&mut self, defect: Defect);
144
145 async fn perform_recovery(&mut self, summary: recovery::RecoverySummary);
147}
148
149pub struct PhyManager {
151 phys: HashMap<u16, PhyContainer>,
152 recovery_profile: recovery::RecoveryProfile,
153 recovery_enabled: bool,
154 device_monitor: fidl_service::DeviceMonitorProxy,
155 client_connections_enabled: bool,
156 suggested_ap_mac: Option<MacAddr>,
157 saved_country_code: Option<client_types::CountryCode>,
158 _node: inspect::Node,
159 telemetry_sender: TelemetrySender,
160 recovery_action_sender: recovery::RecoveryActionSender,
161 phy_add_fail_count: inspect::UintProperty,
162}
163
164impl PhyContainer {
165 pub fn new(supported_mac_roles: Vec<fidl_common::WlanMacRole>) -> Self {
168 PhyContainer {
169 supported_mac_roles: supported_mac_roles.into_iter().collect(),
170 client_ifaces: HashSet::new(),
171 ap_ifaces: HashSet::new(),
172 destroyed_ifaces: HashSet::new(),
173 defects: EventHistory::<Defect>::new(DEFECT_RETENTION_SECONDS),
174 recoveries: EventHistory::<RecoveryAction>::new(DEFECT_RETENTION_SECONDS),
175 }
176 }
177}
178
179impl PhyManager {
183 pub fn new(
186 device_monitor: fidl_service::DeviceMonitorProxy,
187 recovery_profile: recovery::RecoveryProfile,
188 recovery_enabled: bool,
189 node: inspect::Node,
190 telemetry_sender: TelemetrySender,
191 recovery_action_sender: recovery::RecoveryActionSender,
192 ) -> Self {
193 let phy_add_fail_count = node.create_uint("phy_add_fail_count", 0);
194 PhyManager {
195 phys: HashMap::new(),
196 recovery_profile,
197 recovery_enabled,
198 device_monitor,
199 client_connections_enabled: false,
200 suggested_ap_mac: None,
201 saved_country_code: None,
202 _node: node,
203 telemetry_sender,
204 recovery_action_sender,
205 phy_add_fail_count,
206 }
207 }
208 async fn ensure_phy(&mut self, phy_id: u16) -> Result<&mut PhyContainer, PhyManagerError> {
210 if !self.phys.contains_key(&phy_id) {
211 self.add_phy(phy_id).await?;
212 }
213
214 Ok(self.phys.get_mut(&phy_id).ok_or_else(|| {
217 error!("Phy ID did not exist in self.phys");
218 PhyManagerError::InternalError
219 }))?
220 }
221
222 async fn query_iface(
224 &self,
225 iface_id: u16,
226 ) -> Result<Option<fidl_service::QueryIfaceResponse>, PhyManagerError> {
227 match self.device_monitor.query_iface(iface_id).await {
228 Ok(Ok(response)) => Ok(Some(response)),
229 Ok(Err(zx::sys::ZX_ERR_NOT_FOUND)) => Ok(None),
230 _ => Err(PhyManagerError::IfaceQueryFailure),
231 }
232 }
233
234 fn phys_for_role(&self, role: fidl_common::WlanMacRole) -> Vec<u16> {
236 self.phys
237 .iter()
238 .filter_map(|(k, v)| {
239 if v.supported_mac_roles.contains(&role) {
240 return Some(*k);
241 }
242 None
243 })
244 .collect()
245 }
246
247 fn log_recovery_action(&mut self, summary: recovery::RecoverySummary) {
249 let affected_phy_id = match summary.action {
250 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id }) => {
251 if let Some(container) = self.phys.get_mut(&phy_id) {
252 container.recoveries.add_event(summary.action);
253 Some(phy_id)
254 } else {
255 None
256 }
257 }
258 RecoveryAction::PhyRecovery(PhyRecoveryOperation::DestroyIface { iface_id })
259 | RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::Disconnect { iface_id })
260 | RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::StopAp { iface_id }) => {
261 let mut affected_phy_id = None;
262 for (phy_id, phy_info) in self.phys.iter_mut() {
263 if phy_info.ap_ifaces.contains(&iface_id)
264 || phy_info.client_ifaces.contains(&iface_id)
265 {
266 phy_info.recoveries.add_event(summary.action);
267 affected_phy_id = Some(*phy_id);
268 }
269 }
270
271 affected_phy_id
272 }
273 };
274
275 if let Some(phy_id) = affected_phy_id {
276 warn!("Recovery has been recommended for PHY {}: {:?}", phy_id, summary.action);
277 }
278
279 if let Some(recovery_summary) = summary.as_recovery_reason() {
280 self.telemetry_sender.send(TelemetryEvent::RecoveryEvent { reason: recovery_summary });
281 }
282 }
283
284 async fn create_iface(
287 &mut self,
288 phy_id: u16,
289 role: fidl_common::WlanMacRole,
290 sta_addr: MacAddr,
291 ) -> Result<u16, PhyManagerError> {
292 let request = fidl_service::DeviceMonitorCreateIfaceRequest {
293 phy_id: Some(phy_id),
294 role: Some(role),
295 sta_address: Some(sta_addr.to_array()),
296 ..Default::default()
297 };
298
299 let response = self.device_monitor.create_iface(&request).await;
300 let result = match response {
301 Err(e) => {
302 warn!("Failed to create iface: phy {}, FIDL error {:?}", phy_id, e);
303 Err(PhyManagerError::IfaceCreateFailure)
304 }
305 Ok(Err(e)) => {
306 warn!("Failed to create iface: phy {}, error {:?}", phy_id, e);
307 Err(PhyManagerError::IfaceCreateFailure)
308 }
309 Ok(Ok(fidl_service::DeviceMonitorCreateIfaceResponse { iface_id: None, .. })) => {
310 warn!("Failed to create iface. No iface ID received: phy {}", phy_id);
311 Err(PhyManagerError::IfaceCreateFailure)
312 }
313 Ok(Ok(fidl_service::DeviceMonitorCreateIfaceResponse {
314 iface_id: Some(iface_id),
315 ..
316 })) => Ok(iface_id),
317 };
318
319 if result.is_err() {
320 self.record_defect(Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id }));
321 return result;
322 }
323 self.telemetry_sender.send(TelemetryEvent::IfaceCreationResult(Ok(())));
324 result
325 }
326}
327
328#[async_trait(?Send)]
329impl PhyManagerApi for PhyManager {
330 async fn add_phy(&mut self, phy_id: u16) -> Result<(), PhyManagerError> {
331 let supported_mac_roles = self
332 .device_monitor
333 .get_supported_mac_roles(phy_id)
334 .await
335 .map_err(|e| {
336 warn!("Failed to communicate with monitor service: {:?}", e);
337 PhyManagerError::PhyQueryFailure
338 })?
339 .map_err(|e| {
340 warn!("Unable to get supported MAC roles: {:?}", e);
341 PhyManagerError::PhyQueryFailure
342 })?;
343
344 info!("adding PHY ID #{}", phy_id);
346 let mut phy_container = PhyContainer::new(supported_mac_roles);
347
348 let set_country_result = match self.saved_country_code {
350 Some(country_code) => {
351 set_phy_country_code(&self.device_monitor, phy_id, country_code).await
352 }
353 None => Ok(()),
354 };
355
356 if set_country_result.is_err() {
360 clear_phy_country_code(&self.device_monitor, phy_id).await?;
361 }
362
363 if self.client_connections_enabled
364 && phy_container.supported_mac_roles.contains(&fidl_common::WlanMacRole::Client)
365 {
366 let iface_id =
367 self.create_iface(phy_id, fidl_common::WlanMacRole::Client, NULL_ADDR).await?;
368 let _ = phy_container.client_ifaces.insert(iface_id);
369 }
370
371 if self.phys.insert(phy_id, phy_container).is_some() {
372 warn!("Unexpectedly replaced existing phy information for id {}", phy_id);
373 };
374
375 Ok(())
376 }
377
378 fn remove_phy(&mut self, phy_id: u16) {
379 if self.phys.remove(&phy_id).is_none() {
380 warn!("Attempted to remove non-existed phy {}", phy_id);
381 };
382 }
383
384 async fn on_iface_added(&mut self, iface_id: u16) -> Result<(), PhyManagerError> {
385 if let Some(query_iface_response) = self.query_iface(iface_id).await? {
386 let iface_id = query_iface_response.id;
387 let phy = self.ensure_phy(query_iface_response.phy_id).await?;
388
389 match query_iface_response.role {
390 fidl_common::WlanMacRole::Client => {
391 if phy.client_ifaces.insert(iface_id) {
392 warn!(
394 "Detected an unexpected client iface id {} created outside of PhyManager",
395 iface_id
396 );
397 }
398 }
399 fidl_common::WlanMacRole::Ap => {
400 if phy.ap_ifaces.insert(iface_id) {
401 warn!("Detected an unexpected AP iface created outside of PhyManager");
403 }
404 }
405 fidl_common::WlanMacRole::Mesh => {
406 return Err(PhyManagerError::Unsupported);
407 }
408 fidl_common::WlanMacRoleUnknown!() => {
409 error!("Unknown WlanMacRole type {:?}", query_iface_response.role);
410 return Err(PhyManagerError::Unsupported);
411 }
412 }
413 }
414 Ok(())
415 }
416
417 fn on_iface_removed(&mut self, iface_id: u16) {
418 for (_, phy_info) in self.phys.iter_mut() {
419 let _ = phy_info.client_ifaces.remove(&iface_id);
423 let _ = phy_info.ap_ifaces.remove(&iface_id);
424 }
425 }
426
427 async fn create_all_client_ifaces(
428 &mut self,
429 reason: CreateClientIfacesReason,
430 ) -> HashMap<u16, Result<Vec<u16>, PhyManagerError>> {
431 if reason == CreateClientIfacesReason::StartClientConnections {
432 self.client_connections_enabled = true;
433 }
434
435 let mut available_iface_ids = HashMap::new();
436 if self.client_connections_enabled {
437 let client_capable_phy_ids = self.phys_for_role(fidl_common::WlanMacRole::Client);
438
439 for phy_id in client_capable_phy_ids.iter().copied() {
440 let phy_container = match self.phys.get_mut(&phy_id) {
441 Some(phy_container) => phy_container,
442 None => {
443 let _ = available_iface_ids
444 .insert(phy_id, Err(PhyManagerError::PhyQueryFailure));
445 continue;
446 }
447 };
448
449 if phy_container.client_ifaces.is_empty() {
452 let iface_id = match self
453 .create_iface(phy_id, fidl_common::WlanMacRole::Client, NULL_ADDR)
454 .await
455 {
456 Ok(iface_id) => iface_id,
457 Err(e) => {
458 warn!("Failed to recover iface for PHY {}: {:?}", phy_id, e);
459 let _ = available_iface_ids.insert(phy_id, Err(e));
460 continue;
461 }
462 };
463
464 #[expect(clippy::unwrap_used)]
467 let phy_container = self.phys.get_mut(&phy_id).unwrap();
468 let _ = phy_container.client_ifaces.insert(iface_id);
469
470 let _ = available_iface_ids.insert(phy_id, Ok(vec![iface_id]));
473 } else {
474 let _ = available_iface_ids
475 .insert(phy_id, Ok(phy_container.client_ifaces.iter().copied().collect()));
476 }
477 }
478 }
479
480 available_iface_ids
481 }
482
483 fn client_connections_enabled(&self) -> bool {
484 self.client_connections_enabled
485 }
486
487 async fn destroy_all_client_ifaces(&mut self) -> Result<(), PhyManagerError> {
488 self.client_connections_enabled = false;
489
490 let client_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Client);
491 let mut result = Ok(());
492 let mut failing_phys = Vec::new();
493
494 for client_phy in client_capable_phys.iter() {
495 let phy_container =
496 self.phys.get_mut(client_phy).ok_or(PhyManagerError::PhyQueryFailure)?;
497
498 let mut lingering_ifaces = HashSet::new();
500
501 for iface_id in phy_container.client_ifaces.drain() {
502 match destroy_iface(&self.device_monitor, iface_id, &self.telemetry_sender).await {
503 Ok(()) => {
504 let _ = phy_container.destroyed_ifaces.insert(iface_id);
505 }
506 Err(e) => {
507 result = Err(e);
508 failing_phys.push(*client_phy);
509 if !lingering_ifaces.insert(iface_id) {
510 warn!("Unexpected duplicate lingering iface for id {}", iface_id);
511 };
512 }
513 }
514 }
515 phy_container.client_ifaces = lingering_ifaces;
516 }
517
518 if result.is_err() {
519 for phy_id in failing_phys {
520 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }))
521 }
522 }
523
524 result
525 }
526
527 fn get_client(&mut self) -> Option<u16> {
528 if !self.client_connections_enabled {
529 return None;
530 }
531
532 let client_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Client);
533
534 let first_client_capable_phy = client_capable_phys.first()?;
536 let phy = self.phys.get_mut(first_client_capable_phy)?;
537 phy.client_ifaces.iter().next().copied()
538 }
539
540 async fn create_or_get_ap_iface(&mut self) -> Result<Option<u16>, PhyManagerError> {
541 let ap_capable_phy_ids = self.phys_for_role(fidl_common::WlanMacRole::Ap);
542
543 for ap_phy_id in ap_capable_phy_ids.iter() {
545 let phy_container =
546 self.phys.get_mut(ap_phy_id).ok_or(PhyManagerError::PhyQueryFailure)?;
547 if phy_container.ap_ifaces.is_empty() {
548 let mac = match self.suggested_ap_mac {
549 Some(mac) => mac,
550 None => NULL_ADDR,
551 };
552 let iface_id =
553 self.create_iface(*ap_phy_id, fidl_common::WlanMacRole::Ap, mac).await?;
554
555 #[expect(clippy::unwrap_used)]
559 let phy_container = self.phys.get_mut(ap_phy_id).unwrap();
560 let _ = phy_container.ap_ifaces.insert(iface_id);
561 return Ok(Some(iface_id));
562 }
563 }
564
565 let Some(first_ap_capable_phy) = ap_capable_phy_ids.first() else {
569 return Ok(None);
570 };
571 let phy = match self.phys.get_mut(first_ap_capable_phy) {
572 Some(phy_container) => phy_container,
573 None => return Ok(None),
574 };
575 match phy.ap_ifaces.iter().next() {
576 Some(iface_id) => Ok(Some(*iface_id)),
577 None => Ok(None),
578 }
579 }
580
581 async fn destroy_ap_iface(&mut self, iface_id: u16) -> Result<(), PhyManagerError> {
582 let mut result = Ok(());
583 let mut failing_phy = None;
584
585 for (phy_id, phy_container) in self.phys.iter_mut() {
588 if phy_container.ap_ifaces.remove(&iface_id) {
589 match destroy_iface(&self.device_monitor, iface_id, &self.telemetry_sender).await {
590 Ok(()) => {
591 let _ = phy_container.destroyed_ifaces.insert(iface_id);
592 }
593 Err(e) => {
594 let _ = phy_container.ap_ifaces.insert(iface_id);
595 result = Err(e);
596 failing_phy = Some(*phy_id);
597 }
598 }
599 break;
600 }
601 }
602
603 if let (Err(_), Some(phy_id)) = (result.as_ref(), failing_phy) {
604 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }))
605 }
606
607 result
608 }
609
610 async fn destroy_all_ap_ifaces(&mut self) -> Result<(), PhyManagerError> {
611 let ap_capable_phys = self.phys_for_role(fidl_common::WlanMacRole::Ap);
612 let mut result = Ok(());
613 let mut failing_phys = Vec::new();
614
615 for ap_phy in ap_capable_phys.iter() {
616 let phy_container =
617 self.phys.get_mut(ap_phy).ok_or(PhyManagerError::PhyQueryFailure)?;
618
619 let mut lingering_ifaces = HashSet::new();
621 for iface_id in phy_container.ap_ifaces.drain() {
622 match destroy_iface(&self.device_monitor, iface_id, &self.telemetry_sender).await {
623 Ok(()) => {
624 let _ = phy_container.destroyed_ifaces.insert(iface_id);
625 }
626 Err(e) => {
627 result = Err(e);
628 failing_phys.push(ap_phy);
629 let _ = lingering_ifaces.insert(iface_id);
630 }
631 }
632 }
633 phy_container.ap_ifaces = lingering_ifaces;
634 }
635
636 if result.is_err() {
637 for phy_id in failing_phys {
638 self.record_defect(Defect::Phy(PhyFailure::IfaceDestructionFailure {
639 phy_id: *phy_id,
640 }))
641 }
642 }
643
644 result
645 }
646
647 fn suggest_ap_mac(&mut self, mac: MacAddr) {
648 self.suggested_ap_mac = Some(mac);
649 }
650
651 fn get_phy_ids(&self) -> Vec<u16> {
652 self.phys.keys().cloned().collect()
653 }
654
655 fn log_phy_add_failure(&mut self) {
656 let _ = self.phy_add_fail_count.add(1);
657 }
658
659 async fn set_country_code(
660 &mut self,
661 country_code: Option<client_types::CountryCode>,
662 ) -> Result<(), PhyManagerError> {
663 self.saved_country_code = country_code;
664
665 match country_code {
666 Some(country_code) => {
667 for phy_id in self.phys.keys() {
668 set_phy_country_code(&self.device_monitor, *phy_id, country_code).await?;
669 }
670 }
671 None => {
672 for phy_id in self.phys.keys() {
673 clear_phy_country_code(&self.device_monitor, *phy_id).await?;
674 }
675 }
676 }
677
678 Ok(())
679 }
680
681 fn record_defect(&mut self, defect: Defect) {
682 let mut recovery_action = None;
683
684 match defect {
685 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id }) => {
686 self.telemetry_sender.send(TelemetryEvent::IfaceCreationResult(Err(())));
687 if let Some(container) = self.phys.get_mut(&phy_id) {
688 container.defects.add_event(defect);
689 recovery_action = (self.recovery_profile)(
690 phy_id,
691 &mut container.defects,
692 &mut container.recoveries,
693 defect,
694 )
695 }
696 }
697 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id }) => {
698 if let Some(container) = self.phys.get_mut(&phy_id) {
699 container.defects.add_event(defect);
700 recovery_action = (self.recovery_profile)(
701 phy_id,
702 &mut container.defects,
703 &mut container.recoveries,
704 defect,
705 )
706 }
707 }
708 Defect::Iface(IfaceFailure::CanceledScan { iface_id })
709 | Defect::Iface(IfaceFailure::FailedScan { iface_id })
710 | Defect::Iface(IfaceFailure::EmptyScanResults { iface_id })
711 | Defect::Iface(IfaceFailure::ConnectionFailure { iface_id }) => {
712 for (phy_id, phy_info) in self.phys.iter_mut() {
713 if phy_info.client_ifaces.contains(&iface_id)
714 || phy_info.destroyed_ifaces.contains(&iface_id)
715 {
716 phy_info.defects.add_event(defect);
717
718 recovery_action = (self.recovery_profile)(
719 *phy_id,
720 &mut phy_info.defects,
721 &mut phy_info.recoveries,
722 defect,
723 );
724
725 break;
726 }
727 }
728 }
729 Defect::Iface(IfaceFailure::ApStartFailure { iface_id }) => {
730 for (phy_id, phy_info) in self.phys.iter_mut() {
731 if phy_info.ap_ifaces.contains(&iface_id)
732 || phy_info.destroyed_ifaces.contains(&iface_id)
733 {
734 phy_info.defects.add_event(defect);
735
736 recovery_action = (self.recovery_profile)(
737 *phy_id,
738 &mut phy_info.defects,
739 &mut phy_info.recoveries,
740 defect,
741 );
742
743 break;
744 }
745 }
746 }
747 Defect::Iface(IfaceFailure::Timeout { iface_id, source }) => {
748 self.telemetry_sender.send(TelemetryEvent::SmeTimeout { source });
749
750 for (phy_id, phy_info) in self.phys.iter_mut() {
751 if phy_info.ap_ifaces.contains(&iface_id)
752 || phy_info.client_ifaces.contains(&iface_id)
753 || phy_info.destroyed_ifaces.contains(&iface_id)
754 {
755 phy_info.defects.add_event(defect);
756
757 recovery_action = (self.recovery_profile)(
758 *phy_id,
759 &mut phy_info.defects,
760 &mut phy_info.recoveries,
761 defect,
762 );
763
764 break;
765 }
766 }
767 }
768 }
769
770 if let Some(recovery_action) = recovery_action.take()
771 && let Err(e) = self
772 .recovery_action_sender
773 .try_send(recovery::RecoverySummary::new(defect, recovery_action))
774 {
775 warn!("Unable to suggest recovery action {:?}: {:?}", recovery_action, e);
776 }
777 }
778
779 async fn perform_recovery(&mut self, summary: recovery::RecoverySummary) {
780 self.log_recovery_action(summary);
781
782 if self.recovery_enabled {
783 match summary.action {
784 RecoveryAction::PhyRecovery(PhyRecoveryOperation::DestroyIface { iface_id }) => {
785 for (_, phy_container) in self.phys.iter_mut() {
786 if phy_container.ap_ifaces.remove(&iface_id) {
787 #[allow(
788 clippy::redundant_pattern_matching,
789 reason = "mass allow for https://fxbug.dev/381896734"
790 )]
791 if let Err(_) = destroy_iface(
792 &self.device_monitor,
793 iface_id,
794 &self.telemetry_sender,
795 )
796 .await
797 {
798 let _ = phy_container.ap_ifaces.insert(iface_id);
799 } else {
800 let _ = phy_container.destroyed_ifaces.insert(iface_id);
801 }
802
803 return;
804 }
805
806 if phy_container.client_ifaces.remove(&iface_id) {
807 if destroy_iface(&self.device_monitor, iface_id, &self.telemetry_sender)
808 .await
809 .is_err()
810 {
811 let _ = phy_container.client_ifaces.insert(iface_id);
812 } else {
813 let _ = phy_container.destroyed_ifaces.insert(iface_id);
814 }
815
816 return;
817 }
818 }
819
820 warn!(
821 "Recovery suggested destroying iface {}, but no record was found.",
822 iface_id
823 );
824 }
825 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id }) => {
826 for recorded_phy_id in self.phys.keys() {
827 if phy_id == *recorded_phy_id {
828 if let Err(e) = reset_phy(&self.device_monitor, phy_id).await {
829 warn!("Resetting PHY {} failed: {:?}", phy_id, e);
830 }
831
832 if let Some(country_code) = self.saved_country_code {
834 info!("Setting country code after phy reset");
835 if let Err(e) =
836 set_phy_country_code(&self.device_monitor, phy_id, country_code)
837 .await
838 {
839 warn!(
840 "Proceeding with default country code because we failed to set the cached one: {}",
841 e
842 );
843 }
844 };
845
846 return;
847 }
848 }
849 }
850 RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::Disconnect { iface_id }) => {
851 if let Err(e) = disconnect(&self.device_monitor, iface_id).await {
852 warn!("Disconnecting client {} failed: {:?}", iface_id, e);
853 }
854 }
855 RecoveryAction::IfaceRecovery(IfaceRecoveryOperation::StopAp { iface_id }) => {
856 if let Err(e) = stop_ap(&self.device_monitor, iface_id).await {
857 warn!("Stopping AP {} failed: {:?}", iface_id, e);
858 }
859 }
860 }
861 }
862 }
863}
864
865async fn destroy_iface(
867 proxy: &fidl_service::DeviceMonitorProxy,
868 iface_id: u16,
869 telemetry_sender: &TelemetrySender,
870) -> Result<(), PhyManagerError> {
871 let request = fidl_service::DestroyIfaceRequest { iface_id };
872 let (destroy_iface_response, metric) = match proxy.destroy_iface(&request).await {
873 Ok(status) => match status {
874 zx::sys::ZX_OK => (Ok(()), Some(Ok(()))),
875 zx::sys::ZX_ERR_NOT_FOUND => {
876 info!("Interface not found, assuming it is already destroyed");
877 (Ok(()), None)
879 }
880 e => {
881 warn!("failed to destroy iface {}: {}", iface_id, e);
882 (Err(PhyManagerError::IfaceDestroyFailure), Some(Err(())))
883 }
884 },
885 Err(e) => {
886 warn!("failed to send destroy iface {}: {}", iface_id, e);
887 (Err(PhyManagerError::IfaceDestroyFailure), Some(Err(())))
888 }
889 };
890
891 if let Some(metric) = metric {
892 telemetry_sender.send(TelemetryEvent::IfaceDestructionResult(metric));
893 }
894
895 destroy_iface_response
896}
897
898async fn reset_phy(
899 proxy: &fidl_service::DeviceMonitorProxy,
900 phy_id: u16,
901) -> Result<(), PhyManagerError> {
902 let result = proxy.reset(phy_id).await.map_err(|e| {
903 warn!("Request to reset PHY {} failed: {:?}", phy_id, e);
904 PhyManagerError::InternalError
905 })?;
906
907 result.map_err(|e| {
908 warn!("Failed to reset PHY {}: {:?}", phy_id, e);
909 PhyManagerError::PhyResetFailure
910 })
911}
912
913async fn set_phy_country_code(
914 proxy: &fidl_service::DeviceMonitorProxy,
915 phy_id: u16,
916 country_code: client_types::CountryCode,
917) -> Result<(), PhyManagerError> {
918 let status = proxy
919 .set_country(&fidl_service::SetCountryRequest { phy_id, alpha2: country_code.into() })
920 .await
921 .map_err(|e| {
922 error!("Failed to set country code for PHY {}: {:?}", phy_id, e);
923 PhyManagerError::PhySetCountryFailure
924 })?;
925
926 zx::ok(status).map_err(|e| {
927 error!("Received bad status when setting country code for PHY {}: {}", phy_id, e);
928 PhyManagerError::PhySetCountryFailure
929 })
930}
931
932async fn clear_phy_country_code(
933 proxy: &fidl_service::DeviceMonitorProxy,
934 phy_id: u16,
935) -> Result<(), PhyManagerError> {
936 let status =
937 proxy.clear_country(&fidl_service::ClearCountryRequest { phy_id }).await.map_err(|e| {
938 error!("Failed to clear country code for PHY {}: {:?}", phy_id, e);
939 PhyManagerError::PhySetCountryFailure
940 })?;
941
942 zx::ok(status).map_err(|e| {
943 error!("Received bad status when clearing country code for PHY {}: {}", phy_id, e);
944 PhyManagerError::PhySetCountryFailure
945 })
946}
947
948async fn disconnect(
949 dev_monitor_proxy: &fidl_service::DeviceMonitorProxy,
950 iface_id: u16,
951) -> Result<(), Error> {
952 let (sme_proxy, remote) = create_proxy();
953 dev_monitor_proxy.get_client_sme(iface_id, remote).await?.map_err(zx::Status::from_raw)?;
954
955 sme_proxy
956 .disconnect(fidl_sme::UserDisconnectReason::Recovery)
957 .await
958 .map_err(|e| format_err!("Disconnect failed: {:?}", e))
959}
960
961async fn stop_ap(
962 dev_monitor_proxy: &fidl_service::DeviceMonitorProxy,
963 iface_id: u16,
964) -> Result<(), Error> {
965 let (sme_proxy, remote) = create_proxy();
966 dev_monitor_proxy.get_ap_sme(iface_id, remote).await?.map_err(zx::Status::from_raw)?;
967
968 match sme_proxy.stop().await {
969 Ok(result) => match result {
970 fidl_sme::StopApResultCode::Success => Ok(()),
971 err => Err(format_err!("Stop AP failed: {:?}", err)),
972 },
973 Err(e) => Err(format_err!("Stop AP request failed: {:?}", e)),
974 }
975}
976
977#[cfg(test)]
978mod tests {
979 use super::*;
980 use crate::telemetry;
981 use crate::util::testing::{poll_ap_sme_req, poll_sme_req};
982 use assert_matches::assert_matches;
983 use diagnostics_assertions::assert_data_tree;
984 use fidl::endpoints;
985 use fuchsia_async::{TestExecutor, run_singlethreaded};
986 use futures::channel::mpsc;
987 use futures::stream::StreamExt;
988 use futures::task::Poll;
989 use std::pin::pin;
990 use test_case::test_case;
991 use zx::sys::{ZX_ERR_NOT_FOUND, ZX_OK};
992 use {
993 fidl_fuchsia_wlan_device_service as fidl_service, fidl_fuchsia_wlan_sme as fidl_sme,
994 fuchsia_inspect as inspect,
995 };
996
997 struct TestValues {
1000 monitor_proxy: fidl_service::DeviceMonitorProxy,
1001 monitor_stream: fidl_service::DeviceMonitorRequestStream,
1002 inspector: inspect::Inspector,
1003 node: inspect::Node,
1004 telemetry_sender: TelemetrySender,
1005 telemetry_receiver: mpsc::Receiver<TelemetryEvent>,
1006 recovery_sender: recovery::RecoveryActionSender,
1007 recovery_receiver: recovery::RecoveryActionReceiver,
1008 }
1009
1010 fn test_setup() -> TestValues {
1012 let (monitor_proxy, monitor_requests) =
1013 endpoints::create_proxy::<fidl_service::DeviceMonitorMarker>();
1014 let monitor_stream = monitor_requests.into_stream();
1015
1016 let inspector = inspect::Inspector::default();
1017 let node = inspector.root().create_child("phy_manager");
1018 let (sender, telemetry_receiver) = mpsc::channel::<TelemetryEvent>(100);
1019 let telemetry_sender = TelemetrySender::new(sender);
1020 let (recovery_sender, recovery_receiver) =
1021 mpsc::channel::<recovery::RecoverySummary>(recovery::RECOVERY_SUMMARY_CHANNEL_CAPACITY);
1022
1023 TestValues {
1024 monitor_proxy,
1025 monitor_stream,
1026 inspector,
1027 node,
1028 telemetry_sender,
1029 telemetry_receiver,
1030 recovery_sender,
1031 recovery_receiver,
1032 }
1033 }
1034
1035 fn send_get_supported_mac_roles_response(
1038 exec: &mut TestExecutor,
1039 server: &mut fidl_service::DeviceMonitorRequestStream,
1040 supported_mac_roles: Result<&[fidl_common::WlanMacRole], zx::sys::zx_status_t>,
1041 ) {
1042 let _ = assert_matches!(
1043 exec.run_until_stalled(&mut server.next()),
1044 Poll::Ready(Some(Ok(
1045 fidl_service::DeviceMonitorRequest::GetSupportedMacRoles {
1046 responder, ..
1047 }
1048 ))) => {
1049 responder.send(supported_mac_roles)
1050 }
1051 );
1052 }
1053
1054 #[track_caller]
1056 fn send_query_iface_response(
1057 exec: &mut TestExecutor,
1058 server: &mut fidl_service::DeviceMonitorRequestStream,
1059 iface_info: Option<fidl_service::QueryIfaceResponse>,
1060 ) {
1061 let response = iface_info.as_ref().ok_or(ZX_ERR_NOT_FOUND);
1062 assert_matches!(
1063 exec.run_until_stalled(&mut server.next()),
1064 Poll::Ready(Some(Ok(
1065 fidl_service::DeviceMonitorRequest::QueryIface {
1066 iface_id: _,
1067 responder,
1068 }
1069 ))) => {
1070 responder.send(response).expect("sending fake iface info");
1071 }
1072 );
1073 }
1074
1075 #[track_caller]
1078 fn send_create_iface_response(
1079 exec: &mut TestExecutor,
1080 server: &mut fidl_service::DeviceMonitorRequestStream,
1081 iface_id: Option<u16>,
1082 ) {
1083 assert_matches!(
1084 exec.run_until_stalled(&mut server.next()),
1085 Poll::Ready(Some(Ok(
1086 fidl_service::DeviceMonitorRequest::CreateIface {
1087 responder,
1088 ..
1089 }
1090 ))) => {
1091 match iface_id {
1092 Some(iface_id) => responder.send(
1093 Ok(&fidl_service::DeviceMonitorCreateIfaceResponse {
1094 iface_id: Some(iface_id),
1095 ..Default::default()
1096 })
1097 )
1098 .expect("sending fake iface id"),
1099 None => responder.send(Err(fidl_service::DeviceMonitorError::unknown())).expect("sending fake response with none")
1100 }
1101 }
1102 );
1103 }
1104
1105 fn send_destroy_iface_response(
1108 exec: &mut TestExecutor,
1109 server: &mut fidl_service::DeviceMonitorRequestStream,
1110 return_status: zx::sys::zx_status_t,
1111 ) {
1112 assert_matches!(
1113 exec.run_until_stalled(&mut server.next()),
1114 Poll::Ready(Some(Ok(
1115 fidl_service::DeviceMonitorRequest::DestroyIface {
1116 req: _,
1117 responder,
1118 }
1119 ))) => {
1120 responder
1121 .send(return_status)
1122 .unwrap_or_else(|e| panic!("sending fake response: {return_status}: {e:?}"));
1123 }
1124 );
1125 }
1126
1127 fn create_iface_response(
1129 role: fidl_common::WlanMacRole,
1130 id: u16,
1131 phy_id: u16,
1132 phy_assigned_id: u16,
1133 sta_addr: [u8; 6],
1134 factory_addr: [u8; 6],
1135 ) -> fidl_service::QueryIfaceResponse {
1136 fidl_service::QueryIfaceResponse {
1137 role,
1138 id,
1139 phy_id,
1140 phy_assigned_id,
1141 sta_addr,
1142 factory_addr,
1143 }
1144 }
1145
1146 #[fuchsia::test]
1151 fn add_valid_phy() {
1152 let mut exec = TestExecutor::new();
1153 let mut test_values = test_setup();
1154
1155 let fake_phy_id = 0;
1156 let fake_mac_roles = vec![];
1157
1158 let mut phy_manager = PhyManager::new(
1159 test_values.monitor_proxy,
1160 recovery::lookup_recovery_profile(""),
1161 false,
1162 test_values.node,
1163 test_values.telemetry_sender,
1164 test_values.recovery_sender,
1165 );
1166 {
1167 let add_phy_fut = phy_manager.add_phy(0);
1168 let mut add_phy_fut = pin!(add_phy_fut);
1169 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1170
1171 send_get_supported_mac_roles_response(
1172 &mut exec,
1173 &mut test_values.monitor_stream,
1174 Ok(&fake_mac_roles),
1175 );
1176
1177 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1178 }
1179
1180 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1181 assert_eq!(
1182 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1183 fake_mac_roles.into_iter().collect()
1184 );
1185 }
1186
1187 #[fuchsia::test]
1191 fn add_invalid_phy() {
1192 let mut exec = TestExecutor::new();
1193 let mut test_values = test_setup();
1194 let mut phy_manager = PhyManager::new(
1195 test_values.monitor_proxy,
1196 recovery::lookup_recovery_profile(""),
1197 false,
1198 test_values.node,
1199 test_values.telemetry_sender,
1200 test_values.recovery_sender,
1201 );
1202
1203 {
1204 let add_phy_fut = phy_manager.add_phy(1);
1205 let mut add_phy_fut = pin!(add_phy_fut);
1206 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1207
1208 send_get_supported_mac_roles_response(
1209 &mut exec,
1210 &mut test_values.monitor_stream,
1211 Err(zx::sys::ZX_ERR_NOT_FOUND),
1212 );
1213
1214 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1215 }
1216 assert!(phy_manager.phys.is_empty());
1217 }
1218
1219 #[fuchsia::test]
1223 fn add_duplicate_phy() {
1224 let mut exec = TestExecutor::new();
1225 let mut test_values = test_setup();
1226 let mut phy_manager = PhyManager::new(
1227 test_values.monitor_proxy,
1228 recovery::lookup_recovery_profile(""),
1229 false,
1230 test_values.node,
1231 test_values.telemetry_sender,
1232 test_values.recovery_sender,
1233 );
1234
1235 let fake_phy_id = 0;
1236 let fake_mac_roles = vec![];
1237
1238 {
1239 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1240 let mut add_phy_fut = pin!(add_phy_fut);
1241 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1242
1243 send_get_supported_mac_roles_response(
1244 &mut exec,
1245 &mut test_values.monitor_stream,
1246 Ok(&fake_mac_roles),
1247 );
1248
1249 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1250 }
1251
1252 {
1253 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1254 assert_eq!(
1255 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1256 fake_mac_roles.clone().into_iter().collect()
1257 );
1258 }
1259
1260 {
1262 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1263 let mut add_phy_fut = pin!(add_phy_fut);
1264 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1265
1266 send_get_supported_mac_roles_response(
1267 &mut exec,
1268 &mut test_values.monitor_stream,
1269 Ok(&fake_mac_roles),
1270 );
1271
1272 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1273 }
1274
1275 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1276 assert_eq!(
1277 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1278 fake_mac_roles.into_iter().collect()
1279 );
1280 }
1281
1282 #[fuchsia::test]
1283 fn create_all_client_ifaces_after_phys_added() {
1284 let mut exec = TestExecutor::new();
1285 let mut test_values = test_setup();
1286 let mut phy_manager = PhyManager::new(
1287 test_values.monitor_proxy,
1288 recovery::lookup_recovery_profile(""),
1289 false,
1290 test_values.node,
1291 test_values.telemetry_sender,
1292 test_values.recovery_sender,
1293 );
1294
1295 for phy_id in 0..2 {
1296 {
1297 let add_phy_fut = phy_manager.add_phy(phy_id);
1298 let mut add_phy_fut = pin!(add_phy_fut);
1299
1300 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1301
1302 send_get_supported_mac_roles_response(
1303 &mut exec,
1304 &mut test_values.monitor_stream,
1305 Ok(&[fidl_common::WlanMacRole::Client]),
1306 );
1307
1308 assert_matches!(exec.run_until_stalled(&mut add_phy_fut), Poll::Ready(Ok(())));
1309 }
1310 assert!(phy_manager.phys.contains_key(&phy_id));
1311 }
1312
1313 {
1314 let start_connections_fut = phy_manager
1315 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1316 let mut start_connections_fut = pin!(start_connections_fut);
1317
1318 for iface_id in [10, 20] {
1321 assert!(exec.run_until_stalled(&mut start_connections_fut).is_pending());
1322
1323 send_create_iface_response(
1324 &mut exec,
1325 &mut test_values.monitor_stream,
1326 Some(iface_id),
1327 );
1328 }
1329
1330 assert_matches!(exec.run_until_stalled(&mut start_connections_fut),
1331 Poll::Ready(iface_ids) => {
1332 assert!(iface_ids.values().all(Result::is_ok));
1333 assert!(iface_ids.contains_key(&0));
1334 assert!(iface_ids.contains_key(&1));
1335 let iface_ids: HashSet<_> = iface_ids.into_values().flat_map(Result::unwrap).collect();
1336 assert_eq!(iface_ids, HashSet::from([10, 20]));
1337 }
1338 );
1339 }
1340
1341 let mut iface_ids = HashSet::new();
1342 for phy_id in 0..2 {
1343 let phy_container = phy_manager.phys.get(&phy_id).unwrap();
1344 assert_eq!(phy_container.client_ifaces.len(), 1);
1347 phy_container.client_ifaces.iter().for_each(|iface_id| {
1348 assert!(iface_ids.insert(*iface_id));
1349 });
1350 assert!(phy_container.defects.events.is_empty());
1351 }
1352 assert_eq!(iface_ids, HashSet::from([10, 20]));
1353 }
1354
1355 #[fuchsia::test]
1359 fn add_phy_after_create_all_client_ifaces() {
1360 let mut exec = TestExecutor::new();
1361 let mut test_values = test_setup();
1362 let mut phy_manager = PhyManager::new(
1363 test_values.monitor_proxy,
1364 recovery::lookup_recovery_profile(""),
1365 false,
1366 test_values.node,
1367 test_values.telemetry_sender,
1368 test_values.recovery_sender,
1369 );
1370
1371 let fake_iface_id = 1;
1372 let fake_phy_id = 1;
1373 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1374
1375 {
1376 let start_connections_fut = phy_manager
1377 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1378 let mut start_connections_fut = pin!(start_connections_fut);
1379 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1380 }
1381
1382 {
1385 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1386 let mut add_phy_fut = pin!(add_phy_fut);
1387 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1388
1389 send_get_supported_mac_roles_response(
1390 &mut exec,
1391 &mut test_values.monitor_stream,
1392 Ok(&fake_mac_roles),
1393 );
1394
1395 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1396
1397 send_create_iface_response(
1398 &mut exec,
1399 &mut test_values.monitor_stream,
1400 Some(fake_iface_id),
1401 );
1402
1403 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1404 }
1405
1406 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1407 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1408 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1409 assert!(phy_container.defects.events.is_empty());
1410 }
1411
1412 #[fuchsia::test]
1417 fn add_phy_with_iface_creation_failure() {
1418 let mut exec = TestExecutor::new();
1419 let mut test_values = test_setup();
1420 let mut phy_manager = PhyManager::new(
1421 test_values.monitor_proxy,
1422 recovery::lookup_recovery_profile(""),
1423 false,
1424 test_values.node,
1425 test_values.telemetry_sender,
1426 test_values.recovery_sender,
1427 );
1428
1429 let fake_phy_id = 1;
1430 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1431
1432 {
1433 let start_connections_fut = phy_manager
1434 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1435 let mut start_connections_fut = pin!(start_connections_fut);
1436 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1437 }
1438
1439 {
1442 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1443 let mut add_phy_fut = pin!(add_phy_fut);
1444 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1445
1446 send_get_supported_mac_roles_response(
1447 &mut exec,
1448 &mut test_values.monitor_stream,
1449 Ok(&fake_mac_roles),
1450 );
1451
1452 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1453
1454 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
1456
1457 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1458 }
1459
1460 assert!(!phy_manager.phys.contains_key(&fake_phy_id));
1461 }
1462
1463 #[fuchsia::test]
1465 fn test_add_phy_after_setting_country_code() {
1466 let mut exec = TestExecutor::new();
1467 let mut test_values = test_setup();
1468
1469 let fake_phy_id = 1;
1470 let fake_mac_roles = vec![];
1471
1472 let mut phy_manager = PhyManager::new(
1473 test_values.monitor_proxy,
1474 recovery::lookup_recovery_profile(""),
1475 false,
1476 test_values.node,
1477 test_values.telemetry_sender,
1478 test_values.recovery_sender,
1479 );
1480
1481 {
1482 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
1483 let mut set_country_fut = pin!(set_country_fut);
1484 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
1485 }
1486
1487 {
1488 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1489 let mut add_phy_fut = pin!(add_phy_fut);
1490 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1491
1492 send_get_supported_mac_roles_response(
1493 &mut exec,
1494 &mut test_values.monitor_stream,
1495 Ok(&fake_mac_roles),
1496 );
1497
1498 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1499
1500 assert_matches!(
1501 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
1502 Poll::Ready(Some(Ok(
1503 fidl_service::DeviceMonitorRequest::SetCountry {
1504 req: fidl_service::SetCountryRequest {
1505 phy_id: 1,
1506 alpha2: [b'U', b'S'],
1507 },
1508 responder,
1509 }
1510 ))) => {
1511 responder.send(ZX_OK).expect("sending fake set country response");
1512 }
1513 );
1514
1515 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1516 }
1517
1518 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1519 assert_eq!(
1520 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1521 fake_mac_roles.into_iter().collect()
1522 );
1523 }
1524
1525 #[run_singlethreaded(test)]
1526 async fn remove_valid_phy() {
1527 let test_values = test_setup();
1528 let mut phy_manager = PhyManager::new(
1529 test_values.monitor_proxy,
1530 recovery::lookup_recovery_profile(""),
1531 false,
1532 test_values.node,
1533 test_values.telemetry_sender,
1534 test_values.recovery_sender,
1535 );
1536
1537 let fake_phy_id = 1;
1538 let fake_mac_roles = vec![];
1539
1540 let phy_container = PhyContainer::new(fake_mac_roles);
1541 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1542 phy_manager.remove_phy(fake_phy_id);
1543 assert!(phy_manager.phys.is_empty());
1544 }
1545
1546 #[run_singlethreaded(test)]
1551 async fn remove_nonexistent_phy() {
1552 let test_values = test_setup();
1553 let mut phy_manager = PhyManager::new(
1554 test_values.monitor_proxy,
1555 recovery::lookup_recovery_profile(""),
1556 false,
1557 test_values.node,
1558 test_values.telemetry_sender,
1559 test_values.recovery_sender,
1560 );
1561
1562 let fake_phy_id = 1;
1563 let fake_mac_roles = vec![];
1564
1565 let phy_container = PhyContainer::new(fake_mac_roles);
1566 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1567 phy_manager.remove_phy(2);
1568 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1569 }
1570
1571 #[fuchsia::test]
1575 fn on_iface_added() {
1576 let mut exec = TestExecutor::new();
1577 let mut test_values = test_setup();
1578 let mut phy_manager = PhyManager::new(
1579 test_values.monitor_proxy,
1580 recovery::lookup_recovery_profile(""),
1581 false,
1582 test_values.node,
1583 test_values.telemetry_sender,
1584 test_values.recovery_sender,
1585 );
1586
1587 let fake_phy_id = 1;
1590 let fake_mac_roles = vec![];
1591
1592 let phy_container = PhyContainer::new(fake_mac_roles);
1593
1594 let fake_role = fidl_common::WlanMacRole::Client;
1596 let fake_iface_id = 1;
1597 let fake_phy_assigned_id = 1;
1598 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1599 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1600 let iface_response = create_iface_response(
1601 fake_role,
1602 fake_iface_id,
1603 fake_phy_id,
1604 fake_phy_assigned_id,
1605 fake_sta_addr,
1606 fake_factory_addr,
1607 );
1608
1609 {
1610 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1612
1613 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1615 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1616 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1617
1618 send_query_iface_response(
1619 &mut exec,
1620 &mut test_values.monitor_stream,
1621 Some(iface_response),
1622 );
1623
1624 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1626 }
1627
1628 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1631 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1632 }
1633
1634 #[fuchsia::test]
1635 fn on_iface_added_unknown_role_is_unsupported() {
1636 let mut exec = TestExecutor::new();
1637 let mut test_values = test_setup();
1638 let mut phy_manager = PhyManager::new(
1639 test_values.monitor_proxy,
1640 recovery::lookup_recovery_profile(""),
1641 false,
1642 test_values.node,
1643 test_values.telemetry_sender,
1644 test_values.recovery_sender,
1645 );
1646
1647 let fake_phy_id = 1;
1650 let fake_mac_roles = vec![];
1651
1652 let phy_container = PhyContainer::new(fake_mac_roles);
1653
1654 let fake_role = fidl_common::WlanMacRole::unknown();
1656 let fake_iface_id = 1;
1657 let fake_phy_assigned_id = 1;
1658 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1659 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1660 let iface_response = create_iface_response(
1661 fake_role,
1662 fake_iface_id,
1663 fake_phy_id,
1664 fake_phy_assigned_id,
1665 fake_sta_addr,
1666 fake_factory_addr,
1667 );
1668
1669 {
1670 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1672
1673 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1675 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1676 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1677
1678 send_query_iface_response(
1679 &mut exec,
1680 &mut test_values.monitor_stream,
1681 Some(iface_response),
1682 );
1683
1684 assert_matches!(
1686 exec.run_until_stalled(&mut on_iface_added_fut),
1687 Poll::Ready(Err(PhyManagerError::Unsupported))
1688 );
1689 }
1690
1691 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1694 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
1695 }
1696
1697 #[fuchsia::test]
1702 fn on_iface_added_missing_phy() {
1703 let mut exec = TestExecutor::new();
1704 let mut test_values = test_setup();
1705 let mut phy_manager = PhyManager::new(
1706 test_values.monitor_proxy,
1707 recovery::lookup_recovery_profile(""),
1708 false,
1709 test_values.node,
1710 test_values.telemetry_sender,
1711 test_values.recovery_sender,
1712 );
1713
1714 let fake_phy_id = 1;
1717 let fake_mac_roles = vec![];
1718
1719 let fake_role = fidl_common::WlanMacRole::Client;
1721 let fake_iface_id = 1;
1722 let fake_phy_assigned_id = 1;
1723 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1724 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1725 let iface_response = create_iface_response(
1726 fake_role,
1727 fake_iface_id,
1728 fake_phy_id,
1729 fake_phy_assigned_id,
1730 fake_sta_addr,
1731 fake_factory_addr,
1732 );
1733
1734 {
1735 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1737 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1738
1739 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1744
1745 send_query_iface_response(
1746 &mut exec,
1747 &mut test_values.monitor_stream,
1748 Some(iface_response),
1749 );
1750
1751 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1753
1754 send_get_supported_mac_roles_response(
1755 &mut exec,
1756 &mut test_values.monitor_stream,
1757 Ok(&fake_mac_roles),
1758 );
1759
1760 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1762 }
1763
1764 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1767
1768 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1769 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1770 }
1771
1772 #[fuchsia::test]
1776 fn add_duplicate_iface() {
1777 let mut exec = TestExecutor::new();
1778 let mut test_values = test_setup();
1779 let mut phy_manager = PhyManager::new(
1780 test_values.monitor_proxy,
1781 recovery::lookup_recovery_profile(""),
1782 false,
1783 test_values.node,
1784 test_values.telemetry_sender,
1785 test_values.recovery_sender,
1786 );
1787
1788 let fake_phy_id = 1;
1791 let fake_mac_roles = vec![];
1792
1793 let phy_container = PhyContainer::new(fake_mac_roles);
1795 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1796
1797 let fake_role = fidl_common::WlanMacRole::Client;
1799 let fake_iface_id = 1;
1800 let fake_phy_assigned_id = 1;
1801 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1802 let fake_factory_addr = [0, 1, 2, 3, 4, 5];
1803 let iface_response = create_iface_response(
1804 fake_role,
1805 fake_iface_id,
1806 fake_phy_id,
1807 fake_phy_assigned_id,
1808 fake_sta_addr,
1809 fake_factory_addr,
1810 );
1811
1812 for _ in 0..2 {
1814 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1816 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1817 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1818
1819 send_query_iface_response(
1820 &mut exec,
1821 &mut test_values.monitor_stream,
1822 Some(iface_response),
1823 );
1824
1825 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1827 }
1828
1829 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1832 assert_eq!(phy_container.client_ifaces.len(), 1);
1833 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1834 }
1835
1836 #[fuchsia::test]
1840 fn add_nonexistent_iface() {
1841 let mut exec = TestExecutor::new();
1842 let mut test_values = test_setup();
1843 let mut phy_manager = PhyManager::new(
1844 test_values.monitor_proxy,
1845 recovery::lookup_recovery_profile(""),
1846 false,
1847 test_values.node,
1848 test_values.telemetry_sender,
1849 test_values.recovery_sender,
1850 );
1851
1852 {
1853 let on_iface_added_fut = phy_manager.on_iface_added(1);
1855 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1856 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1857
1858 send_query_iface_response(&mut exec, &mut test_values.monitor_stream, None);
1859
1860 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1862 }
1863
1864 assert!(phy_manager.phys.is_empty());
1867 }
1868
1869 #[run_singlethreaded(test)]
1873 async fn test_on_iface_removed() {
1874 let test_values = test_setup();
1875 let mut phy_manager = PhyManager::new(
1876 test_values.monitor_proxy,
1877 recovery::lookup_recovery_profile(""),
1878 false,
1879 test_values.node,
1880 test_values.telemetry_sender,
1881 test_values.recovery_sender,
1882 );
1883
1884 let fake_phy_id = 1;
1887 let fake_mac_roles = vec![];
1888
1889 let mut phy_container = PhyContainer::new(fake_mac_roles);
1891 let fake_iface_id = 1;
1892 let _ = phy_container.client_ifaces.insert(fake_iface_id);
1893
1894 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1895
1896 phy_manager.on_iface_removed(fake_iface_id);
1897
1898 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1900 assert!(phy_container.client_ifaces.is_empty());
1901 }
1902
1903 #[run_singlethreaded(test)]
1907 async fn remove_missing_iface() {
1908 let test_values = test_setup();
1909 let mut phy_manager = PhyManager::new(
1910 test_values.monitor_proxy,
1911 recovery::lookup_recovery_profile(""),
1912 false,
1913 test_values.node,
1914 test_values.telemetry_sender,
1915 test_values.recovery_sender,
1916 );
1917
1918 let fake_phy_id = 1;
1921 let fake_mac_roles = vec![];
1922
1923 let present_iface_id = 1;
1924 let removed_iface_id = 2;
1925
1926 let mut phy_container = PhyContainer::new(fake_mac_roles);
1928 let _ = phy_container.client_ifaces.insert(present_iface_id);
1929 let _ = phy_container.client_ifaces.insert(removed_iface_id);
1930 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1931 phy_manager.on_iface_removed(removed_iface_id);
1932
1933 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1935 assert_eq!(phy_container.client_ifaces.len(), 1);
1936 assert!(phy_container.client_ifaces.contains(&present_iface_id));
1937 }
1938
1939 #[run_singlethreaded(test)]
1942 async fn get_client_no_phys() {
1943 let test_values = test_setup();
1944 let mut phy_manager = PhyManager::new(
1945 test_values.monitor_proxy,
1946 recovery::lookup_recovery_profile(""),
1947 false,
1948 test_values.node,
1949 test_values.telemetry_sender,
1950 test_values.recovery_sender,
1951 );
1952
1953 let client = phy_manager.get_client();
1954 assert!(client.is_none());
1955 }
1956
1957 #[run_singlethreaded(test)]
1961 async fn get_unconfigured_client() {
1962 let test_values = test_setup();
1963 let mut phy_manager = PhyManager::new(
1964 test_values.monitor_proxy,
1965 recovery::lookup_recovery_profile(""),
1966 false,
1967 test_values.node,
1968 test_values.telemetry_sender,
1969 test_values.recovery_sender,
1970 );
1971
1972 let fake_phy_id = 1;
1975 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1976 let phy_container = PhyContainer::new(fake_mac_roles);
1977
1978 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1979
1980 let client = phy_manager.get_client();
1982 assert!(client.is_none());
1983 }
1984
1985 #[run_singlethreaded(test)]
1989 async fn get_configured_client() {
1990 let test_values = test_setup();
1991 let mut phy_manager = PhyManager::new(
1992 test_values.monitor_proxy,
1993 recovery::lookup_recovery_profile(""),
1994 false,
1995 test_values.node,
1996 test_values.telemetry_sender,
1997 test_values.recovery_sender,
1998 );
1999 phy_manager.client_connections_enabled = true;
2000
2001 let fake_phy_id = 1;
2004 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2005 let phy_container = PhyContainer::new(fake_mac_roles);
2006
2007 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2008
2009 let fake_iface_id = 1;
2011 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2012 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2013
2014 let client = phy_manager.get_client();
2016 assert_eq!(client.unwrap(), fake_iface_id)
2017 }
2018
2019 #[run_singlethreaded(test)]
2023 async fn get_client_no_compatible_phys() {
2024 let test_values = test_setup();
2025 let mut phy_manager = PhyManager::new(
2026 test_values.monitor_proxy,
2027 recovery::lookup_recovery_profile(""),
2028 false,
2029 test_values.node,
2030 test_values.telemetry_sender,
2031 test_values.recovery_sender,
2032 );
2033
2034 let fake_iface_id = 1;
2037 let fake_phy_id = 1;
2038 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2039 let mut phy_container = PhyContainer::new(fake_mac_roles);
2040 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2041 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2042
2043 let client = phy_manager.get_client();
2045 assert!(client.is_none());
2046 }
2047
2048 #[fuchsia::test]
2051 fn get_client_while_stopped() {
2052 let _exec = TestExecutor::new();
2053 let test_values = test_setup();
2054
2055 let mut phy_manager = PhyManager::new(
2057 test_values.monitor_proxy,
2058 recovery::lookup_recovery_profile(""),
2059 false,
2060 test_values.node,
2061 test_values.telemetry_sender,
2062 test_values.recovery_sender,
2063 );
2064 assert!(!phy_manager.client_connections_enabled);
2065
2066 let fake_phy_id = 1;
2068 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2069 let mut phy_container = PhyContainer::new(fake_mac_roles);
2070 let _ = phy_container.client_ifaces.insert(1);
2071 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2072
2073 assert_eq!(phy_manager.get_client(), None);
2076 }
2077
2078 #[fuchsia::test]
2082 fn destroy_all_client_ifaces() {
2083 let mut exec = TestExecutor::new();
2084 let mut test_values = test_setup();
2085 let mut phy_manager = PhyManager::new(
2086 test_values.monitor_proxy,
2087 recovery::lookup_recovery_profile(""),
2088 false,
2089 test_values.node,
2090 test_values.telemetry_sender,
2091 test_values.recovery_sender,
2092 );
2093
2094 let fake_iface_id = 1;
2097 let fake_phy_id = 1;
2098 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2099 let phy_container = PhyContainer::new(fake_mac_roles);
2100
2101 {
2102 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2103
2104 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2106 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2107
2108 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2110 let mut stop_clients_future = pin!(stop_clients_future);
2111
2112 assert!(exec.run_until_stalled(&mut stop_clients_future).is_pending());
2113
2114 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2115
2116 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2117 }
2118
2119 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2121
2122 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2123 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
2124
2125 assert!(!phy_manager.client_connections_enabled);
2127
2128 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2130 }
2131
2132 #[fuchsia::test]
2135 fn destroy_all_client_ifaces_no_clients() {
2136 let mut exec = TestExecutor::new();
2137 let test_values = test_setup();
2138 let mut phy_manager = PhyManager::new(
2139 test_values.monitor_proxy,
2140 recovery::lookup_recovery_profile(""),
2141 false,
2142 test_values.node,
2143 test_values.telemetry_sender,
2144 test_values.recovery_sender,
2145 );
2146
2147 let fake_iface_id = 1;
2150 let fake_phy_id = 1;
2151 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2152 let phy_container = PhyContainer::new(fake_mac_roles);
2153
2154 {
2156 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2157
2158 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2160 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2161
2162 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2164 let mut stop_clients_future = pin!(stop_clients_future);
2165
2166 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2167 }
2168
2169 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2171
2172 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2173 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2174 }
2175
2176 #[fuchsia::test]
2178 fn destroy_all_client_ifaces_fails() {
2179 let mut exec = TestExecutor::new();
2180 let test_values = test_setup();
2181 let mut phy_manager = PhyManager::new(
2182 test_values.monitor_proxy,
2183 recovery::lookup_recovery_profile(""),
2184 false,
2185 test_values.node,
2186 test_values.telemetry_sender,
2187 test_values.recovery_sender,
2188 );
2189
2190 drop(test_values.monitor_stream);
2192
2193 let fake_iface_id = 1;
2196 let fake_phy_id = 1;
2197 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2198 let mut phy_container = PhyContainer::new(fake_mac_roles);
2199
2200 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2203
2204 {
2205 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2206
2207 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2209 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2210
2211 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2213 let mut stop_clients_future = pin!(stop_clients_future);
2214 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2215 }
2216
2217 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2219
2220 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2221 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2222 assert_eq!(phy_container.defects.events.len(), 1);
2223 assert_eq!(
2224 phy_container.defects.events[0].value,
2225 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2226 );
2227 }
2228
2229 #[fuchsia::test]
2232 fn get_ap_no_phys() {
2233 let mut exec = TestExecutor::new();
2234 let test_values = test_setup();
2235 let mut phy_manager = PhyManager::new(
2236 test_values.monitor_proxy,
2237 recovery::lookup_recovery_profile(""),
2238 false,
2239 test_values.node,
2240 test_values.telemetry_sender,
2241 test_values.recovery_sender,
2242 );
2243
2244 let get_ap_future = phy_manager.create_or_get_ap_iface();
2245
2246 let mut get_ap_future = pin!(get_ap_future);
2247 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2248 }
2249
2250 #[fuchsia::test]
2254 fn get_unconfigured_ap() {
2255 let mut exec = TestExecutor::new();
2256 let mut test_values = test_setup();
2257 let mut phy_manager = PhyManager::new(
2258 test_values.monitor_proxy,
2259 recovery::lookup_recovery_profile(""),
2260 false,
2261 test_values.node,
2262 test_values.telemetry_sender,
2263 test_values.recovery_sender,
2264 );
2265
2266 let fake_phy_id = 1;
2269 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2270 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2271
2272 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2273
2274 let fake_iface_id = 1;
2276 {
2277 let get_ap_future = phy_manager.create_or_get_ap_iface();
2278
2279 let mut get_ap_future = pin!(get_ap_future);
2280 assert!(exec.run_until_stalled(&mut get_ap_future).is_pending());
2281
2282 send_create_iface_response(
2283 &mut exec,
2284 &mut test_values.monitor_stream,
2285 Some(fake_iface_id),
2286 );
2287 assert_matches!(
2288 exec.run_until_stalled(&mut get_ap_future),
2289 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2290 );
2291 }
2292
2293 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.contains(&fake_iface_id));
2294 }
2295
2296 #[fuchsia::test]
2298 fn get_ap_iface_creation_fails() {
2299 let mut exec = TestExecutor::new();
2300 let test_values = test_setup();
2301 let mut phy_manager = PhyManager::new(
2302 test_values.monitor_proxy,
2303 recovery::lookup_recovery_profile(""),
2304 false,
2305 test_values.node,
2306 test_values.telemetry_sender,
2307 test_values.recovery_sender,
2308 );
2309
2310 drop(test_values.monitor_stream);
2312
2313 let fake_phy_id = 1;
2316 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2317 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2318
2319 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2322
2323 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2324
2325 {
2326 let get_ap_future = phy_manager.create_or_get_ap_iface();
2327
2328 let mut get_ap_future = pin!(get_ap_future);
2329 assert!(exec.run_until_stalled(&mut get_ap_future).is_ready());
2330 }
2331
2332 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.is_empty());
2333 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
2334 assert_eq!(
2335 phy_manager.phys[&fake_phy_id].defects.events[0].value,
2336 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
2337 );
2338 }
2339
2340 #[fuchsia::test]
2344 fn get_configured_ap() {
2345 let mut exec = TestExecutor::new();
2346 let test_values = test_setup();
2347 let mut phy_manager = PhyManager::new(
2348 test_values.monitor_proxy,
2349 recovery::lookup_recovery_profile(""),
2350 false,
2351 test_values.node,
2352 test_values.telemetry_sender,
2353 test_values.recovery_sender,
2354 );
2355
2356 let fake_phy_id = 1;
2359 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2360 let phy_container = PhyContainer::new(fake_mac_roles);
2361
2362 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2363
2364 let fake_iface_id = 1;
2366 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2367 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2368
2369 let get_ap_future = phy_manager.create_or_get_ap_iface();
2371 let mut get_ap_future = pin!(get_ap_future);
2372 assert_matches!(
2373 exec.run_until_stalled(&mut get_ap_future),
2374 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2375 );
2376 }
2377
2378 #[fuchsia::test]
2381 fn get_ap_no_compatible_phys() {
2382 let mut exec = TestExecutor::new();
2383 let test_values = test_setup();
2384 let mut phy_manager = PhyManager::new(
2385 test_values.monitor_proxy,
2386 recovery::lookup_recovery_profile(""),
2387 false,
2388 test_values.node,
2389 test_values.telemetry_sender,
2390 test_values.recovery_sender,
2391 );
2392
2393 let fake_phy_id = 1;
2396 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2397 let phy_container = PhyContainer::new(fake_mac_roles);
2398
2399 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2400
2401 let get_ap_future = phy_manager.create_or_get_ap_iface();
2403 let mut get_ap_future = pin!(get_ap_future);
2404 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2405 }
2406
2407 #[fuchsia::test]
2410 fn stop_valid_ap_iface() {
2411 let mut exec = TestExecutor::new();
2412 let mut test_values = test_setup();
2413 let mut phy_manager = PhyManager::new(
2414 test_values.monitor_proxy,
2415 recovery::lookup_recovery_profile(""),
2416 false,
2417 test_values.node,
2418 test_values.telemetry_sender,
2419 test_values.recovery_sender,
2420 );
2421
2422 let fake_iface_id = 1;
2425 let fake_phy_id = 1;
2426 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2427
2428 {
2429 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2430
2431 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2432
2433 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2435 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2436
2437 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2439 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2440 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2441 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2442
2443 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2444 }
2445
2446 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2447
2448 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2449 assert!(!phy_container.ap_ifaces.contains(&fake_iface_id));
2450 assert!(phy_container.defects.events.is_empty());
2451 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2452 }
2453
2454 #[fuchsia::test]
2457 fn stop_invalid_ap_iface() {
2458 let mut exec = TestExecutor::new();
2459 let test_values = test_setup();
2460 let mut phy_manager = PhyManager::new(
2461 test_values.monitor_proxy,
2462 recovery::lookup_recovery_profile(""),
2463 false,
2464 test_values.node,
2465 test_values.telemetry_sender,
2466 test_values.recovery_sender,
2467 );
2468
2469 let fake_iface_id = 1;
2472 let fake_phy_id = 1;
2473 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2474
2475 {
2476 let phy_container = PhyContainer::new(fake_mac_roles);
2477
2478 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2479
2480 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2482 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2483
2484 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(2);
2486 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2487 assert_matches!(
2488 exec.run_until_stalled(&mut destroy_ap_iface_future),
2489 Poll::Ready(Ok(()))
2490 );
2491 }
2492
2493 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2494
2495 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2496 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2497 assert!(phy_container.defects.events.is_empty());
2498 }
2499
2500 #[fuchsia::test]
2503 fn stop_ap_iface_fails() {
2504 let mut exec = TestExecutor::new();
2505 let test_values = test_setup();
2506 let mut phy_manager = PhyManager::new(
2507 test_values.monitor_proxy,
2508 recovery::lookup_recovery_profile(""),
2509 false,
2510 test_values.node,
2511 test_values.telemetry_sender,
2512 test_values.recovery_sender,
2513 );
2514
2515 drop(test_values.monitor_stream);
2517
2518 let fake_iface_id = 1;
2521 let fake_phy_id = 1;
2522 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2523
2524 {
2525 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2526
2527 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2530
2531 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2532
2533 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2535 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2536
2537 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2539 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2540 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2541 }
2542
2543 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2544
2545 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2546 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2547 assert_eq!(phy_container.defects.events.len(), 1);
2548 assert_eq!(
2549 phy_container.defects.events[0].value,
2550 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2551 );
2552 }
2553
2554 #[fuchsia::test]
2559 fn stop_all_ap_ifaces() {
2560 let mut exec = TestExecutor::new();
2561 let mut test_values = test_setup();
2562 let mut phy_manager = PhyManager::new(
2563 test_values.monitor_proxy,
2564 recovery::lookup_recovery_profile(""),
2565 false,
2566 test_values.node,
2567 test_values.telemetry_sender,
2568 test_values.recovery_sender,
2569 );
2570
2571 let fake_phy_id = 1;
2574 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2575
2576 {
2577 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2578
2579 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2580
2581 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2583 let _ = phy_container.ap_ifaces.insert(0);
2584 let _ = phy_container.ap_ifaces.insert(1);
2585
2586 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2588 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2589
2590 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2591 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2592
2593 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2594 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2595
2596 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2597 }
2598
2599 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2600
2601 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2602 assert!(phy_container.ap_ifaces.is_empty());
2603 assert!(phy_container.destroyed_ifaces.contains(&0));
2604 assert!(phy_container.destroyed_ifaces.contains(&1));
2605 assert!(phy_container.defects.events.is_empty());
2606 }
2607
2608 #[fuchsia::test]
2612 fn stop_all_ap_ifaces_with_client() {
2613 let mut exec = TestExecutor::new();
2614 let test_values = test_setup();
2615 let mut phy_manager = PhyManager::new(
2616 test_values.monitor_proxy,
2617 recovery::lookup_recovery_profile(""),
2618 false,
2619 test_values.node,
2620 test_values.telemetry_sender,
2621 test_values.recovery_sender,
2622 );
2623
2624 let fake_iface_id = 1;
2627 let fake_phy_id = 1;
2628 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2629
2630 {
2631 let phy_container = PhyContainer::new(fake_mac_roles);
2632
2633 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2634
2635 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2637 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2638
2639 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2641 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2642 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2643 }
2644
2645 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2646
2647 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2648 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2649 assert!(phy_container.defects.events.is_empty());
2650 }
2651
2652 #[fuchsia::test]
2654 fn stop_all_ap_ifaces_fails() {
2655 let mut exec = TestExecutor::new();
2656 let test_values = test_setup();
2657 let mut phy_manager = PhyManager::new(
2658 test_values.monitor_proxy,
2659 recovery::lookup_recovery_profile(""),
2660 false,
2661 test_values.node,
2662 test_values.telemetry_sender,
2663 test_values.recovery_sender,
2664 );
2665
2666 drop(test_values.monitor_stream);
2668
2669 let fake_phy_id = 1;
2672 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2673
2674 {
2675 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2676
2677 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2680
2681 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2682
2683 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2685 let _ = phy_container.ap_ifaces.insert(0);
2686 let _ = phy_container.ap_ifaces.insert(1);
2687
2688 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2690 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2691 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2692 }
2693
2694 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2695
2696 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2697 assert_eq!(phy_container.ap_ifaces.len(), 2);
2698 assert_eq!(phy_container.defects.events.len(), 2);
2699 assert_eq!(
2700 phy_container.defects.events[0].value,
2701 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2702 );
2703 assert_eq!(
2704 phy_container.defects.events[1].value,
2705 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2706 );
2707 }
2708
2709 #[fuchsia::test]
2713 fn test_suggest_ap_mac() {
2714 let mut exec = TestExecutor::new();
2715 let mut test_values = test_setup();
2716 let mut phy_manager = PhyManager::new(
2717 test_values.monitor_proxy,
2718 recovery::lookup_recovery_profile(""),
2719 false,
2720 test_values.node,
2721 test_values.telemetry_sender,
2722 test_values.recovery_sender,
2723 );
2724
2725 let fake_iface_id = 1;
2728 let fake_phy_id = 1;
2729 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2730 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2731
2732 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2733
2734 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2736 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2737
2738 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
2740 phy_manager.suggest_ap_mac(mac);
2741
2742 let get_ap_future = phy_manager.create_or_get_ap_iface();
2743 let mut get_ap_future = pin!(get_ap_future);
2744 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Pending);
2745
2746 assert_matches!(
2748 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
2749 Poll::Ready(Some(Ok(
2750 fidl_service::DeviceMonitorRequest::CreateIface {
2751 payload,
2752 responder,
2753 }
2754 ))) => {
2755 let requested_mac: MacAddr = payload.sta_address.unwrap().into();
2756 assert_eq!(requested_mac, mac);
2757 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
2758 iface_id: Some(fake_iface_id),
2759 ..Default::default()
2760 };
2761 responder.send(Ok(&response)).expect("sending fake iface id");
2762 }
2763 );
2764 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(_));
2765 }
2766
2767 #[fuchsia::test]
2768 fn test_suggested_mac_does_not_apply_to_client() {
2769 let mut exec = TestExecutor::new();
2770 let mut test_values = test_setup();
2771 let mut phy_manager = PhyManager::new(
2772 test_values.monitor_proxy,
2773 recovery::lookup_recovery_profile(""),
2774 false,
2775 test_values.node,
2776 test_values.telemetry_sender,
2777 test_values.recovery_sender,
2778 );
2779
2780 let fake_iface_id = 1;
2783 let fake_phy_id = 1;
2784 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2785 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2786
2787 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2788
2789 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
2791 phy_manager.suggest_ap_mac(mac);
2792
2793 let start_client_future =
2795 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2796 let mut start_client_future = pin!(start_client_future);
2797 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Pending);
2798
2799 assert_matches!(
2801 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
2802 Poll::Ready(Some(Ok(
2803 fidl_service::DeviceMonitorRequest::CreateIface {
2804 payload,
2805 responder,
2806 }
2807 ))) => {
2808 assert_eq!(payload.sta_address, Some(ieee80211::NULL_ADDR.to_array()));
2809 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
2810 iface_id: Some(fake_iface_id),
2811 ..Default::default()
2812 };
2813 responder.send(Ok(&response)).expect("sending fake iface id");
2814 }
2815 );
2816 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Ready(_));
2817 }
2818
2819 #[fuchsia::test]
2821 fn test_iface_creation_fails_during_start_client_connections() {
2822 let mut exec = TestExecutor::new();
2823 let test_values = test_setup();
2824 let mut phy_manager = PhyManager::new(
2825 test_values.monitor_proxy,
2826 recovery::lookup_recovery_profile(""),
2827 false,
2828 test_values.node,
2829 test_values.telemetry_sender,
2830 test_values.recovery_sender,
2831 );
2832
2833 drop(test_values.monitor_stream);
2835
2836 let fake_phy_id = 1;
2839 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2840 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2841
2842 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2845
2846 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2847
2848 {
2849 let start_client_future = phy_manager
2851 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2852 let mut start_client_future = pin!(start_client_future);
2853 assert!(exec.run_until_stalled(&mut start_client_future).is_ready());
2854 }
2855
2856 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
2858 assert_eq!(
2859 phy_manager.phys[&fake_phy_id].defects.events[0].value,
2860 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
2861 );
2862 }
2863
2864 #[fuchsia::test]
2865 fn test_all_iface_creation_failures_retained_across_multiple_phys() {
2866 let mut exec = TestExecutor::new();
2867 let test_values = test_setup();
2868 let mut phy_manager = PhyManager::new(
2869 test_values.monitor_proxy,
2870 recovery::lookup_recovery_profile(""),
2871 false,
2872 test_values.node,
2873 test_values.telemetry_sender,
2874 test_values.recovery_sender,
2875 );
2876
2877 drop(test_values.monitor_stream);
2879
2880 for fake_phy_id in 0..2 {
2883 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2884 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2885
2886 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2889
2890 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2891 }
2892
2893 let start_client_future =
2894 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2895 let mut start_client_future = pin!(start_client_future);
2896 assert_matches!(exec.run_until_stalled(&mut start_client_future),
2897 Poll::Ready(iface_ids) => {
2898 assert_eq!(iface_ids.len(), 2);
2899 assert_eq!(iface_ids[&0], Err(PhyManagerError::IfaceCreateFailure));
2900 assert_eq!(iface_ids[&1], Err(PhyManagerError::IfaceCreateFailure));
2901 }
2902 );
2903 }
2904
2905 #[run_singlethreaded(test)]
2909 async fn get_phy_ids_no_phys() {
2910 let test_values = test_setup();
2911 let phy_manager = PhyManager::new(
2912 test_values.monitor_proxy,
2913 recovery::lookup_recovery_profile(""),
2914 false,
2915 test_values.node,
2916 test_values.telemetry_sender,
2917 test_values.recovery_sender,
2918 );
2919 assert_eq!(phy_manager.get_phy_ids(), Vec::<u16>::new());
2920 }
2921
2922 #[fuchsia::test]
2925 fn get_phy_ids_single_phy() {
2926 let mut exec = TestExecutor::new();
2927 let mut test_values = test_setup();
2928 let mut phy_manager = PhyManager::new(
2929 test_values.monitor_proxy,
2930 recovery::lookup_recovery_profile(""),
2931 false,
2932 test_values.node,
2933 test_values.telemetry_sender,
2934 test_values.recovery_sender,
2935 );
2936
2937 {
2938 let add_phy_fut = phy_manager.add_phy(1);
2939 let mut add_phy_fut = pin!(add_phy_fut);
2940 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2941 send_get_supported_mac_roles_response(
2942 &mut exec,
2943 &mut test_values.monitor_stream,
2944 Ok(&[]),
2945 );
2946 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2947 }
2948
2949 assert_eq!(phy_manager.get_phy_ids(), vec![1]);
2950 }
2951
2952 #[fuchsia::test]
2955 fn get_phy_ids_two_phys() {
2956 let mut exec = TestExecutor::new();
2957 let mut test_values = test_setup();
2958 let mut phy_manager = PhyManager::new(
2959 test_values.monitor_proxy,
2960 recovery::lookup_recovery_profile(""),
2961 false,
2962 test_values.node,
2963 test_values.telemetry_sender,
2964 test_values.recovery_sender,
2965 );
2966
2967 {
2968 let add_phy_fut = phy_manager.add_phy(1);
2969 let mut add_phy_fut = pin!(add_phy_fut);
2970 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2971 send_get_supported_mac_roles_response(
2972 &mut exec,
2973 &mut test_values.monitor_stream,
2974 Ok(&[]),
2975 );
2976 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2977 }
2978
2979 {
2980 let add_phy_fut = phy_manager.add_phy(2);
2981 let mut add_phy_fut = pin!(add_phy_fut);
2982 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2983 send_get_supported_mac_roles_response(
2984 &mut exec,
2985 &mut test_values.monitor_stream,
2986 Ok(&[]),
2987 );
2988 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2989 }
2990
2991 let phy_ids = phy_manager.get_phy_ids();
2992 assert!(phy_ids.contains(&1), "expected phy_ids to contain `1`, but phy_ids={phy_ids:?}");
2993 assert!(phy_ids.contains(&2), "expected phy_ids to contain `2`, but phy_ids={phy_ids:?}");
2994 }
2995
2996 #[run_singlethreaded(test)]
2998 async fn log_phy_add_failure() {
2999 let test_values = test_setup();
3000 let mut phy_manager = PhyManager::new(
3001 test_values.monitor_proxy,
3002 recovery::lookup_recovery_profile(""),
3003 false,
3004 test_values.node,
3005 test_values.telemetry_sender,
3006 test_values.recovery_sender,
3007 );
3008
3009 assert_data_tree!(test_values.inspector, root: {
3010 phy_manager: {
3011 phy_add_fail_count: 0u64,
3012 },
3013 });
3014
3015 phy_manager.log_phy_add_failure();
3016 assert_data_tree!(test_values.inspector, root: {
3017 phy_manager: {
3018 phy_add_fail_count: 1u64,
3019 },
3020 });
3021 }
3022
3023 #[fuchsia::test]
3026 fn test_set_country_code() {
3027 let mut exec = TestExecutor::new();
3028 let mut test_values = test_setup();
3029 let mut phy_manager = PhyManager::new(
3030 test_values.monitor_proxy,
3031 recovery::lookup_recovery_profile(""),
3032 false,
3033 test_values.node,
3034 test_values.telemetry_sender,
3035 test_values.recovery_sender,
3036 );
3037
3038 let _ = phy_manager.phys.insert(
3040 0,
3041 PhyContainer {
3042 supported_mac_roles: HashSet::new(),
3043 client_ifaces: HashSet::new(),
3044 ap_ifaces: HashSet::new(),
3045 destroyed_ifaces: HashSet::new(),
3046 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3047 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3048 },
3049 );
3050 let _ = phy_manager.phys.insert(
3051 1,
3052 PhyContainer {
3053 supported_mac_roles: HashSet::new(),
3054 client_ifaces: HashSet::new(),
3055 ap_ifaces: HashSet::new(),
3056 destroyed_ifaces: HashSet::new(),
3057 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3058 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3059 },
3060 );
3061
3062 assert!(phy_manager.saved_country_code.is_none());
3064
3065 {
3067 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3068 let mut set_country_fut = pin!(set_country_fut);
3069
3070 for _ in 0..2 {
3072 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3073 assert_matches!(
3074 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3075 Poll::Ready(Some(Ok(
3076 fidl_service::DeviceMonitorRequest::SetCountry {
3077 req: fidl_service::SetCountryRequest {
3078 phy_id: _,
3079 alpha2: [b'U', b'S'],
3080 },
3081 responder,
3082 }
3083 ))) => {
3084 responder.send(ZX_OK).expect("sending fake set country response");
3085 }
3086 );
3087 }
3088
3089 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3090 }
3091 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3092
3093 {
3096 let set_country_fut = phy_manager.set_country_code(None);
3097 let mut set_country_fut = pin!(set_country_fut);
3098
3099 for _ in 0..2 {
3101 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3102 assert_matches!(
3103 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3104 Poll::Ready(Some(Ok(
3105 fidl_service::DeviceMonitorRequest::ClearCountry {
3106 req: fidl_service::ClearCountryRequest {
3107 phy_id: _,
3108 },
3109 responder,
3110 }
3111 ))) => {
3112 responder.send(ZX_OK).expect("sending fake clear country response");
3113 }
3114 );
3115 }
3116
3117 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3118 }
3119 assert_eq!(phy_manager.saved_country_code, None);
3120 }
3121
3122 #[fuchsia::test]
3124 fn test_setting_country_code_fails() {
3125 let mut exec = TestExecutor::new();
3126 let mut test_values = test_setup();
3127 let mut phy_manager = PhyManager::new(
3128 test_values.monitor_proxy,
3129 recovery::lookup_recovery_profile(""),
3130 false,
3131 test_values.node,
3132 test_values.telemetry_sender,
3133 test_values.recovery_sender,
3134 );
3135
3136 let _ = phy_manager.phys.insert(
3138 0,
3139 PhyContainer {
3140 supported_mac_roles: HashSet::new(),
3141 client_ifaces: HashSet::new(),
3142 ap_ifaces: HashSet::new(),
3143 destroyed_ifaces: HashSet::new(),
3144 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3145 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3146 },
3147 );
3148
3149 assert!(phy_manager.saved_country_code.is_none());
3151
3152 {
3154 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3155 let mut set_country_fut = pin!(set_country_fut);
3156
3157 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3158 assert_matches!(
3159 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3160 Poll::Ready(Some(Ok(
3161 fidl_service::DeviceMonitorRequest::SetCountry {
3162 req: fidl_service::SetCountryRequest {
3163 phy_id: 0,
3164 alpha2: [b'U', b'S'],
3165 },
3166 responder,
3167 }
3168 ))) => {
3169 responder
3171 .send(zx::sys::ZX_ERR_NOT_SUPPORTED)
3172 .expect("sending fake set country response");
3173 }
3174 );
3175
3176 assert_matches!(
3177 exec.run_until_stalled(&mut set_country_fut),
3178 Poll::Ready(Err(PhyManagerError::PhySetCountryFailure))
3179 );
3180 }
3181 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3182 }
3183
3184 #[fuchsia::test]
3186 fn test_recover_client_interfaces_succeeds() {
3187 let mut exec = TestExecutor::new();
3188 let mut test_values = test_setup();
3189 let mut phy_manager = PhyManager::new(
3190 test_values.monitor_proxy,
3191 recovery::lookup_recovery_profile(""),
3192 false,
3193 test_values.node,
3194 test_values.telemetry_sender,
3195 test_values.recovery_sender,
3196 );
3197 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3198
3199 phy_manager.client_connections_enabled = true;
3201
3202 for phy_id in 0..4 {
3205 let fake_mac_roles = fake_mac_roles.clone();
3206 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3207
3208 if phy_id.is_multiple_of(2) {
3210 let phy_container = phy_manager.phys.get_mut(&phy_id).expect("missing PHY");
3211 let _ = phy_container.client_ifaces.insert(phy_id);
3212 }
3213 }
3214
3215 {
3219 let recovery_fut =
3220 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3221 let mut recovery_fut = pin!(recovery_fut);
3222 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3223
3224 loop {
3225 match exec.run_until_stalled(&mut recovery_fut) {
3229 Poll::Pending => {}
3230 Poll::Ready(iface_ids) => {
3231 if iface_ids.values().any(Result::is_err) {
3232 panic!("recovery failed unexpectedly");
3233 }
3234 let iface_ids: Vec<_> =
3235 iface_ids.into_values().flat_map(Result::unwrap).collect();
3236 assert!(iface_ids.contains(&1));
3237 assert!(iface_ids.contains(&3));
3238 break;
3239 }
3240 }
3241
3242 assert_matches!(
3245 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3246 Poll::Ready(Some(Ok(
3247 fidl_service::DeviceMonitorRequest::CreateIface {
3248 payload,
3249 responder,
3250 }
3251 ))) => {
3252 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3253 iface_id: Some(payload.phy_id.unwrap()),
3254 ..Default::default()
3255 };
3256 responder.send(Ok(&response)).expect("sending fake iface id");
3257 });
3258 }
3259 }
3260
3261 for phy_id in phy_manager.phys.keys() {
3264 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3265 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3266 }
3267 }
3268
3269 #[fuchsia::test]
3271 fn test_recover_client_interfaces_fails() {
3272 let mut exec = TestExecutor::new();
3273 let mut test_values = test_setup();
3274 let mut phy_manager = PhyManager::new(
3275 test_values.monitor_proxy,
3276 recovery::lookup_recovery_profile(""),
3277 false,
3278 test_values.node,
3279 test_values.telemetry_sender,
3280 test_values.recovery_sender,
3281 );
3282 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3283
3284 phy_manager.client_connections_enabled = true;
3286
3287 for phy_id in 0..3 {
3292 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3293 }
3294
3295 {
3297 let recovery_fut =
3298 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3299 let mut recovery_fut = pin!(recovery_fut);
3300 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3301
3302 loop {
3303 match exec.run_until_stalled(&mut recovery_fut) {
3304 Poll::Pending => {}
3305 Poll::Ready(iface_ids) => {
3306 assert!(iface_ids.values().any(Result::is_err));
3307 let iface_ids: Vec<_> =
3308 iface_ids.into_values().filter_map(Result::ok).flatten().collect();
3309 assert_eq!(iface_ids, vec![1]);
3310 break;
3311 }
3312 }
3313
3314 assert_matches!(
3317 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3318 Poll::Ready(Some(Ok(
3319 fidl_service::DeviceMonitorRequest::CreateIface {
3320 payload,
3321 responder,
3322 }
3323 ))) => {
3324 let iface_id = payload.phy_id.unwrap();
3325 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3326 iface_id: Some(iface_id),
3327 ..Default::default()
3328 };
3329
3330 match payload.phy_id.unwrap() {
3333 1 => {
3334 responder.send(Ok(&response)).expect("sending fake iface id")
3335 },
3336 _ => responder.send(Err(fidl_service::DeviceMonitorError::unknown())).expect("sending fake iface id"),
3337 };
3338 }
3339 );
3340 }
3341 }
3342
3343 for phy_id in phy_manager.phys.keys() {
3345 match phy_id {
3346 1 => {
3347 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3348 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3349 }
3350 _ => assert!(phy_manager.phys[phy_id].client_ifaces.is_empty()),
3351 }
3352 }
3353 }
3354
3355 #[fuchsia::test]
3358 fn test_recover_client_interfaces_while_disabled() {
3359 let mut exec = TestExecutor::new();
3360 let test_values = test_setup();
3361 let mut phy_manager = PhyManager::new(
3362 test_values.monitor_proxy,
3363 recovery::lookup_recovery_profile(""),
3364 false,
3365 test_values.node,
3366 test_values.telemetry_sender,
3367 test_values.recovery_sender,
3368 );
3369
3370 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3373 let _ = phy_manager.phys.insert(0, PhyContainer::new(fake_mac_roles));
3374
3375 {
3378 let recovery_fut =
3379 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3380 let mut recovery_fut = pin!(recovery_fut);
3381 assert_matches!(
3382 exec.run_until_stalled(&mut recovery_fut),
3383 Poll::Ready(recovered_ifaces) => {
3384 assert!(recovered_ifaces.is_empty());
3385 }
3386 );
3387 }
3388
3389 for (_, phy_container) in phy_manager.phys {
3391 assert!(phy_container.client_ifaces.is_empty());
3392 }
3393 }
3394
3395 #[fuchsia::test]
3398 fn test_start_after_unsuccessful_stop() {
3399 let mut exec = TestExecutor::new();
3400 let test_values = test_setup();
3401 let mut phy_manager = PhyManager::new(
3402 test_values.monitor_proxy,
3403 recovery::lookup_recovery_profile(""),
3404 false,
3405 test_values.node,
3406 test_values.telemetry_sender,
3407 test_values.recovery_sender,
3408 );
3409
3410 assert!(!phy_manager.client_connections_enabled);
3412
3413 let fake_phy_id = 1;
3415 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3416 let mut phy_container = PhyContainer::new(fake_mac_roles);
3417 let fake_iface_id = 1;
3419 let _ = phy_container.client_ifaces.insert(fake_iface_id);
3420 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3421
3422 {
3425 let start_client_future =
3426 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3427 let mut start_client_future = pin!(start_client_future);
3428 assert_matches!(
3429 exec.run_until_stalled(&mut start_client_future),
3430 Poll::Ready(v) => {
3431 assert!(v.is_empty())
3432 });
3433 }
3434
3435 {
3438 let start_client_future = phy_manager
3439 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3440 let mut start_client_future = pin!(start_client_future);
3441 assert_matches!(
3442 exec.run_until_stalled(&mut start_client_future),
3443 Poll::Ready(iface_ids) => {
3444 assert_eq!(iface_ids.into_values().collect::<Vec<_>>(), vec![Ok(vec![1])]);
3445 }
3446 );
3447 }
3448 }
3449
3450 #[fuchsia::test]
3452 fn test_client_connections_enabled_when_enabled() {
3453 let _exec = TestExecutor::new();
3454 let test_values = test_setup();
3455 let mut phy_manager = PhyManager::new(
3456 test_values.monitor_proxy,
3457 recovery::lookup_recovery_profile(""),
3458 false,
3459 test_values.node,
3460 test_values.telemetry_sender,
3461 test_values.recovery_sender,
3462 );
3463
3464 phy_manager.client_connections_enabled = true;
3465 assert!(phy_manager.client_connections_enabled());
3466 }
3467
3468 #[fuchsia::test]
3470 fn test_client_connections_enabled_when_disabled() {
3471 let _exec = TestExecutor::new();
3472 let test_values = test_setup();
3473 let mut phy_manager = PhyManager::new(
3474 test_values.monitor_proxy,
3475 recovery::lookup_recovery_profile(""),
3476 false,
3477 test_values.node,
3478 test_values.telemetry_sender,
3479 test_values.recovery_sender,
3480 );
3481
3482 phy_manager.client_connections_enabled = false;
3483 assert!(!phy_manager.client_connections_enabled());
3484 }
3485
3486 #[fuchsia::test]
3487 fn test_create_iface_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 );
3498
3499 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3501 let mut fut = pin!(fut);
3502
3503 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3505
3506 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, Some(0));
3508
3509 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(0)));
3511
3512 assert_matches!(
3514 test_values.telemetry_receiver.try_next(),
3515 Ok(Some(TelemetryEvent::IfaceCreationResult(Ok(()))))
3516 )
3517 }
3518
3519 #[fuchsia::test]
3520 fn test_create_iface_fails() {
3521 let mut exec = TestExecutor::new();
3522 let mut test_values = test_setup();
3523 let mut phy_manager = PhyManager::new(
3524 test_values.monitor_proxy,
3525 recovery::lookup_recovery_profile(""),
3526 false,
3527 test_values.node,
3528 test_values.telemetry_sender,
3529 test_values.recovery_sender,
3530 );
3531 let mut phy_container = PhyContainer::new(vec![]);
3532 let _ = phy_container.client_ifaces.insert(0);
3533 let _ = phy_manager.phys.insert(0, phy_container);
3534
3535 {
3536 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3538 let mut fut = pin!(fut);
3539
3540 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3542
3543 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
3545
3546 assert_matches!(
3548 exec.run_until_stalled(&mut fut),
3549 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3550 );
3551
3552 assert_matches!(
3554 test_values.telemetry_receiver.try_next(),
3555 Ok(Some(TelemetryEvent::IfaceCreationResult(Err(()))))
3556 );
3557 }
3558
3559 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3561 assert_eq!(
3562 phy_manager.phys[&0].defects.events[0].value,
3563 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3564 );
3565 }
3566
3567 #[fuchsia::test]
3568 fn test_create_iface_request_fails() {
3569 let mut exec = TestExecutor::new();
3570 let mut test_values = test_setup();
3571 let mut phy_manager = PhyManager::new(
3572 test_values.monitor_proxy,
3573 recovery::lookup_recovery_profile(""),
3574 false,
3575 test_values.node,
3576 test_values.telemetry_sender,
3577 test_values.recovery_sender,
3578 );
3579 let mut phy_container = PhyContainer::new(vec![]);
3580 let _ = phy_container.client_ifaces.insert(0);
3581 let _ = phy_manager.phys.insert(0, phy_container);
3582
3583 drop(test_values.monitor_stream);
3584
3585 {
3586 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3588 let mut fut = pin!(fut);
3589
3590 assert_matches!(
3592 exec.run_until_stalled(&mut fut),
3593 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3594 );
3595
3596 assert_matches!(
3598 test_values.telemetry_receiver.try_next(),
3599 Ok(Some(TelemetryEvent::IfaceCreationResult(Err(()))))
3600 );
3601 }
3602
3603 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3605 assert_eq!(
3606 phy_manager.phys[&0].defects.events[0].value,
3607 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3608 );
3609 }
3610
3611 #[fuchsia::test]
3612 fn test_destroy_iface_succeeds() {
3613 let mut exec = TestExecutor::new();
3614 let mut test_values = test_setup();
3615
3616 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3618 let mut fut = pin!(fut);
3619
3620 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3622
3623 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
3625
3626 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3628
3629 assert_matches!(
3631 test_values.telemetry_receiver.try_next(),
3632 Ok(Some(TelemetryEvent::IfaceDestructionResult(Ok(()))))
3633 )
3634 }
3635
3636 #[fuchsia::test]
3637 fn test_destroy_iface_not_found() {
3638 let mut exec = TestExecutor::new();
3639 let mut test_values = test_setup();
3640
3641 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3643 let mut fut = pin!(fut);
3644
3645 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3647
3648 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_ERR_NOT_FOUND);
3650
3651 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3653
3654 assert_matches!(test_values.telemetry_receiver.try_next(), Err(_))
3656 }
3657
3658 #[fuchsia::test]
3659 fn test_destroy_iface_fails() {
3660 let mut exec = TestExecutor::new();
3661 let mut test_values = test_setup();
3662
3663 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3665 let mut fut = pin!(fut);
3666
3667 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3669
3670 send_destroy_iface_response(
3672 &mut exec,
3673 &mut test_values.monitor_stream,
3674 zx::sys::ZX_ERR_NO_RESOURCES,
3675 );
3676
3677 assert_matches!(
3679 exec.run_until_stalled(&mut fut),
3680 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
3681 );
3682
3683 assert_matches!(
3685 test_values.telemetry_receiver.try_next(),
3686 Ok(Some(TelemetryEvent::IfaceDestructionResult(Err(()))))
3687 )
3688 }
3689
3690 #[fuchsia::test]
3691 fn test_destroy_iface_request_fails() {
3692 let mut exec = TestExecutor::new();
3693 let mut test_values = test_setup();
3694
3695 drop(test_values.monitor_stream);
3696
3697 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3699 let mut fut = pin!(fut);
3700
3701 assert_matches!(
3703 exec.run_until_stalled(&mut fut),
3704 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
3705 );
3706
3707 assert_matches!(
3709 test_values.telemetry_receiver.try_next(),
3710 Ok(Some(TelemetryEvent::IfaceDestructionResult(Err(()))))
3711 )
3712 }
3713
3714 #[fuchsia::test]
3716 fn test_record_iface_event() {
3717 let _exec = TestExecutor::new();
3718 let test_values = test_setup();
3719
3720 let mut phy_manager = PhyManager::new(
3721 test_values.monitor_proxy,
3722 recovery::lookup_recovery_profile(""),
3723 false,
3724 test_values.node,
3725 test_values.telemetry_sender,
3726 test_values.recovery_sender,
3727 );
3728
3729 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3731 let _ = phy_manager.phys.insert(1, PhyContainer::new(vec![]));
3732 let _ = phy_manager.phys.insert(2, PhyContainer::new(vec![]));
3733 let _ = phy_manager.phys.insert(3, PhyContainer::new(vec![]));
3734
3735 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
3737 let _ = phy_manager.phys.get_mut(&1).expect("missing PHY").client_ifaces.insert(456);
3738 let _ = phy_manager.phys.get_mut(&2).expect("missing PHY").client_ifaces.insert(789);
3739 let _ = phy_manager.phys.get_mut(&3).expect("missing PHY").ap_ifaces.insert(246);
3740
3741 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3743 phy_manager.phys.get_mut(&1).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3744 phy_manager.phys.get_mut(&2).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3745 phy_manager.phys.get_mut(&3).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3746
3747 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
3749 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }));
3750 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 }));
3751 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
3752
3753 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 }));
3755
3756 assert_eq!(phy_manager.phys[&0].defects.events.len(), 2);
3758 assert_eq!(
3759 phy_manager.phys[&0].defects.events[0].value,
3760 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 })
3761 );
3762 assert_eq!(
3763 phy_manager.phys[&0].defects.events[1].value,
3764 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 })
3765 );
3766 assert_eq!(phy_manager.phys[&1].defects.events.len(), 1);
3767 assert_eq!(
3768 phy_manager.phys[&1].defects.events[0].value,
3769 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 })
3770 );
3771 assert_eq!(phy_manager.phys[&2].defects.events.len(), 1);
3772 assert_eq!(
3773 phy_manager.phys[&2].defects.events[0].value,
3774 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 })
3775 );
3776 assert_eq!(phy_manager.phys[&3].defects.events.len(), 1);
3777 assert_eq!(
3778 phy_manager.phys[&3].defects.events[0].value,
3779 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 })
3780 );
3781 }
3782
3783 #[fuchsia::test]
3785 fn test_aps_do_not_record_client_defects() {
3786 let _exec = TestExecutor::new();
3787 let test_values = test_setup();
3788
3789 let mut phy_manager = PhyManager::new(
3790 test_values.monitor_proxy,
3791 recovery::lookup_recovery_profile(""),
3792 false,
3793 test_values.node,
3794 test_values.telemetry_sender,
3795 test_values.recovery_sender,
3796 );
3797
3798 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3800
3801 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").ap_ifaces.insert(123);
3803
3804 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3806
3807 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
3809 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }));
3810 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 123 }));
3811 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
3812
3813 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
3815 }
3816
3817 #[fuchsia::test]
3819 fn test_clients_do_not_record_ap_defects() {
3820 let _exec = TestExecutor::new();
3821 let test_values = test_setup();
3822
3823 let mut phy_manager = PhyManager::new(
3824 test_values.monitor_proxy,
3825 recovery::lookup_recovery_profile(""),
3826 false,
3827 test_values.node,
3828 test_values.telemetry_sender,
3829 test_values.recovery_sender,
3830 );
3831
3832 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3834
3835 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
3837
3838 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3840
3841 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }));
3843
3844 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
3846 }
3847
3848 fn aggressive_test_recovery_profile(
3849 _phy_id: u16,
3850 _defect_history: &mut EventHistory<Defect>,
3851 _recovery_history: &mut EventHistory<RecoveryAction>,
3852 _latest_defect: Defect,
3853 ) -> Option<RecoveryAction> {
3854 Some(RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 }))
3855 }
3856
3857 #[test_case(
3858 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
3859 "recommend AP start recovery"
3860 )]
3861 #[test_case(
3862 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
3863 "recommend connection failure recovery"
3864 )]
3865 #[test_case(
3866 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
3867 "recommend empty scan recovery"
3868 )]
3869 #[test_case(
3870 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
3871 "recommend failed scan recovery"
3872 )]
3873 #[test_case(
3874 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
3875 "recommend canceled scan recovery"
3876 )]
3877 #[test_case(
3878 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
3879 "recommend iface destruction recovery"
3880 )]
3881 #[test_case(
3882 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
3883 "recommend iface creation recovery"
3884 )]
3885 #[fuchsia::test(add_test_attr = false)]
3886 fn test_recovery_action_sent_from_record_defect(defect: Defect) {
3887 let _exec = TestExecutor::new();
3888 let mut test_values = test_setup();
3889 let mut phy_manager = PhyManager::new(
3890 test_values.monitor_proxy,
3891 recovery::lookup_recovery_profile(""),
3892 false,
3893 test_values.node,
3894 test_values.telemetry_sender,
3895 test_values.recovery_sender,
3896 );
3897
3898 let mut phy_container = PhyContainer::new(vec![]);
3900 let _ = phy_container.ap_ifaces.insert(123);
3901 let _ = phy_container.client_ifaces.insert(456);
3902 let _ = phy_manager.phys.insert(0, phy_container);
3903
3904 phy_manager.recovery_profile = aggressive_test_recovery_profile;
3906
3907 phy_manager.record_defect(defect);
3909
3910 let recovery_action = test_values.recovery_receiver.try_next().unwrap().unwrap();
3912 assert_eq!(recovery_action.defect, defect);
3913 assert_eq!(
3914 recovery_action.action,
3915 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3916 );
3917 }
3918
3919 #[test_case(
3920 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
3921 "do not recommend AP start recovery"
3922 )]
3923 #[test_case(
3924 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
3925 "do not recommend connection failure recovery"
3926 )]
3927 #[test_case(
3928 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
3929 "do not recommend empty scan recovery"
3930 )]
3931 #[test_case(
3932 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
3933 "do not recommend failed scan recovery"
3934 )]
3935 #[test_case(
3936 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
3937 "do not recommend canceled scan recovery"
3938 )]
3939 #[test_case(
3940 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
3941 "do not recommend iface destruction recovery"
3942 )]
3943 #[test_case(
3944 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
3945 "do not recommend iface creation recovery"
3946 )]
3947 #[fuchsia::test(add_test_attr = false)]
3948 fn test_no_recovery_when_defect_contains_bad_ids(defect: Defect) {
3949 let _exec = TestExecutor::new();
3950 let mut test_values = test_setup();
3951
3952 let mut phy_manager = PhyManager::new(
3954 test_values.monitor_proxy,
3955 recovery::lookup_recovery_profile(""),
3956 false,
3957 test_values.node,
3958 test_values.telemetry_sender,
3959 test_values.recovery_sender,
3960 );
3961
3962 phy_manager.recovery_profile = aggressive_test_recovery_profile;
3964
3965 phy_manager.record_defect(defect);
3967
3968 assert!(test_values.recovery_receiver.try_next().is_err());
3970 }
3971
3972 #[test_case(
3973 recovery::RecoverySummary {
3974 defect: Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 0 }),
3975 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3976 },
3977 telemetry::RecoveryReason::ScanResultsEmpty(
3978 telemetry::ClientRecoveryMechanism::PhyReset
3979 ) ;
3980 "PHY reset for empty scan results"
3981 )]
3982 #[test_case(
3983 recovery::RecoverySummary {
3984 defect: Defect::Iface(IfaceFailure::CanceledScan { iface_id: 0 }),
3985 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3986 },
3987 telemetry::RecoveryReason::ScanCancellation(
3988 telemetry::ClientRecoveryMechanism::PhyReset
3989 ) ;
3990 "PHY reset for scan cancellation"
3991 )]
3992 #[test_case(
3993 recovery::RecoverySummary {
3994 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 0 }),
3995 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3996 },
3997 telemetry::RecoveryReason::ScanFailure(
3998 telemetry::ClientRecoveryMechanism::PhyReset
3999 ) ;
4000 "PHY reset for scan failure"
4001 )]
4002 #[test_case(
4003 recovery::RecoverySummary {
4004 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 0 }),
4005 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4006 },
4007 telemetry::RecoveryReason::StartApFailure(
4008 telemetry::ApRecoveryMechanism::ResetPhy
4009 ) ;
4010 "PHY reset for start AP failure"
4011 )]
4012 #[test_case(
4013 recovery::RecoverySummary {
4014 defect: Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 0 }),
4015 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4016 },
4017 telemetry::RecoveryReason::ConnectFailure(
4018 telemetry::ClientRecoveryMechanism::PhyReset
4019 ) ;
4020 "PHY reset for connection failure"
4021 )]
4022 #[test_case(
4023 recovery::RecoverySummary {
4024 defect: Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }),
4025 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4026 },
4027 telemetry::RecoveryReason::DestroyIfaceFailure(
4028 telemetry::PhyRecoveryMechanism::PhyReset
4029 ) ;
4030 "PHY reset for iface destruction failure"
4031 )]
4032 #[test_case(
4033 recovery::RecoverySummary {
4034 defect: Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }),
4035 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4036 },
4037 telemetry::RecoveryReason::CreateIfaceFailure(
4038 telemetry::PhyRecoveryMechanism::PhyReset
4039 ) ;
4040 "PHY reset for iface creation failure"
4041 )]
4042 #[fuchsia::test(add_test_attr = false)]
4043 fn test_log_recovery_action_sends_metrics(
4044 summary: recovery::RecoverySummary,
4045 expected_reason: telemetry::RecoveryReason,
4046 ) {
4047 let _exec = TestExecutor::new();
4048 let mut test_values = test_setup();
4049 let mut phy_manager = PhyManager::new(
4050 test_values.monitor_proxy,
4051 recovery::lookup_recovery_profile(""),
4052 false,
4053 test_values.node,
4054 test_values.telemetry_sender,
4055 test_values.recovery_sender,
4056 );
4057
4058 phy_manager.log_recovery_action(summary);
4060 assert_matches!(
4061 test_values.telemetry_receiver.try_next(),
4062 Ok(Some(TelemetryEvent::RecoveryEvent { reason } )) => {
4063 assert_eq!(reason, expected_reason);
4064 })
4065 }
4066
4067 #[test_case(
4068 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 456 }) ;
4069 "recommend AP start recovery"
4070 )]
4071 #[test_case(
4072 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
4073 "recommend connection failure recovery"
4074 )]
4075 #[test_case(
4076 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
4077 "recommend empty scan recovery"
4078 )]
4079 #[test_case(
4080 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
4081 "recommend failed scan recovery"
4082 )]
4083 #[test_case(
4084 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
4085 "recommend canceled scan recovery"
4086 )]
4087 #[fuchsia::test(add_test_attr = false)]
4088 fn log_defect_for_destroyed_iface(defect: Defect) {
4089 let _exec = TestExecutor::new();
4090 let test_values = test_setup();
4091
4092 let fake_phy_id = 123;
4093 let fake_iface_id = 456;
4094
4095 let mut phy_manager = PhyManager::new(
4098 test_values.monitor_proxy,
4099 recovery::lookup_recovery_profile(""),
4100 false,
4101 test_values.node,
4102 test_values.telemetry_sender,
4103 test_values.recovery_sender,
4104 );
4105 let mut phy_container = PhyContainer::new(vec![]);
4106 let _ = phy_container.destroyed_ifaces.insert(fake_iface_id);
4107 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
4108
4109 phy_manager.record_defect(defect);
4111 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
4112 }
4113
4114 #[fuchsia::test]
4115 fn test_reset_request_fails() {
4116 let mut exec = TestExecutor::new();
4117 let test_values = test_setup();
4118
4119 drop(test_values.monitor_stream);
4121
4122 let fut = reset_phy(&test_values.monitor_proxy, 0);
4124 let mut fut = pin!(fut);
4125 assert_matches!(
4126 exec.run_until_stalled(&mut fut),
4127 Poll::Ready(Err(PhyManagerError::InternalError))
4128 );
4129 }
4130
4131 #[fuchsia::test]
4132 fn test_reset_fails() {
4133 let mut exec = TestExecutor::new();
4134 let mut test_values = test_setup();
4135
4136 let fut = reset_phy(&test_values.monitor_proxy, 0);
4138 let mut fut = pin!(fut);
4139 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4140
4141 assert_matches!(
4143 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4144 Poll::Ready(Some(Ok(
4145 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4146 ))) => {
4147 responder.send(Err(ZX_ERR_NOT_FOUND)).expect("sending fake reset response");
4148 }
4149 );
4150
4151 assert_matches!(
4153 exec.run_until_stalled(&mut fut),
4154 Poll::Ready(Err(PhyManagerError::PhyResetFailure))
4155 );
4156 }
4157
4158 #[fuchsia::test]
4159 fn test_reset_succeeds() {
4160 let mut exec = TestExecutor::new();
4161 let mut test_values = test_setup();
4162
4163 let fut = reset_phy(&test_values.monitor_proxy, 0);
4165 let mut fut = pin!(fut);
4166 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4167
4168 assert_matches!(
4170 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4171 Poll::Ready(Some(Ok(
4172 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4173 ))) => {
4174 responder.send(Ok(())).expect("sending fake reset response");
4175 }
4176 );
4177
4178 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4180 }
4181
4182 #[fuchsia::test]
4183 fn test_disconnect_request_fails() {
4184 let mut exec = TestExecutor::new();
4185 let mut test_values = test_setup();
4186
4187 let fut = disconnect(&test_values.monitor_proxy, 0);
4189 let mut fut = pin!(fut);
4190 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4191
4192 let sme_server = assert_matches!(
4194 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4195 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4196 iface_id: 0, sme_server, responder
4197 }))) => {
4198 assert!(responder.send(Ok(())).is_ok());
4200 sme_server
4201 }
4202 );
4203
4204 drop(sme_server);
4206
4207 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4209 }
4210
4211 #[fuchsia::test]
4212 fn test_disconnect_succeeds() {
4213 let mut exec = TestExecutor::new();
4214 let mut test_values = test_setup();
4215
4216 let fut = disconnect(&test_values.monitor_proxy, 0);
4218 let mut fut = pin!(fut);
4219 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4220
4221 let sme_server = assert_matches!(
4223 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4224 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4225 iface_id: 0, sme_server, responder
4226 }))) => {
4227 assert!(responder.send(Ok(())).is_ok());
4229 sme_server
4230 }
4231 );
4232
4233 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4235 let mut sme_stream = sme_server.into_stream().into_future();
4236 assert_matches!(
4237 poll_sme_req(&mut exec, &mut sme_stream),
4238 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
4239 responder,
4240 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
4241 }) => {
4242 responder.send().expect("Failed to send disconnect response")
4243 }
4244 );
4245
4246 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4248 }
4249
4250 #[fuchsia::test]
4251 fn test_disconnect_cannot_get_sme() {
4252 let mut exec = TestExecutor::new();
4253 let test_values = test_setup();
4254
4255 drop(test_values.monitor_stream);
4257
4258 let fut = disconnect(&test_values.monitor_proxy, 0);
4260 let mut fut = pin!(fut);
4261 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4262 }
4263
4264 #[fuchsia::test]
4265 fn test_stop_ap_request_fails() {
4266 let mut exec = TestExecutor::new();
4267 let mut test_values = test_setup();
4268
4269 let fut = stop_ap(&test_values.monitor_proxy, 0);
4271 let mut fut = pin!(fut);
4272 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4273
4274 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4276 let sme_server = assert_matches!(
4277 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4278 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4279 iface_id: 0, sme_server, responder
4280 }))) => {
4281 assert!(responder.send(Ok(())).is_ok());
4283 sme_server
4284 }
4285 );
4286
4287 drop(sme_server);
4289
4290 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4292 }
4293
4294 #[fuchsia::test]
4295 fn test_stop_ap_fails() {
4296 let mut exec = TestExecutor::new();
4297 let mut test_values = test_setup();
4298
4299 let fut = stop_ap(&test_values.monitor_proxy, 0);
4301 let mut fut = pin!(fut);
4302 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4303
4304 let sme_server = assert_matches!(
4306 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4307 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4308 iface_id: 0, sme_server, responder
4309 }))) => {
4310 assert!(responder.send(Ok(())).is_ok());
4312 sme_server
4313 }
4314 );
4315
4316 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4318 let mut sme_stream = sme_server.into_stream().into_future();
4319 assert_matches!(
4320 poll_ap_sme_req(&mut exec, &mut sme_stream),
4321 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4322 responder,
4323 }) => {
4324 responder.send(fidl_sme::StopApResultCode::InternalError).expect("Failed to send stop AP response")
4325 }
4326 );
4327
4328 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4330 }
4331
4332 #[fuchsia::test]
4333 fn test_stop_ap_succeeds() {
4334 let mut exec = TestExecutor::new();
4335 let mut test_values = test_setup();
4336
4337 let fut = stop_ap(&test_values.monitor_proxy, 0);
4339 let mut fut = pin!(fut);
4340 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4341
4342 let sme_server = assert_matches!(
4344 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4345 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4346 iface_id: 0, sme_server, responder
4347 }))) => {
4348 assert!(responder.send(Ok(())).is_ok());
4350 sme_server
4351 }
4352 );
4353
4354 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4356 let mut sme_stream = sme_server.into_stream().into_future();
4357 assert_matches!(
4358 poll_ap_sme_req(&mut exec, &mut sme_stream),
4359 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4360 responder,
4361 }) => {
4362 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
4363 }
4364 );
4365
4366 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4368 }
4369
4370 #[fuchsia::test]
4371 fn test_stop_ap_cannot_get_sme() {
4372 let mut exec = TestExecutor::new();
4373 let test_values = test_setup();
4374
4375 drop(test_values.monitor_stream);
4377
4378 let fut = stop_ap(&test_values.monitor_proxy, 0);
4380 let mut fut = pin!(fut);
4381 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4382 }
4383
4384 fn phy_manager_for_recovery_test(
4385 device_monitor: fidl_service::DeviceMonitorProxy,
4386 node: inspect::Node,
4387 telemetry_sender: TelemetrySender,
4388 recovery_action_sender: recovery::RecoveryActionSender,
4389 ) -> PhyManager {
4390 let mut phy_manager = PhyManager::new(
4391 device_monitor,
4392 recovery::lookup_recovery_profile("thresholded_recovery"),
4393 true,
4394 node,
4395 telemetry_sender,
4396 recovery_action_sender,
4397 );
4398
4399 let mut phy_container =
4401 PhyContainer::new(vec![fidl_common::WlanMacRole::Client, fidl_common::WlanMacRole::Ap]);
4402 assert!(phy_container.client_ifaces.insert(1,));
4403 assert!(phy_container.ap_ifaces.insert(2));
4404 assert!(phy_manager.phys.insert(0, phy_container).is_none());
4405
4406 phy_manager
4407 }
4408
4409 #[fuchsia::test]
4410 fn test_perform_recovery_destroy_nonexistent_iface() {
4411 let mut exec = TestExecutor::new();
4412 let mut test_values = test_setup();
4413 let mut phy_manager = phy_manager_for_recovery_test(
4414 test_values.monitor_proxy,
4415 test_values.node,
4416 test_values.telemetry_sender,
4417 test_values.recovery_sender,
4418 );
4419
4420 let summary = recovery::RecoverySummary {
4422 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }),
4423 action: recovery::RecoveryAction::PhyRecovery(
4424 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 123 },
4425 ),
4426 };
4427
4428 {
4430 let fut = phy_manager.perform_recovery(summary);
4431 let mut fut = pin!(fut);
4432 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4433 }
4434
4435 assert_matches!(
4437 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4438 Poll::Pending
4439 );
4440 }
4441
4442 #[fuchsia::test]
4443 fn test_perform_recovery_destroy_client_iface_fails() {
4444 let mut exec = TestExecutor::new();
4445 let test_values = test_setup();
4446 let mut phy_manager = phy_manager_for_recovery_test(
4447 test_values.monitor_proxy,
4448 test_values.node,
4449 test_values.telemetry_sender,
4450 test_values.recovery_sender,
4451 );
4452
4453 drop(test_values.monitor_stream);
4455
4456 let summary = recovery::RecoverySummary {
4458 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4459 action: recovery::RecoveryAction::PhyRecovery(
4460 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4461 ),
4462 };
4463
4464 {
4465 let fut = phy_manager.perform_recovery(summary);
4466 let mut fut = pin!(fut);
4467 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4468 }
4469
4470 assert!(phy_manager.phys[&0].client_ifaces.contains(&1));
4472 }
4473
4474 #[fuchsia::test]
4475 fn test_perform_recovery_destroy_client_iface_succeeds() {
4476 let mut exec = TestExecutor::new();
4477 let mut test_values = test_setup();
4478 let mut phy_manager = phy_manager_for_recovery_test(
4479 test_values.monitor_proxy,
4480 test_values.node,
4481 test_values.telemetry_sender,
4482 test_values.recovery_sender,
4483 );
4484
4485 let summary = recovery::RecoverySummary {
4487 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4488 action: recovery::RecoveryAction::PhyRecovery(
4489 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4490 ),
4491 };
4492
4493 {
4494 let fut = phy_manager.perform_recovery(summary);
4495 let mut fut = pin!(fut);
4496 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4497
4498 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4500
4501 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4503 }
4504
4505 assert!(!phy_manager.phys[&0].client_ifaces.contains(&1));
4507
4508 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&1));
4510 }
4511
4512 #[fuchsia::test]
4513 fn test_perform_recovery_destroy_ap_iface_fails() {
4514 let mut exec = TestExecutor::new();
4515 let test_values = test_setup();
4516 let mut phy_manager = phy_manager_for_recovery_test(
4517 test_values.monitor_proxy,
4518 test_values.node,
4519 test_values.telemetry_sender,
4520 test_values.recovery_sender,
4521 );
4522
4523 drop(test_values.monitor_stream);
4525
4526 let summary = recovery::RecoverySummary {
4528 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4529 action: recovery::RecoveryAction::PhyRecovery(
4530 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4531 ),
4532 };
4533
4534 {
4535 let fut = phy_manager.perform_recovery(summary);
4536 let mut fut = pin!(fut);
4537 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4538 }
4539
4540 assert!(phy_manager.phys[&0].ap_ifaces.contains(&2));
4542 }
4543
4544 #[fuchsia::test]
4545 fn test_perform_recovery_destroy_ap_iface_succeeds() {
4546 let mut exec = TestExecutor::new();
4547 let mut test_values = test_setup();
4548 let mut phy_manager = phy_manager_for_recovery_test(
4549 test_values.monitor_proxy,
4550 test_values.node,
4551 test_values.telemetry_sender,
4552 test_values.recovery_sender,
4553 );
4554
4555 let summary = recovery::RecoverySummary {
4557 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4558 action: recovery::RecoveryAction::PhyRecovery(
4559 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4560 ),
4561 };
4562
4563 {
4564 let fut = phy_manager.perform_recovery(summary);
4565 let mut fut = pin!(fut);
4566 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4567
4568 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4570
4571 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4573 }
4574
4575 assert!(!phy_manager.phys[&0].ap_ifaces.contains(&2));
4577
4578 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&2));
4580 }
4581
4582 #[ignore]
4584 #[test_case(Some("US".parse().unwrap()); "Cached country code")]
4585 #[test_case(None; "No cached country code")]
4586 #[fuchsia::test(add_test_attr = false)]
4587 fn test_perform_recovery_reset_requests_phy_reset(
4588 cached_country_code: Option<client_types::CountryCode>,
4589 ) {
4590 let mut exec = TestExecutor::new();
4591 let mut test_values = test_setup();
4592 let mut phy_manager = phy_manager_for_recovery_test(
4593 test_values.monitor_proxy,
4594 test_values.node,
4595 test_values.telemetry_sender,
4596 test_values.recovery_sender,
4597 );
4598
4599 phy_manager.saved_country_code = cached_country_code;
4601
4602 let summary = recovery::RecoverySummary {
4604 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4605 action: recovery::RecoveryAction::PhyRecovery(
4606 recovery::PhyRecoveryOperation::ResetPhy { phy_id: 0 },
4607 ),
4608 };
4609
4610 let fut = phy_manager.perform_recovery(summary);
4611 let mut fut = pin!(fut);
4612 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4613
4614 assert_matches!(
4616 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4617 Poll::Ready(Some(Ok(
4618 fidl_service::DeviceMonitorRequest::Reset {
4619 phy_id: 0,
4620 responder,
4621 }
4622 ))) => {
4623 responder
4624 .send(Ok(()))
4625 .expect("failed to send reset response.");
4626 }
4627 );
4628
4629 if let Some(cached_cc) = cached_country_code {
4631 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4632 assert_matches!(
4633 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4634 Poll::Ready(Some(Ok(
4635 fidl_service::DeviceMonitorRequest::SetCountry {
4636 req: fidl_service::SetCountryRequest {
4637 phy_id: 0,
4638 alpha2: cc_in_req,
4639 },
4640 responder,
4641 }
4642 ))) => {
4643 assert_eq!(cc_in_req, <[u8; 2]>::from(cached_cc));
4644 responder
4645 .send(zx::sys::ZX_OK)
4646 .expect("failed to send setCountry response.");
4647 }
4648 );
4649 }
4650
4651 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4653 }
4654
4655 #[fuchsia::test]
4656 fn test_perform_recovery_disconnect_issues_request() {
4657 let mut exec = TestExecutor::new();
4658 let mut test_values = test_setup();
4659 let mut phy_manager = phy_manager_for_recovery_test(
4660 test_values.monitor_proxy,
4661 test_values.node,
4662 test_values.telemetry_sender,
4663 test_values.recovery_sender,
4664 );
4665
4666 let summary = recovery::RecoverySummary {
4668 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4669 action: recovery::RecoveryAction::IfaceRecovery(
4670 recovery::IfaceRecoveryOperation::Disconnect { iface_id: 1 },
4671 ),
4672 };
4673
4674 let fut = phy_manager.perform_recovery(summary);
4675 let mut fut = pin!(fut);
4676 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4677
4678 let sme_server = assert_matches!(
4681 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4682 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4683 iface_id: 1, sme_server, responder
4684 }))) => {
4685 assert!(responder.send(Ok(())).is_ok());
4687 sme_server
4688 }
4689 );
4690
4691 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4693 let mut sme_stream = sme_server.into_stream().into_future();
4694 assert_matches!(
4695 poll_sme_req(&mut exec, &mut sme_stream),
4696 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
4697 responder,
4698 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
4699 }) => {
4700 responder.send().expect("Failed to send disconnect response")
4701 }
4702 );
4703
4704 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4706 }
4707
4708 #[fuchsia::test]
4709 fn test_perform_recovery_stop_ap_issues_request() {
4710 let mut exec = TestExecutor::new();
4711 let mut test_values = test_setup();
4712 let mut phy_manager = phy_manager_for_recovery_test(
4713 test_values.monitor_proxy,
4714 test_values.node,
4715 test_values.telemetry_sender,
4716 test_values.recovery_sender,
4717 );
4718
4719 let summary = recovery::RecoverySummary {
4721 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4722 action: recovery::RecoveryAction::IfaceRecovery(
4723 recovery::IfaceRecoveryOperation::StopAp { iface_id: 2 },
4724 ),
4725 };
4726
4727 let fut = phy_manager.perform_recovery(summary);
4728 let mut fut = pin!(fut);
4729 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4730
4731 let sme_server = assert_matches!(
4734 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4735 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4736 iface_id: 2, sme_server, responder
4737 }))) => {
4738 assert!(responder.send(Ok(())).is_ok());
4740 sme_server
4741 }
4742 );
4743
4744 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4746 let mut sme_stream = sme_server.into_stream().into_future();
4747 assert_matches!(
4748 poll_ap_sme_req(&mut exec, &mut sme_stream),
4749 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4750 responder,
4751 }) => {
4752 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
4753 }
4754 );
4755
4756 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4758 }
4759
4760 #[fuchsia::test]
4761 fn test_log_timeout_defect() {
4762 let _exec = TestExecutor::new();
4763 let mut test_values = test_setup();
4764 let mut phy_manager = phy_manager_for_recovery_test(
4765 test_values.monitor_proxy,
4766 test_values.node,
4767 test_values.telemetry_sender,
4768 test_values.recovery_sender,
4769 );
4770
4771 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
4773
4774 phy_manager.record_defect(Defect::Iface(IfaceFailure::Timeout {
4776 iface_id: 1,
4777 source: telemetry::TimeoutSource::Scan,
4778 }));
4779
4780 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
4782
4783 assert_matches!(
4785 test_values.telemetry_receiver.try_next(),
4786 Ok(Some(TelemetryEvent::SmeTimeout { source: telemetry::TimeoutSource::Scan }))
4787 )
4788 }
4789}