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 mac: [u8; 6],
1134 ) -> fidl_service::QueryIfaceResponse {
1135 fidl_service::QueryIfaceResponse { role, id, phy_id, phy_assigned_id, sta_addr: mac }
1136 }
1137
1138 #[fuchsia::test]
1143 fn add_valid_phy() {
1144 let mut exec = TestExecutor::new();
1145 let mut test_values = test_setup();
1146
1147 let fake_phy_id = 0;
1148 let fake_mac_roles = vec![];
1149
1150 let mut phy_manager = PhyManager::new(
1151 test_values.monitor_proxy,
1152 recovery::lookup_recovery_profile(""),
1153 false,
1154 test_values.node,
1155 test_values.telemetry_sender,
1156 test_values.recovery_sender,
1157 );
1158 {
1159 let add_phy_fut = phy_manager.add_phy(0);
1160 let mut add_phy_fut = pin!(add_phy_fut);
1161 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1162
1163 send_get_supported_mac_roles_response(
1164 &mut exec,
1165 &mut test_values.monitor_stream,
1166 Ok(&fake_mac_roles),
1167 );
1168
1169 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1170 }
1171
1172 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1173 assert_eq!(
1174 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1175 fake_mac_roles.into_iter().collect()
1176 );
1177 }
1178
1179 #[fuchsia::test]
1183 fn add_invalid_phy() {
1184 let mut exec = TestExecutor::new();
1185 let mut test_values = test_setup();
1186 let mut phy_manager = PhyManager::new(
1187 test_values.monitor_proxy,
1188 recovery::lookup_recovery_profile(""),
1189 false,
1190 test_values.node,
1191 test_values.telemetry_sender,
1192 test_values.recovery_sender,
1193 );
1194
1195 {
1196 let add_phy_fut = phy_manager.add_phy(1);
1197 let mut add_phy_fut = pin!(add_phy_fut);
1198 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1199
1200 send_get_supported_mac_roles_response(
1201 &mut exec,
1202 &mut test_values.monitor_stream,
1203 Err(zx::sys::ZX_ERR_NOT_FOUND),
1204 );
1205
1206 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1207 }
1208 assert!(phy_manager.phys.is_empty());
1209 }
1210
1211 #[fuchsia::test]
1215 fn add_duplicate_phy() {
1216 let mut exec = TestExecutor::new();
1217 let mut test_values = test_setup();
1218 let mut phy_manager = PhyManager::new(
1219 test_values.monitor_proxy,
1220 recovery::lookup_recovery_profile(""),
1221 false,
1222 test_values.node,
1223 test_values.telemetry_sender,
1224 test_values.recovery_sender,
1225 );
1226
1227 let fake_phy_id = 0;
1228 let fake_mac_roles = vec![];
1229
1230 {
1231 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1232 let mut add_phy_fut = pin!(add_phy_fut);
1233 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1234
1235 send_get_supported_mac_roles_response(
1236 &mut exec,
1237 &mut test_values.monitor_stream,
1238 Ok(&fake_mac_roles),
1239 );
1240
1241 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1242 }
1243
1244 {
1245 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1246 assert_eq!(
1247 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1248 fake_mac_roles.clone().into_iter().collect()
1249 );
1250 }
1251
1252 {
1254 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1255 let mut add_phy_fut = pin!(add_phy_fut);
1256 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1257
1258 send_get_supported_mac_roles_response(
1259 &mut exec,
1260 &mut test_values.monitor_stream,
1261 Ok(&fake_mac_roles),
1262 );
1263
1264 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1265 }
1266
1267 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1268 assert_eq!(
1269 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1270 fake_mac_roles.into_iter().collect()
1271 );
1272 }
1273
1274 #[fuchsia::test]
1275 fn create_all_client_ifaces_after_phys_added() {
1276 let mut exec = TestExecutor::new();
1277 let mut test_values = test_setup();
1278 let mut phy_manager = PhyManager::new(
1279 test_values.monitor_proxy,
1280 recovery::lookup_recovery_profile(""),
1281 false,
1282 test_values.node,
1283 test_values.telemetry_sender,
1284 test_values.recovery_sender,
1285 );
1286
1287 for phy_id in 0..2 {
1288 {
1289 let add_phy_fut = phy_manager.add_phy(phy_id);
1290 let mut add_phy_fut = pin!(add_phy_fut);
1291
1292 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1293
1294 send_get_supported_mac_roles_response(
1295 &mut exec,
1296 &mut test_values.monitor_stream,
1297 Ok(&[fidl_common::WlanMacRole::Client]),
1298 );
1299
1300 assert_matches!(exec.run_until_stalled(&mut add_phy_fut), Poll::Ready(Ok(())));
1301 }
1302 assert!(phy_manager.phys.contains_key(&phy_id));
1303 }
1304
1305 {
1306 let start_connections_fut = phy_manager
1307 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1308 let mut start_connections_fut = pin!(start_connections_fut);
1309
1310 for iface_id in [10, 20] {
1313 assert!(exec.run_until_stalled(&mut start_connections_fut).is_pending());
1314
1315 send_create_iface_response(
1316 &mut exec,
1317 &mut test_values.monitor_stream,
1318 Some(iface_id),
1319 );
1320 }
1321
1322 assert_matches!(exec.run_until_stalled(&mut start_connections_fut),
1323 Poll::Ready(iface_ids) => {
1324 assert!(iface_ids.values().all(Result::is_ok));
1325 assert!(iface_ids.contains_key(&0));
1326 assert!(iface_ids.contains_key(&1));
1327 let iface_ids: HashSet<_> = iface_ids.into_values().flat_map(Result::unwrap).collect();
1328 assert_eq!(iface_ids, HashSet::from([10, 20]));
1329 }
1330 );
1331 }
1332
1333 let mut iface_ids = HashSet::new();
1334 for phy_id in 0..2 {
1335 let phy_container = phy_manager.phys.get(&phy_id).unwrap();
1336 assert_eq!(phy_container.client_ifaces.len(), 1);
1339 phy_container.client_ifaces.iter().for_each(|iface_id| {
1340 assert!(iface_ids.insert(*iface_id));
1341 });
1342 assert!(phy_container.defects.events.is_empty());
1343 }
1344 assert_eq!(iface_ids, HashSet::from([10, 20]));
1345 }
1346
1347 #[fuchsia::test]
1351 fn add_phy_after_create_all_client_ifaces() {
1352 let mut exec = TestExecutor::new();
1353 let mut test_values = test_setup();
1354 let mut phy_manager = PhyManager::new(
1355 test_values.monitor_proxy,
1356 recovery::lookup_recovery_profile(""),
1357 false,
1358 test_values.node,
1359 test_values.telemetry_sender,
1360 test_values.recovery_sender,
1361 );
1362
1363 let fake_iface_id = 1;
1364 let fake_phy_id = 1;
1365 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1366
1367 {
1368 let start_connections_fut = phy_manager
1369 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1370 let mut start_connections_fut = pin!(start_connections_fut);
1371 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1372 }
1373
1374 {
1377 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1378 let mut add_phy_fut = pin!(add_phy_fut);
1379 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1380
1381 send_get_supported_mac_roles_response(
1382 &mut exec,
1383 &mut test_values.monitor_stream,
1384 Ok(&fake_mac_roles),
1385 );
1386
1387 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1388
1389 send_create_iface_response(
1390 &mut exec,
1391 &mut test_values.monitor_stream,
1392 Some(fake_iface_id),
1393 );
1394
1395 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1396 }
1397
1398 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1399 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1400 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1401 assert!(phy_container.defects.events.is_empty());
1402 }
1403
1404 #[fuchsia::test]
1409 fn add_phy_with_iface_creation_failure() {
1410 let mut exec = TestExecutor::new();
1411 let mut test_values = test_setup();
1412 let mut phy_manager = PhyManager::new(
1413 test_values.monitor_proxy,
1414 recovery::lookup_recovery_profile(""),
1415 false,
1416 test_values.node,
1417 test_values.telemetry_sender,
1418 test_values.recovery_sender,
1419 );
1420
1421 let fake_phy_id = 1;
1422 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1423
1424 {
1425 let start_connections_fut = phy_manager
1426 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
1427 let mut start_connections_fut = pin!(start_connections_fut);
1428 assert!(exec.run_until_stalled(&mut start_connections_fut).is_ready());
1429 }
1430
1431 {
1434 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1435 let mut add_phy_fut = pin!(add_phy_fut);
1436 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1437
1438 send_get_supported_mac_roles_response(
1439 &mut exec,
1440 &mut test_values.monitor_stream,
1441 Ok(&fake_mac_roles),
1442 );
1443
1444 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1445
1446 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
1448
1449 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1450 }
1451
1452 assert!(!phy_manager.phys.contains_key(&fake_phy_id));
1453 }
1454
1455 #[fuchsia::test]
1457 fn test_add_phy_after_setting_country_code() {
1458 let mut exec = TestExecutor::new();
1459 let mut test_values = test_setup();
1460
1461 let fake_phy_id = 1;
1462 let fake_mac_roles = vec![];
1463
1464 let mut phy_manager = PhyManager::new(
1465 test_values.monitor_proxy,
1466 recovery::lookup_recovery_profile(""),
1467 false,
1468 test_values.node,
1469 test_values.telemetry_sender,
1470 test_values.recovery_sender,
1471 );
1472
1473 {
1474 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
1475 let mut set_country_fut = pin!(set_country_fut);
1476 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
1477 }
1478
1479 {
1480 let add_phy_fut = phy_manager.add_phy(fake_phy_id);
1481 let mut add_phy_fut = pin!(add_phy_fut);
1482 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1483
1484 send_get_supported_mac_roles_response(
1485 &mut exec,
1486 &mut test_values.monitor_stream,
1487 Ok(&fake_mac_roles),
1488 );
1489
1490 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
1491
1492 assert_matches!(
1493 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
1494 Poll::Ready(Some(Ok(
1495 fidl_service::DeviceMonitorRequest::SetCountry {
1496 req: fidl_service::SetCountryRequest {
1497 phy_id: 1,
1498 alpha2: [b'U', b'S'],
1499 },
1500 responder,
1501 }
1502 ))) => {
1503 responder.send(ZX_OK).expect("sending fake set country response");
1504 }
1505 );
1506
1507 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
1508 }
1509
1510 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1511 assert_eq!(
1512 phy_manager.phys.get(&fake_phy_id).unwrap().supported_mac_roles,
1513 fake_mac_roles.into_iter().collect()
1514 );
1515 }
1516
1517 #[run_singlethreaded(test)]
1518 async fn remove_valid_phy() {
1519 let test_values = test_setup();
1520 let mut phy_manager = PhyManager::new(
1521 test_values.monitor_proxy,
1522 recovery::lookup_recovery_profile(""),
1523 false,
1524 test_values.node,
1525 test_values.telemetry_sender,
1526 test_values.recovery_sender,
1527 );
1528
1529 let fake_phy_id = 1;
1530 let fake_mac_roles = vec![];
1531
1532 let phy_container = PhyContainer::new(fake_mac_roles);
1533 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1534 phy_manager.remove_phy(fake_phy_id);
1535 assert!(phy_manager.phys.is_empty());
1536 }
1537
1538 #[run_singlethreaded(test)]
1543 async fn remove_nonexistent_phy() {
1544 let test_values = test_setup();
1545 let mut phy_manager = PhyManager::new(
1546 test_values.monitor_proxy,
1547 recovery::lookup_recovery_profile(""),
1548 false,
1549 test_values.node,
1550 test_values.telemetry_sender,
1551 test_values.recovery_sender,
1552 );
1553
1554 let fake_phy_id = 1;
1555 let fake_mac_roles = vec![];
1556
1557 let phy_container = PhyContainer::new(fake_mac_roles);
1558 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1559 phy_manager.remove_phy(2);
1560 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1561 }
1562
1563 #[fuchsia::test]
1567 fn on_iface_added() {
1568 let mut exec = TestExecutor::new();
1569 let mut test_values = test_setup();
1570 let mut phy_manager = PhyManager::new(
1571 test_values.monitor_proxy,
1572 recovery::lookup_recovery_profile(""),
1573 false,
1574 test_values.node,
1575 test_values.telemetry_sender,
1576 test_values.recovery_sender,
1577 );
1578
1579 let fake_phy_id = 1;
1582 let fake_mac_roles = vec![];
1583
1584 let phy_container = PhyContainer::new(fake_mac_roles);
1585
1586 let fake_role = fidl_common::WlanMacRole::Client;
1588 let fake_iface_id = 1;
1589 let fake_phy_assigned_id = 1;
1590 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1591 let iface_response = create_iface_response(
1592 fake_role,
1593 fake_iface_id,
1594 fake_phy_id,
1595 fake_phy_assigned_id,
1596 fake_sta_addr,
1597 );
1598
1599 {
1600 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1602
1603 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1605 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1606 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1607
1608 send_query_iface_response(
1609 &mut exec,
1610 &mut test_values.monitor_stream,
1611 Some(iface_response),
1612 );
1613
1614 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1616 }
1617
1618 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1621 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1622 }
1623
1624 #[fuchsia::test]
1625 fn on_iface_added_unknown_role_is_unsupported() {
1626 let mut exec = TestExecutor::new();
1627 let mut test_values = test_setup();
1628 let mut phy_manager = PhyManager::new(
1629 test_values.monitor_proxy,
1630 recovery::lookup_recovery_profile(""),
1631 false,
1632 test_values.node,
1633 test_values.telemetry_sender,
1634 test_values.recovery_sender,
1635 );
1636
1637 let fake_phy_id = 1;
1640 let fake_mac_roles = vec![];
1641
1642 let phy_container = PhyContainer::new(fake_mac_roles);
1643
1644 let fake_role = fidl_common::WlanMacRole::unknown();
1646 let fake_iface_id = 1;
1647 let fake_phy_assigned_id = 1;
1648 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1649 let iface_response = create_iface_response(
1650 fake_role,
1651 fake_iface_id,
1652 fake_phy_id,
1653 fake_phy_assigned_id,
1654 fake_sta_addr,
1655 );
1656
1657 {
1658 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1660
1661 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1663 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1664 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1665
1666 send_query_iface_response(
1667 &mut exec,
1668 &mut test_values.monitor_stream,
1669 Some(iface_response),
1670 );
1671
1672 assert_matches!(
1674 exec.run_until_stalled(&mut on_iface_added_fut),
1675 Poll::Ready(Err(PhyManagerError::Unsupported))
1676 );
1677 }
1678
1679 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1682 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
1683 }
1684
1685 #[fuchsia::test]
1690 fn on_iface_added_missing_phy() {
1691 let mut exec = TestExecutor::new();
1692 let mut test_values = test_setup();
1693 let mut phy_manager = PhyManager::new(
1694 test_values.monitor_proxy,
1695 recovery::lookup_recovery_profile(""),
1696 false,
1697 test_values.node,
1698 test_values.telemetry_sender,
1699 test_values.recovery_sender,
1700 );
1701
1702 let fake_phy_id = 1;
1705 let fake_mac_roles = vec![];
1706
1707 let fake_role = fidl_common::WlanMacRole::Client;
1709 let fake_iface_id = 1;
1710 let fake_phy_assigned_id = 1;
1711 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1712 let iface_response = create_iface_response(
1713 fake_role,
1714 fake_iface_id,
1715 fake_phy_id,
1716 fake_phy_assigned_id,
1717 fake_sta_addr,
1718 );
1719
1720 {
1721 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1723 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1724
1725 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1730
1731 send_query_iface_response(
1732 &mut exec,
1733 &mut test_values.monitor_stream,
1734 Some(iface_response),
1735 );
1736
1737 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1739
1740 send_get_supported_mac_roles_response(
1741 &mut exec,
1742 &mut test_values.monitor_stream,
1743 Ok(&fake_mac_roles),
1744 );
1745
1746 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1748 }
1749
1750 assert!(phy_manager.phys.contains_key(&fake_phy_id));
1753
1754 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1755 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1756 }
1757
1758 #[fuchsia::test]
1762 fn add_duplicate_iface() {
1763 let mut exec = TestExecutor::new();
1764 let mut test_values = test_setup();
1765 let mut phy_manager = PhyManager::new(
1766 test_values.monitor_proxy,
1767 recovery::lookup_recovery_profile(""),
1768 false,
1769 test_values.node,
1770 test_values.telemetry_sender,
1771 test_values.recovery_sender,
1772 );
1773
1774 let fake_phy_id = 1;
1777 let fake_mac_roles = vec![];
1778
1779 let phy_container = PhyContainer::new(fake_mac_roles);
1781 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1782
1783 let fake_role = fidl_common::WlanMacRole::Client;
1785 let fake_iface_id = 1;
1786 let fake_phy_assigned_id = 1;
1787 let fake_sta_addr = [0, 1, 2, 3, 4, 5];
1788 let iface_response = create_iface_response(
1789 fake_role,
1790 fake_iface_id,
1791 fake_phy_id,
1792 fake_phy_assigned_id,
1793 fake_sta_addr,
1794 );
1795
1796 for _ in 0..2 {
1798 let on_iface_added_fut = phy_manager.on_iface_added(fake_iface_id);
1800 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1801 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1802
1803 send_query_iface_response(
1804 &mut exec,
1805 &mut test_values.monitor_stream,
1806 Some(iface_response),
1807 );
1808
1809 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1811 }
1812
1813 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1816 assert_eq!(phy_container.client_ifaces.len(), 1);
1817 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
1818 }
1819
1820 #[fuchsia::test]
1824 fn add_nonexistent_iface() {
1825 let mut exec = TestExecutor::new();
1826 let mut test_values = test_setup();
1827 let mut phy_manager = PhyManager::new(
1828 test_values.monitor_proxy,
1829 recovery::lookup_recovery_profile(""),
1830 false,
1831 test_values.node,
1832 test_values.telemetry_sender,
1833 test_values.recovery_sender,
1834 );
1835
1836 {
1837 let on_iface_added_fut = phy_manager.on_iface_added(1);
1839 let mut on_iface_added_fut = pin!(on_iface_added_fut);
1840 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_pending());
1841
1842 send_query_iface_response(&mut exec, &mut test_values.monitor_stream, None);
1843
1844 assert!(exec.run_until_stalled(&mut on_iface_added_fut).is_ready());
1846 }
1847
1848 assert!(phy_manager.phys.is_empty());
1851 }
1852
1853 #[run_singlethreaded(test)]
1857 async fn test_on_iface_removed() {
1858 let test_values = test_setup();
1859 let mut phy_manager = PhyManager::new(
1860 test_values.monitor_proxy,
1861 recovery::lookup_recovery_profile(""),
1862 false,
1863 test_values.node,
1864 test_values.telemetry_sender,
1865 test_values.recovery_sender,
1866 );
1867
1868 let fake_phy_id = 1;
1871 let fake_mac_roles = vec![];
1872
1873 let mut phy_container = PhyContainer::new(fake_mac_roles);
1875 let fake_iface_id = 1;
1876 let _ = phy_container.client_ifaces.insert(fake_iface_id);
1877
1878 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1879
1880 phy_manager.on_iface_removed(fake_iface_id);
1881
1882 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1884 assert!(phy_container.client_ifaces.is_empty());
1885 }
1886
1887 #[run_singlethreaded(test)]
1891 async fn remove_missing_iface() {
1892 let test_values = test_setup();
1893 let mut phy_manager = PhyManager::new(
1894 test_values.monitor_proxy,
1895 recovery::lookup_recovery_profile(""),
1896 false,
1897 test_values.node,
1898 test_values.telemetry_sender,
1899 test_values.recovery_sender,
1900 );
1901
1902 let fake_phy_id = 1;
1905 let fake_mac_roles = vec![];
1906
1907 let present_iface_id = 1;
1908 let removed_iface_id = 2;
1909
1910 let mut phy_container = PhyContainer::new(fake_mac_roles);
1912 let _ = phy_container.client_ifaces.insert(present_iface_id);
1913 let _ = phy_container.client_ifaces.insert(removed_iface_id);
1914 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1915 phy_manager.on_iface_removed(removed_iface_id);
1916
1917 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
1919 assert_eq!(phy_container.client_ifaces.len(), 1);
1920 assert!(phy_container.client_ifaces.contains(&present_iface_id));
1921 }
1922
1923 #[run_singlethreaded(test)]
1926 async fn get_client_no_phys() {
1927 let test_values = test_setup();
1928 let mut phy_manager = PhyManager::new(
1929 test_values.monitor_proxy,
1930 recovery::lookup_recovery_profile(""),
1931 false,
1932 test_values.node,
1933 test_values.telemetry_sender,
1934 test_values.recovery_sender,
1935 );
1936
1937 let client = phy_manager.get_client();
1938 assert!(client.is_none());
1939 }
1940
1941 #[run_singlethreaded(test)]
1945 async fn get_unconfigured_client() {
1946 let test_values = test_setup();
1947 let mut phy_manager = PhyManager::new(
1948 test_values.monitor_proxy,
1949 recovery::lookup_recovery_profile(""),
1950 false,
1951 test_values.node,
1952 test_values.telemetry_sender,
1953 test_values.recovery_sender,
1954 );
1955
1956 let fake_phy_id = 1;
1959 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1960 let phy_container = PhyContainer::new(fake_mac_roles);
1961
1962 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1963
1964 let client = phy_manager.get_client();
1966 assert!(client.is_none());
1967 }
1968
1969 #[run_singlethreaded(test)]
1973 async fn get_configured_client() {
1974 let test_values = test_setup();
1975 let mut phy_manager = PhyManager::new(
1976 test_values.monitor_proxy,
1977 recovery::lookup_recovery_profile(""),
1978 false,
1979 test_values.node,
1980 test_values.telemetry_sender,
1981 test_values.recovery_sender,
1982 );
1983 phy_manager.client_connections_enabled = true;
1984
1985 let fake_phy_id = 1;
1988 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
1989 let phy_container = PhyContainer::new(fake_mac_roles);
1990
1991 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
1992
1993 let fake_iface_id = 1;
1995 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
1996 let _ = phy_container.client_ifaces.insert(fake_iface_id);
1997
1998 let client = phy_manager.get_client();
2000 assert_eq!(client.unwrap(), fake_iface_id)
2001 }
2002
2003 #[run_singlethreaded(test)]
2007 async fn get_client_no_compatible_phys() {
2008 let test_values = test_setup();
2009 let mut phy_manager = PhyManager::new(
2010 test_values.monitor_proxy,
2011 recovery::lookup_recovery_profile(""),
2012 false,
2013 test_values.node,
2014 test_values.telemetry_sender,
2015 test_values.recovery_sender,
2016 );
2017
2018 let fake_iface_id = 1;
2021 let fake_phy_id = 1;
2022 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2023 let mut phy_container = PhyContainer::new(fake_mac_roles);
2024 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2025 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2026
2027 let client = phy_manager.get_client();
2029 assert!(client.is_none());
2030 }
2031
2032 #[fuchsia::test]
2035 fn get_client_while_stopped() {
2036 let _exec = TestExecutor::new();
2037 let test_values = test_setup();
2038
2039 let mut phy_manager = PhyManager::new(
2041 test_values.monitor_proxy,
2042 recovery::lookup_recovery_profile(""),
2043 false,
2044 test_values.node,
2045 test_values.telemetry_sender,
2046 test_values.recovery_sender,
2047 );
2048 assert!(!phy_manager.client_connections_enabled);
2049
2050 let fake_phy_id = 1;
2052 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2053 let mut phy_container = PhyContainer::new(fake_mac_roles);
2054 let _ = phy_container.client_ifaces.insert(1);
2055 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2056
2057 assert_eq!(phy_manager.get_client(), None);
2060 }
2061
2062 #[fuchsia::test]
2066 fn destroy_all_client_ifaces() {
2067 let mut exec = TestExecutor::new();
2068 let mut test_values = test_setup();
2069 let mut phy_manager = PhyManager::new(
2070 test_values.monitor_proxy,
2071 recovery::lookup_recovery_profile(""),
2072 false,
2073 test_values.node,
2074 test_values.telemetry_sender,
2075 test_values.recovery_sender,
2076 );
2077
2078 let fake_iface_id = 1;
2081 let fake_phy_id = 1;
2082 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2083 let phy_container = PhyContainer::new(fake_mac_roles);
2084
2085 {
2086 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2087
2088 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2090 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2091
2092 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2094 let mut stop_clients_future = pin!(stop_clients_future);
2095
2096 assert!(exec.run_until_stalled(&mut stop_clients_future).is_pending());
2097
2098 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2099
2100 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2101 }
2102
2103 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2105
2106 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2107 assert!(!phy_container.client_ifaces.contains(&fake_iface_id));
2108
2109 assert!(!phy_manager.client_connections_enabled);
2111
2112 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2114 }
2115
2116 #[fuchsia::test]
2119 fn destroy_all_client_ifaces_no_clients() {
2120 let mut exec = TestExecutor::new();
2121 let test_values = test_setup();
2122 let mut phy_manager = PhyManager::new(
2123 test_values.monitor_proxy,
2124 recovery::lookup_recovery_profile(""),
2125 false,
2126 test_values.node,
2127 test_values.telemetry_sender,
2128 test_values.recovery_sender,
2129 );
2130
2131 let fake_iface_id = 1;
2134 let fake_phy_id = 1;
2135 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2136 let phy_container = PhyContainer::new(fake_mac_roles);
2137
2138 {
2140 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2141
2142 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2144 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2145
2146 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2148 let mut stop_clients_future = pin!(stop_clients_future);
2149
2150 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2151 }
2152
2153 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2155
2156 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2157 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2158 }
2159
2160 #[fuchsia::test]
2162 fn destroy_all_client_ifaces_fails() {
2163 let mut exec = TestExecutor::new();
2164 let test_values = test_setup();
2165 let mut phy_manager = PhyManager::new(
2166 test_values.monitor_proxy,
2167 recovery::lookup_recovery_profile(""),
2168 false,
2169 test_values.node,
2170 test_values.telemetry_sender,
2171 test_values.recovery_sender,
2172 );
2173
2174 drop(test_values.monitor_stream);
2176
2177 let fake_iface_id = 1;
2180 let fake_phy_id = 1;
2181 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2182 let mut phy_container = PhyContainer::new(fake_mac_roles);
2183
2184 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2187
2188 {
2189 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2190
2191 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2193 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2194
2195 let stop_clients_future = phy_manager.destroy_all_client_ifaces();
2197 let mut stop_clients_future = pin!(stop_clients_future);
2198 assert!(exec.run_until_stalled(&mut stop_clients_future).is_ready());
2199 }
2200
2201 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2203
2204 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2205 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2206 assert_eq!(phy_container.defects.events.len(), 1);
2207 assert_eq!(
2208 phy_container.defects.events[0].value,
2209 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2210 );
2211 }
2212
2213 #[fuchsia::test]
2216 fn get_ap_no_phys() {
2217 let mut exec = TestExecutor::new();
2218 let test_values = test_setup();
2219 let mut phy_manager = PhyManager::new(
2220 test_values.monitor_proxy,
2221 recovery::lookup_recovery_profile(""),
2222 false,
2223 test_values.node,
2224 test_values.telemetry_sender,
2225 test_values.recovery_sender,
2226 );
2227
2228 let get_ap_future = phy_manager.create_or_get_ap_iface();
2229
2230 let mut get_ap_future = pin!(get_ap_future);
2231 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2232 }
2233
2234 #[fuchsia::test]
2238 fn get_unconfigured_ap() {
2239 let mut exec = TestExecutor::new();
2240 let mut test_values = test_setup();
2241 let mut phy_manager = PhyManager::new(
2242 test_values.monitor_proxy,
2243 recovery::lookup_recovery_profile(""),
2244 false,
2245 test_values.node,
2246 test_values.telemetry_sender,
2247 test_values.recovery_sender,
2248 );
2249
2250 let fake_phy_id = 1;
2253 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2254 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2255
2256 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2257
2258 let fake_iface_id = 1;
2260 {
2261 let get_ap_future = phy_manager.create_or_get_ap_iface();
2262
2263 let mut get_ap_future = pin!(get_ap_future);
2264 assert!(exec.run_until_stalled(&mut get_ap_future).is_pending());
2265
2266 send_create_iface_response(
2267 &mut exec,
2268 &mut test_values.monitor_stream,
2269 Some(fake_iface_id),
2270 );
2271 assert_matches!(
2272 exec.run_until_stalled(&mut get_ap_future),
2273 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2274 );
2275 }
2276
2277 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.contains(&fake_iface_id));
2278 }
2279
2280 #[fuchsia::test]
2282 fn get_ap_iface_creation_fails() {
2283 let mut exec = TestExecutor::new();
2284 let test_values = test_setup();
2285 let mut phy_manager = PhyManager::new(
2286 test_values.monitor_proxy,
2287 recovery::lookup_recovery_profile(""),
2288 false,
2289 test_values.node,
2290 test_values.telemetry_sender,
2291 test_values.recovery_sender,
2292 );
2293
2294 drop(test_values.monitor_stream);
2296
2297 let fake_phy_id = 1;
2300 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2301 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2302
2303 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2306
2307 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2308
2309 {
2310 let get_ap_future = phy_manager.create_or_get_ap_iface();
2311
2312 let mut get_ap_future = pin!(get_ap_future);
2313 assert!(exec.run_until_stalled(&mut get_ap_future).is_ready());
2314 }
2315
2316 assert!(phy_manager.phys[&fake_phy_id].ap_ifaces.is_empty());
2317 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
2318 assert_eq!(
2319 phy_manager.phys[&fake_phy_id].defects.events[0].value,
2320 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
2321 );
2322 }
2323
2324 #[fuchsia::test]
2328 fn get_configured_ap() {
2329 let mut exec = TestExecutor::new();
2330 let test_values = test_setup();
2331 let mut phy_manager = PhyManager::new(
2332 test_values.monitor_proxy,
2333 recovery::lookup_recovery_profile(""),
2334 false,
2335 test_values.node,
2336 test_values.telemetry_sender,
2337 test_values.recovery_sender,
2338 );
2339
2340 let fake_phy_id = 1;
2343 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2344 let phy_container = PhyContainer::new(fake_mac_roles);
2345
2346 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2347
2348 let fake_iface_id = 1;
2350 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2351 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2352
2353 let get_ap_future = phy_manager.create_or_get_ap_iface();
2355 let mut get_ap_future = pin!(get_ap_future);
2356 assert_matches!(
2357 exec.run_until_stalled(&mut get_ap_future),
2358 Poll::Ready(Ok(Some(iface_id))) => assert_eq!(iface_id, fake_iface_id)
2359 );
2360 }
2361
2362 #[fuchsia::test]
2365 fn get_ap_no_compatible_phys() {
2366 let mut exec = TestExecutor::new();
2367 let test_values = test_setup();
2368 let mut phy_manager = PhyManager::new(
2369 test_values.monitor_proxy,
2370 recovery::lookup_recovery_profile(""),
2371 false,
2372 test_values.node,
2373 test_values.telemetry_sender,
2374 test_values.recovery_sender,
2375 );
2376
2377 let fake_phy_id = 1;
2380 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2381 let phy_container = PhyContainer::new(fake_mac_roles);
2382
2383 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2384
2385 let get_ap_future = phy_manager.create_or_get_ap_iface();
2387 let mut get_ap_future = pin!(get_ap_future);
2388 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(Ok(None)));
2389 }
2390
2391 #[fuchsia::test]
2394 fn stop_valid_ap_iface() {
2395 let mut exec = TestExecutor::new();
2396 let mut test_values = test_setup();
2397 let mut phy_manager = PhyManager::new(
2398 test_values.monitor_proxy,
2399 recovery::lookup_recovery_profile(""),
2400 false,
2401 test_values.node,
2402 test_values.telemetry_sender,
2403 test_values.recovery_sender,
2404 );
2405
2406 let fake_iface_id = 1;
2409 let fake_phy_id = 1;
2410 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2411
2412 {
2413 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2414
2415 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2416
2417 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2419 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2420
2421 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2423 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2424 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2425 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2426
2427 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2428 }
2429
2430 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2431
2432 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2433 assert!(!phy_container.ap_ifaces.contains(&fake_iface_id));
2434 assert!(phy_container.defects.events.is_empty());
2435 assert!(phy_container.destroyed_ifaces.contains(&fake_iface_id));
2436 }
2437
2438 #[fuchsia::test]
2441 fn stop_invalid_ap_iface() {
2442 let mut exec = TestExecutor::new();
2443 let test_values = test_setup();
2444 let mut phy_manager = PhyManager::new(
2445 test_values.monitor_proxy,
2446 recovery::lookup_recovery_profile(""),
2447 false,
2448 test_values.node,
2449 test_values.telemetry_sender,
2450 test_values.recovery_sender,
2451 );
2452
2453 let fake_iface_id = 1;
2456 let fake_phy_id = 1;
2457 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2458
2459 {
2460 let phy_container = PhyContainer::new(fake_mac_roles);
2461
2462 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2463
2464 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2466 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2467
2468 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(2);
2470 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2471 assert_matches!(
2472 exec.run_until_stalled(&mut destroy_ap_iface_future),
2473 Poll::Ready(Ok(()))
2474 );
2475 }
2476
2477 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2478
2479 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2480 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2481 assert!(phy_container.defects.events.is_empty());
2482 }
2483
2484 #[fuchsia::test]
2487 fn stop_ap_iface_fails() {
2488 let mut exec = TestExecutor::new();
2489 let test_values = test_setup();
2490 let mut phy_manager = PhyManager::new(
2491 test_values.monitor_proxy,
2492 recovery::lookup_recovery_profile(""),
2493 false,
2494 test_values.node,
2495 test_values.telemetry_sender,
2496 test_values.recovery_sender,
2497 );
2498
2499 drop(test_values.monitor_stream);
2501
2502 let fake_iface_id = 1;
2505 let fake_phy_id = 1;
2506 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2507
2508 {
2509 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2510
2511 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2514
2515 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2516
2517 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2519 let _ = phy_container.ap_ifaces.insert(fake_iface_id);
2520
2521 let destroy_ap_iface_future = phy_manager.destroy_ap_iface(fake_iface_id);
2523 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2524 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2525 }
2526
2527 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2528
2529 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2530 assert!(phy_container.ap_ifaces.contains(&fake_iface_id));
2531 assert_eq!(phy_container.defects.events.len(), 1);
2532 assert_eq!(
2533 phy_container.defects.events[0].value,
2534 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2535 );
2536 }
2537
2538 #[fuchsia::test]
2543 fn stop_all_ap_ifaces() {
2544 let mut exec = TestExecutor::new();
2545 let mut test_values = test_setup();
2546 let mut phy_manager = PhyManager::new(
2547 test_values.monitor_proxy,
2548 recovery::lookup_recovery_profile(""),
2549 false,
2550 test_values.node,
2551 test_values.telemetry_sender,
2552 test_values.recovery_sender,
2553 );
2554
2555 let fake_phy_id = 1;
2558 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2559
2560 {
2561 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2562
2563 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2564
2565 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2567 let _ = phy_container.ap_ifaces.insert(0);
2568 let _ = phy_container.ap_ifaces.insert(1);
2569
2570 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2572 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2573
2574 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2575 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2576
2577 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_pending());
2578 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
2579
2580 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2581 }
2582
2583 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2584
2585 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2586 assert!(phy_container.ap_ifaces.is_empty());
2587 assert!(phy_container.destroyed_ifaces.contains(&0));
2588 assert!(phy_container.destroyed_ifaces.contains(&1));
2589 assert!(phy_container.defects.events.is_empty());
2590 }
2591
2592 #[fuchsia::test]
2596 fn stop_all_ap_ifaces_with_client() {
2597 let mut exec = TestExecutor::new();
2598 let test_values = test_setup();
2599 let mut phy_manager = PhyManager::new(
2600 test_values.monitor_proxy,
2601 recovery::lookup_recovery_profile(""),
2602 false,
2603 test_values.node,
2604 test_values.telemetry_sender,
2605 test_values.recovery_sender,
2606 );
2607
2608 let fake_iface_id = 1;
2611 let fake_phy_id = 1;
2612 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2613
2614 {
2615 let phy_container = PhyContainer::new(fake_mac_roles);
2616
2617 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2618
2619 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2621 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2622
2623 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2625 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2626 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2627 }
2628
2629 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2630
2631 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2632 assert!(phy_container.client_ifaces.contains(&fake_iface_id));
2633 assert!(phy_container.defects.events.is_empty());
2634 }
2635
2636 #[fuchsia::test]
2638 fn stop_all_ap_ifaces_fails() {
2639 let mut exec = TestExecutor::new();
2640 let test_values = test_setup();
2641 let mut phy_manager = PhyManager::new(
2642 test_values.monitor_proxy,
2643 recovery::lookup_recovery_profile(""),
2644 false,
2645 test_values.node,
2646 test_values.telemetry_sender,
2647 test_values.recovery_sender,
2648 );
2649
2650 drop(test_values.monitor_stream);
2652
2653 let fake_phy_id = 1;
2656 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2657
2658 {
2659 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2660
2661 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2664
2665 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2666
2667 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2669 let _ = phy_container.ap_ifaces.insert(0);
2670 let _ = phy_container.ap_ifaces.insert(1);
2671
2672 let destroy_ap_iface_future = phy_manager.destroy_all_ap_ifaces();
2674 let mut destroy_ap_iface_future = pin!(destroy_ap_iface_future);
2675 assert!(exec.run_until_stalled(&mut destroy_ap_iface_future).is_ready());
2676 }
2677
2678 assert!(phy_manager.phys.contains_key(&fake_phy_id));
2679
2680 let phy_container = phy_manager.phys.get(&fake_phy_id).unwrap();
2681 assert_eq!(phy_container.ap_ifaces.len(), 2);
2682 assert_eq!(phy_container.defects.events.len(), 2);
2683 assert_eq!(
2684 phy_container.defects.events[0].value,
2685 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2686 );
2687 assert_eq!(
2688 phy_container.defects.events[1].value,
2689 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 1 })
2690 );
2691 }
2692
2693 #[fuchsia::test]
2697 fn test_suggest_ap_mac() {
2698 let mut exec = TestExecutor::new();
2699 let mut test_values = test_setup();
2700 let mut phy_manager = PhyManager::new(
2701 test_values.monitor_proxy,
2702 recovery::lookup_recovery_profile(""),
2703 false,
2704 test_values.node,
2705 test_values.telemetry_sender,
2706 test_values.recovery_sender,
2707 );
2708
2709 let fake_iface_id = 1;
2712 let fake_phy_id = 1;
2713 let fake_mac_roles = vec![fidl_common::WlanMacRole::Ap];
2714 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2715
2716 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2717
2718 let phy_container = phy_manager.phys.get_mut(&fake_phy_id).unwrap();
2720 let _ = phy_container.client_ifaces.insert(fake_iface_id);
2721
2722 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
2724 phy_manager.suggest_ap_mac(mac);
2725
2726 let get_ap_future = phy_manager.create_or_get_ap_iface();
2727 let mut get_ap_future = pin!(get_ap_future);
2728 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Pending);
2729
2730 assert_matches!(
2732 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
2733 Poll::Ready(Some(Ok(
2734 fidl_service::DeviceMonitorRequest::CreateIface {
2735 payload,
2736 responder,
2737 }
2738 ))) => {
2739 let requested_mac: MacAddr = payload.sta_address.unwrap().into();
2740 assert_eq!(requested_mac, mac);
2741 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
2742 iface_id: Some(fake_iface_id),
2743 ..Default::default()
2744 };
2745 responder.send(Ok(&response)).expect("sending fake iface id");
2746 }
2747 );
2748 assert_matches!(exec.run_until_stalled(&mut get_ap_future), Poll::Ready(_));
2749 }
2750
2751 #[fuchsia::test]
2752 fn test_suggested_mac_does_not_apply_to_client() {
2753 let mut exec = TestExecutor::new();
2754 let mut test_values = test_setup();
2755 let mut phy_manager = PhyManager::new(
2756 test_values.monitor_proxy,
2757 recovery::lookup_recovery_profile(""),
2758 false,
2759 test_values.node,
2760 test_values.telemetry_sender,
2761 test_values.recovery_sender,
2762 );
2763
2764 let fake_iface_id = 1;
2767 let fake_phy_id = 1;
2768 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2769 let phy_container = PhyContainer::new(fake_mac_roles.clone());
2770
2771 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2772
2773 let mac: MacAddr = [1, 2, 3, 4, 5, 6].into();
2775 phy_manager.suggest_ap_mac(mac);
2776
2777 let start_client_future =
2779 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2780 let mut start_client_future = pin!(start_client_future);
2781 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Pending);
2782
2783 assert_matches!(
2785 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
2786 Poll::Ready(Some(Ok(
2787 fidl_service::DeviceMonitorRequest::CreateIface {
2788 payload,
2789 responder,
2790 }
2791 ))) => {
2792 assert_eq!(payload.sta_address, Some(ieee80211::NULL_ADDR.to_array()));
2793 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
2794 iface_id: Some(fake_iface_id),
2795 ..Default::default()
2796 };
2797 responder.send(Ok(&response)).expect("sending fake iface id");
2798 }
2799 );
2800 assert_matches!(exec.run_until_stalled(&mut start_client_future), Poll::Ready(_));
2801 }
2802
2803 #[fuchsia::test]
2805 fn test_iface_creation_fails_during_start_client_connections() {
2806 let mut exec = TestExecutor::new();
2807 let test_values = test_setup();
2808 let mut phy_manager = PhyManager::new(
2809 test_values.monitor_proxy,
2810 recovery::lookup_recovery_profile(""),
2811 false,
2812 test_values.node,
2813 test_values.telemetry_sender,
2814 test_values.recovery_sender,
2815 );
2816
2817 drop(test_values.monitor_stream);
2819
2820 let fake_phy_id = 1;
2823 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2824 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2825
2826 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2829
2830 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2831
2832 {
2833 let start_client_future = phy_manager
2835 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2836 let mut start_client_future = pin!(start_client_future);
2837 assert!(exec.run_until_stalled(&mut start_client_future).is_ready());
2838 }
2839
2840 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
2842 assert_eq!(
2843 phy_manager.phys[&fake_phy_id].defects.events[0].value,
2844 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 1 })
2845 );
2846 }
2847
2848 #[fuchsia::test]
2849 fn test_all_iface_creation_failures_retained_across_multiple_phys() {
2850 let mut exec = TestExecutor::new();
2851 let test_values = test_setup();
2852 let mut phy_manager = PhyManager::new(
2853 test_values.monitor_proxy,
2854 recovery::lookup_recovery_profile(""),
2855 false,
2856 test_values.node,
2857 test_values.telemetry_sender,
2858 test_values.recovery_sender,
2859 );
2860
2861 drop(test_values.monitor_stream);
2863
2864 for fake_phy_id in 0..2 {
2867 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
2868 let mut phy_container = PhyContainer::new(fake_mac_roles.clone());
2869
2870 phy_container.defects = EventHistory::<Defect>::new(u32::MAX);
2873
2874 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
2875 }
2876
2877 let start_client_future =
2878 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
2879 let mut start_client_future = pin!(start_client_future);
2880 assert_matches!(exec.run_until_stalled(&mut start_client_future),
2881 Poll::Ready(iface_ids) => {
2882 assert_eq!(iface_ids.len(), 2);
2883 assert_eq!(iface_ids[&0], Err(PhyManagerError::IfaceCreateFailure));
2884 assert_eq!(iface_ids[&1], Err(PhyManagerError::IfaceCreateFailure));
2885 }
2886 );
2887 }
2888
2889 #[run_singlethreaded(test)]
2893 async fn get_phy_ids_no_phys() {
2894 let test_values = test_setup();
2895 let phy_manager = PhyManager::new(
2896 test_values.monitor_proxy,
2897 recovery::lookup_recovery_profile(""),
2898 false,
2899 test_values.node,
2900 test_values.telemetry_sender,
2901 test_values.recovery_sender,
2902 );
2903 assert_eq!(phy_manager.get_phy_ids(), Vec::<u16>::new());
2904 }
2905
2906 #[fuchsia::test]
2909 fn get_phy_ids_single_phy() {
2910 let mut exec = TestExecutor::new();
2911 let mut test_values = test_setup();
2912 let mut phy_manager = PhyManager::new(
2913 test_values.monitor_proxy,
2914 recovery::lookup_recovery_profile(""),
2915 false,
2916 test_values.node,
2917 test_values.telemetry_sender,
2918 test_values.recovery_sender,
2919 );
2920
2921 {
2922 let add_phy_fut = phy_manager.add_phy(1);
2923 let mut add_phy_fut = pin!(add_phy_fut);
2924 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2925 send_get_supported_mac_roles_response(
2926 &mut exec,
2927 &mut test_values.monitor_stream,
2928 Ok(&[]),
2929 );
2930 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2931 }
2932
2933 assert_eq!(phy_manager.get_phy_ids(), vec![1]);
2934 }
2935
2936 #[fuchsia::test]
2939 fn get_phy_ids_two_phys() {
2940 let mut exec = TestExecutor::new();
2941 let mut test_values = test_setup();
2942 let mut phy_manager = PhyManager::new(
2943 test_values.monitor_proxy,
2944 recovery::lookup_recovery_profile(""),
2945 false,
2946 test_values.node,
2947 test_values.telemetry_sender,
2948 test_values.recovery_sender,
2949 );
2950
2951 {
2952 let add_phy_fut = phy_manager.add_phy(1);
2953 let mut add_phy_fut = pin!(add_phy_fut);
2954 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2955 send_get_supported_mac_roles_response(
2956 &mut exec,
2957 &mut test_values.monitor_stream,
2958 Ok(&[]),
2959 );
2960 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2961 }
2962
2963 {
2964 let add_phy_fut = phy_manager.add_phy(2);
2965 let mut add_phy_fut = pin!(add_phy_fut);
2966 assert!(exec.run_until_stalled(&mut add_phy_fut).is_pending());
2967 send_get_supported_mac_roles_response(
2968 &mut exec,
2969 &mut test_values.monitor_stream,
2970 Ok(&[]),
2971 );
2972 assert!(exec.run_until_stalled(&mut add_phy_fut).is_ready());
2973 }
2974
2975 let phy_ids = phy_manager.get_phy_ids();
2976 assert!(phy_ids.contains(&1), "expected phy_ids to contain `1`, but phy_ids={phy_ids:?}");
2977 assert!(phy_ids.contains(&2), "expected phy_ids to contain `2`, but phy_ids={phy_ids:?}");
2978 }
2979
2980 #[run_singlethreaded(test)]
2982 async fn log_phy_add_failure() {
2983 let test_values = test_setup();
2984 let mut phy_manager = PhyManager::new(
2985 test_values.monitor_proxy,
2986 recovery::lookup_recovery_profile(""),
2987 false,
2988 test_values.node,
2989 test_values.telemetry_sender,
2990 test_values.recovery_sender,
2991 );
2992
2993 assert_data_tree!(test_values.inspector, root: {
2994 phy_manager: {
2995 phy_add_fail_count: 0u64,
2996 },
2997 });
2998
2999 phy_manager.log_phy_add_failure();
3000 assert_data_tree!(test_values.inspector, root: {
3001 phy_manager: {
3002 phy_add_fail_count: 1u64,
3003 },
3004 });
3005 }
3006
3007 #[fuchsia::test]
3010 fn test_set_country_code() {
3011 let mut exec = TestExecutor::new();
3012 let mut test_values = test_setup();
3013 let mut phy_manager = PhyManager::new(
3014 test_values.monitor_proxy,
3015 recovery::lookup_recovery_profile(""),
3016 false,
3017 test_values.node,
3018 test_values.telemetry_sender,
3019 test_values.recovery_sender,
3020 );
3021
3022 let _ = phy_manager.phys.insert(
3024 0,
3025 PhyContainer {
3026 supported_mac_roles: HashSet::new(),
3027 client_ifaces: HashSet::new(),
3028 ap_ifaces: HashSet::new(),
3029 destroyed_ifaces: HashSet::new(),
3030 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3031 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3032 },
3033 );
3034 let _ = phy_manager.phys.insert(
3035 1,
3036 PhyContainer {
3037 supported_mac_roles: HashSet::new(),
3038 client_ifaces: HashSet::new(),
3039 ap_ifaces: HashSet::new(),
3040 destroyed_ifaces: HashSet::new(),
3041 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3042 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3043 },
3044 );
3045
3046 assert!(phy_manager.saved_country_code.is_none());
3048
3049 {
3051 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3052 let mut set_country_fut = pin!(set_country_fut);
3053
3054 for _ in 0..2 {
3056 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3057 assert_matches!(
3058 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3059 Poll::Ready(Some(Ok(
3060 fidl_service::DeviceMonitorRequest::SetCountry {
3061 req: fidl_service::SetCountryRequest {
3062 phy_id: _,
3063 alpha2: [b'U', b'S'],
3064 },
3065 responder,
3066 }
3067 ))) => {
3068 responder.send(ZX_OK).expect("sending fake set country response");
3069 }
3070 );
3071 }
3072
3073 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3074 }
3075 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3076
3077 {
3080 let set_country_fut = phy_manager.set_country_code(None);
3081 let mut set_country_fut = pin!(set_country_fut);
3082
3083 for _ in 0..2 {
3085 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3086 assert_matches!(
3087 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3088 Poll::Ready(Some(Ok(
3089 fidl_service::DeviceMonitorRequest::ClearCountry {
3090 req: fidl_service::ClearCountryRequest {
3091 phy_id: _,
3092 },
3093 responder,
3094 }
3095 ))) => {
3096 responder.send(ZX_OK).expect("sending fake clear country response");
3097 }
3098 );
3099 }
3100
3101 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Ready(Ok(())));
3102 }
3103 assert_eq!(phy_manager.saved_country_code, None);
3104 }
3105
3106 #[fuchsia::test]
3108 fn test_setting_country_code_fails() {
3109 let mut exec = TestExecutor::new();
3110 let mut test_values = test_setup();
3111 let mut phy_manager = PhyManager::new(
3112 test_values.monitor_proxy,
3113 recovery::lookup_recovery_profile(""),
3114 false,
3115 test_values.node,
3116 test_values.telemetry_sender,
3117 test_values.recovery_sender,
3118 );
3119
3120 let _ = phy_manager.phys.insert(
3122 0,
3123 PhyContainer {
3124 supported_mac_roles: HashSet::new(),
3125 client_ifaces: HashSet::new(),
3126 ap_ifaces: HashSet::new(),
3127 destroyed_ifaces: HashSet::new(),
3128 defects: EventHistory::new(DEFECT_RETENTION_SECONDS),
3129 recoveries: EventHistory::new(DEFECT_RETENTION_SECONDS),
3130 },
3131 );
3132
3133 assert!(phy_manager.saved_country_code.is_none());
3135
3136 {
3138 let set_country_fut = phy_manager.set_country_code(Some("US".parse().unwrap()));
3139 let mut set_country_fut = pin!(set_country_fut);
3140
3141 assert_matches!(exec.run_until_stalled(&mut set_country_fut), Poll::Pending);
3142 assert_matches!(
3143 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3144 Poll::Ready(Some(Ok(
3145 fidl_service::DeviceMonitorRequest::SetCountry {
3146 req: fidl_service::SetCountryRequest {
3147 phy_id: 0,
3148 alpha2: [b'U', b'S'],
3149 },
3150 responder,
3151 }
3152 ))) => {
3153 responder
3155 .send(zx::sys::ZX_ERR_NOT_SUPPORTED)
3156 .expect("sending fake set country response");
3157 }
3158 );
3159
3160 assert_matches!(
3161 exec.run_until_stalled(&mut set_country_fut),
3162 Poll::Ready(Err(PhyManagerError::PhySetCountryFailure))
3163 );
3164 }
3165 assert_eq!(phy_manager.saved_country_code, Some("US".parse().unwrap()));
3166 }
3167
3168 #[fuchsia::test]
3170 fn test_recover_client_interfaces_succeeds() {
3171 let mut exec = TestExecutor::new();
3172 let mut test_values = test_setup();
3173 let mut phy_manager = PhyManager::new(
3174 test_values.monitor_proxy,
3175 recovery::lookup_recovery_profile(""),
3176 false,
3177 test_values.node,
3178 test_values.telemetry_sender,
3179 test_values.recovery_sender,
3180 );
3181 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3182
3183 phy_manager.client_connections_enabled = true;
3185
3186 for phy_id in 0..4 {
3189 let fake_mac_roles = fake_mac_roles.clone();
3190 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3191
3192 if phy_id.is_multiple_of(2) {
3194 let phy_container = phy_manager.phys.get_mut(&phy_id).expect("missing PHY");
3195 let _ = phy_container.client_ifaces.insert(phy_id);
3196 }
3197 }
3198
3199 {
3203 let recovery_fut =
3204 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3205 let mut recovery_fut = pin!(recovery_fut);
3206 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3207
3208 loop {
3209 match exec.run_until_stalled(&mut recovery_fut) {
3213 Poll::Pending => {}
3214 Poll::Ready(iface_ids) => {
3215 if iface_ids.values().any(Result::is_err) {
3216 panic!("recovery failed unexpectedly");
3217 }
3218 let iface_ids: Vec<_> =
3219 iface_ids.into_values().flat_map(Result::unwrap).collect();
3220 assert!(iface_ids.contains(&1));
3221 assert!(iface_ids.contains(&3));
3222 break;
3223 }
3224 }
3225
3226 assert_matches!(
3229 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3230 Poll::Ready(Some(Ok(
3231 fidl_service::DeviceMonitorRequest::CreateIface {
3232 payload,
3233 responder,
3234 }
3235 ))) => {
3236 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3237 iface_id: Some(payload.phy_id.unwrap()),
3238 ..Default::default()
3239 };
3240 responder.send(Ok(&response)).expect("sending fake iface id");
3241 });
3242 }
3243 }
3244
3245 for phy_id in phy_manager.phys.keys() {
3248 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3249 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3250 }
3251 }
3252
3253 #[fuchsia::test]
3255 fn test_recover_client_interfaces_fails() {
3256 let mut exec = TestExecutor::new();
3257 let mut test_values = test_setup();
3258 let mut phy_manager = PhyManager::new(
3259 test_values.monitor_proxy,
3260 recovery::lookup_recovery_profile(""),
3261 false,
3262 test_values.node,
3263 test_values.telemetry_sender,
3264 test_values.recovery_sender,
3265 );
3266 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3267
3268 phy_manager.client_connections_enabled = true;
3270
3271 for phy_id in 0..3 {
3276 let _ = phy_manager.phys.insert(phy_id, PhyContainer::new(fake_mac_roles.clone()));
3277 }
3278
3279 {
3281 let recovery_fut =
3282 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3283 let mut recovery_fut = pin!(recovery_fut);
3284 assert_matches!(exec.run_until_stalled(&mut recovery_fut), Poll::Pending);
3285
3286 loop {
3287 match exec.run_until_stalled(&mut recovery_fut) {
3288 Poll::Pending => {}
3289 Poll::Ready(iface_ids) => {
3290 assert!(iface_ids.values().any(Result::is_err));
3291 let iface_ids: Vec<_> =
3292 iface_ids.into_values().filter_map(Result::ok).flatten().collect();
3293 assert_eq!(iface_ids, vec![1]);
3294 break;
3295 }
3296 }
3297
3298 assert_matches!(
3301 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
3302 Poll::Ready(Some(Ok(
3303 fidl_service::DeviceMonitorRequest::CreateIface {
3304 payload,
3305 responder,
3306 }
3307 ))) => {
3308 let iface_id = payload.phy_id.unwrap();
3309 let response = fidl_service::DeviceMonitorCreateIfaceResponse {
3310 iface_id: Some(iface_id),
3311 ..Default::default()
3312 };
3313
3314 match payload.phy_id.unwrap() {
3317 1 => {
3318 responder.send(Ok(&response)).expect("sending fake iface id")
3319 },
3320 _ => responder.send(Err(fidl_service::DeviceMonitorError::unknown())).expect("sending fake iface id"),
3321 };
3322 }
3323 );
3324 }
3325 }
3326
3327 for phy_id in phy_manager.phys.keys() {
3329 match phy_id {
3330 1 => {
3331 assert_eq!(phy_manager.phys[phy_id].client_ifaces.len(), 1);
3332 assert!(phy_manager.phys[phy_id].client_ifaces.contains(phy_id));
3333 }
3334 _ => assert!(phy_manager.phys[phy_id].client_ifaces.is_empty()),
3335 }
3336 }
3337 }
3338
3339 #[fuchsia::test]
3342 fn test_recover_client_interfaces_while_disabled() {
3343 let mut exec = TestExecutor::new();
3344 let test_values = test_setup();
3345 let mut phy_manager = PhyManager::new(
3346 test_values.monitor_proxy,
3347 recovery::lookup_recovery_profile(""),
3348 false,
3349 test_values.node,
3350 test_values.telemetry_sender,
3351 test_values.recovery_sender,
3352 );
3353
3354 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3357 let _ = phy_manager.phys.insert(0, PhyContainer::new(fake_mac_roles));
3358
3359 {
3362 let recovery_fut =
3363 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3364 let mut recovery_fut = pin!(recovery_fut);
3365 assert_matches!(
3366 exec.run_until_stalled(&mut recovery_fut),
3367 Poll::Ready(recovered_ifaces) => {
3368 assert!(recovered_ifaces.is_empty());
3369 }
3370 );
3371 }
3372
3373 for (_, phy_container) in phy_manager.phys {
3375 assert!(phy_container.client_ifaces.is_empty());
3376 }
3377 }
3378
3379 #[fuchsia::test]
3382 fn test_start_after_unsuccessful_stop() {
3383 let mut exec = TestExecutor::new();
3384 let test_values = test_setup();
3385 let mut phy_manager = PhyManager::new(
3386 test_values.monitor_proxy,
3387 recovery::lookup_recovery_profile(""),
3388 false,
3389 test_values.node,
3390 test_values.telemetry_sender,
3391 test_values.recovery_sender,
3392 );
3393
3394 assert!(!phy_manager.client_connections_enabled);
3396
3397 let fake_phy_id = 1;
3399 let fake_mac_roles = vec![fidl_common::WlanMacRole::Client];
3400 let mut phy_container = PhyContainer::new(fake_mac_roles);
3401 let fake_iface_id = 1;
3403 let _ = phy_container.client_ifaces.insert(fake_iface_id);
3404 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
3405
3406 {
3409 let start_client_future =
3410 phy_manager.create_all_client_ifaces(CreateClientIfacesReason::RecoverClientIfaces);
3411 let mut start_client_future = pin!(start_client_future);
3412 assert_matches!(
3413 exec.run_until_stalled(&mut start_client_future),
3414 Poll::Ready(v) => {
3415 assert!(v.is_empty())
3416 });
3417 }
3418
3419 {
3422 let start_client_future = phy_manager
3423 .create_all_client_ifaces(CreateClientIfacesReason::StartClientConnections);
3424 let mut start_client_future = pin!(start_client_future);
3425 assert_matches!(
3426 exec.run_until_stalled(&mut start_client_future),
3427 Poll::Ready(iface_ids) => {
3428 assert_eq!(iface_ids.into_values().collect::<Vec<_>>(), vec![Ok(vec![1])]);
3429 }
3430 );
3431 }
3432 }
3433
3434 #[fuchsia::test]
3436 fn test_client_connections_enabled_when_enabled() {
3437 let _exec = TestExecutor::new();
3438 let test_values = test_setup();
3439 let mut phy_manager = PhyManager::new(
3440 test_values.monitor_proxy,
3441 recovery::lookup_recovery_profile(""),
3442 false,
3443 test_values.node,
3444 test_values.telemetry_sender,
3445 test_values.recovery_sender,
3446 );
3447
3448 phy_manager.client_connections_enabled = true;
3449 assert!(phy_manager.client_connections_enabled());
3450 }
3451
3452 #[fuchsia::test]
3454 fn test_client_connections_enabled_when_disabled() {
3455 let _exec = TestExecutor::new();
3456 let test_values = test_setup();
3457 let mut phy_manager = PhyManager::new(
3458 test_values.monitor_proxy,
3459 recovery::lookup_recovery_profile(""),
3460 false,
3461 test_values.node,
3462 test_values.telemetry_sender,
3463 test_values.recovery_sender,
3464 );
3465
3466 phy_manager.client_connections_enabled = false;
3467 assert!(!phy_manager.client_connections_enabled());
3468 }
3469
3470 #[fuchsia::test]
3471 fn test_create_iface_succeeds() {
3472 let mut exec = TestExecutor::new();
3473 let mut test_values = test_setup();
3474 let mut phy_manager = PhyManager::new(
3475 test_values.monitor_proxy,
3476 recovery::lookup_recovery_profile(""),
3477 false,
3478 test_values.node,
3479 test_values.telemetry_sender,
3480 test_values.recovery_sender,
3481 );
3482
3483 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3485 let mut fut = pin!(fut);
3486
3487 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3489
3490 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, Some(0));
3492
3493 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(0)));
3495
3496 assert_matches!(
3498 test_values.telemetry_receiver.try_next(),
3499 Ok(Some(TelemetryEvent::IfaceCreationResult(Ok(()))))
3500 )
3501 }
3502
3503 #[fuchsia::test]
3504 fn test_create_iface_fails() {
3505 let mut exec = TestExecutor::new();
3506 let mut test_values = test_setup();
3507 let mut phy_manager = PhyManager::new(
3508 test_values.monitor_proxy,
3509 recovery::lookup_recovery_profile(""),
3510 false,
3511 test_values.node,
3512 test_values.telemetry_sender,
3513 test_values.recovery_sender,
3514 );
3515 let mut phy_container = PhyContainer::new(vec![]);
3516 let _ = phy_container.client_ifaces.insert(0);
3517 let _ = phy_manager.phys.insert(0, phy_container);
3518
3519 {
3520 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3522 let mut fut = pin!(fut);
3523
3524 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3526
3527 send_create_iface_response(&mut exec, &mut test_values.monitor_stream, None);
3529
3530 assert_matches!(
3532 exec.run_until_stalled(&mut fut),
3533 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3534 );
3535
3536 assert_matches!(
3538 test_values.telemetry_receiver.try_next(),
3539 Ok(Some(TelemetryEvent::IfaceCreationResult(Err(()))))
3540 );
3541 }
3542
3543 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3545 assert_eq!(
3546 phy_manager.phys[&0].defects.events[0].value,
3547 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3548 );
3549 }
3550
3551 #[fuchsia::test]
3552 fn test_create_iface_request_fails() {
3553 let mut exec = TestExecutor::new();
3554 let mut test_values = test_setup();
3555 let mut phy_manager = PhyManager::new(
3556 test_values.monitor_proxy,
3557 recovery::lookup_recovery_profile(""),
3558 false,
3559 test_values.node,
3560 test_values.telemetry_sender,
3561 test_values.recovery_sender,
3562 );
3563 let mut phy_container = PhyContainer::new(vec![]);
3564 let _ = phy_container.client_ifaces.insert(0);
3565 let _ = phy_manager.phys.insert(0, phy_container);
3566
3567 drop(test_values.monitor_stream);
3568
3569 {
3570 let fut = phy_manager.create_iface(0, fidl_common::WlanMacRole::Client, NULL_ADDR);
3572 let mut fut = pin!(fut);
3573
3574 assert_matches!(
3576 exec.run_until_stalled(&mut fut),
3577 Poll::Ready(Err(PhyManagerError::IfaceCreateFailure))
3578 );
3579
3580 assert_matches!(
3582 test_values.telemetry_receiver.try_next(),
3583 Ok(Some(TelemetryEvent::IfaceCreationResult(Err(()))))
3584 );
3585 }
3586
3587 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
3589 assert_eq!(
3590 phy_manager.phys[&0].defects.events[0].value,
3591 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 })
3592 );
3593 }
3594
3595 #[fuchsia::test]
3596 fn test_destroy_iface_succeeds() {
3597 let mut exec = TestExecutor::new();
3598 let mut test_values = test_setup();
3599
3600 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3602 let mut fut = pin!(fut);
3603
3604 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3606
3607 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
3609
3610 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3612
3613 assert_matches!(
3615 test_values.telemetry_receiver.try_next(),
3616 Ok(Some(TelemetryEvent::IfaceDestructionResult(Ok(()))))
3617 )
3618 }
3619
3620 #[fuchsia::test]
3621 fn test_destroy_iface_not_found() {
3622 let mut exec = TestExecutor::new();
3623 let mut test_values = test_setup();
3624
3625 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3627 let mut fut = pin!(fut);
3628
3629 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3631
3632 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_ERR_NOT_FOUND);
3634
3635 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
3637
3638 assert_matches!(test_values.telemetry_receiver.try_next(), Err(_))
3640 }
3641
3642 #[fuchsia::test]
3643 fn test_destroy_iface_fails() {
3644 let mut exec = TestExecutor::new();
3645 let mut test_values = test_setup();
3646
3647 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3649 let mut fut = pin!(fut);
3650
3651 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
3653
3654 send_destroy_iface_response(
3656 &mut exec,
3657 &mut test_values.monitor_stream,
3658 zx::sys::ZX_ERR_NO_RESOURCES,
3659 );
3660
3661 assert_matches!(
3663 exec.run_until_stalled(&mut fut),
3664 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
3665 );
3666
3667 assert_matches!(
3669 test_values.telemetry_receiver.try_next(),
3670 Ok(Some(TelemetryEvent::IfaceDestructionResult(Err(()))))
3671 )
3672 }
3673
3674 #[fuchsia::test]
3675 fn test_destroy_iface_request_fails() {
3676 let mut exec = TestExecutor::new();
3677 let mut test_values = test_setup();
3678
3679 drop(test_values.monitor_stream);
3680
3681 let fut = destroy_iface(&test_values.monitor_proxy, 0, &test_values.telemetry_sender);
3683 let mut fut = pin!(fut);
3684
3685 assert_matches!(
3687 exec.run_until_stalled(&mut fut),
3688 Poll::Ready(Err(PhyManagerError::IfaceDestroyFailure))
3689 );
3690
3691 assert_matches!(
3693 test_values.telemetry_receiver.try_next(),
3694 Ok(Some(TelemetryEvent::IfaceDestructionResult(Err(()))))
3695 )
3696 }
3697
3698 #[fuchsia::test]
3700 fn test_record_iface_event() {
3701 let _exec = TestExecutor::new();
3702 let test_values = test_setup();
3703
3704 let mut phy_manager = PhyManager::new(
3705 test_values.monitor_proxy,
3706 recovery::lookup_recovery_profile(""),
3707 false,
3708 test_values.node,
3709 test_values.telemetry_sender,
3710 test_values.recovery_sender,
3711 );
3712
3713 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3715 let _ = phy_manager.phys.insert(1, PhyContainer::new(vec![]));
3716 let _ = phy_manager.phys.insert(2, PhyContainer::new(vec![]));
3717 let _ = phy_manager.phys.insert(3, PhyContainer::new(vec![]));
3718
3719 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
3721 let _ = phy_manager.phys.get_mut(&1).expect("missing PHY").client_ifaces.insert(456);
3722 let _ = phy_manager.phys.get_mut(&2).expect("missing PHY").client_ifaces.insert(789);
3723 let _ = phy_manager.phys.get_mut(&3).expect("missing PHY").ap_ifaces.insert(246);
3724
3725 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3727 phy_manager.phys.get_mut(&1).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3728 phy_manager.phys.get_mut(&2).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3729 phy_manager.phys.get_mut(&3).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3730
3731 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
3733 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }));
3734 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 }));
3735 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
3736
3737 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 }));
3739
3740 assert_eq!(phy_manager.phys[&0].defects.events.len(), 2);
3742 assert_eq!(
3743 phy_manager.phys[&0].defects.events[0].value,
3744 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 })
3745 );
3746 assert_eq!(
3747 phy_manager.phys[&0].defects.events[1].value,
3748 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 })
3749 );
3750 assert_eq!(phy_manager.phys[&1].defects.events.len(), 1);
3751 assert_eq!(
3752 phy_manager.phys[&1].defects.events[0].value,
3753 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 })
3754 );
3755 assert_eq!(phy_manager.phys[&2].defects.events.len(), 1);
3756 assert_eq!(
3757 phy_manager.phys[&2].defects.events[0].value,
3758 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 789 })
3759 );
3760 assert_eq!(phy_manager.phys[&3].defects.events.len(), 1);
3761 assert_eq!(
3762 phy_manager.phys[&3].defects.events[0].value,
3763 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 246 })
3764 );
3765 }
3766
3767 #[fuchsia::test]
3769 fn test_aps_do_not_record_client_defects() {
3770 let _exec = TestExecutor::new();
3771 let test_values = test_setup();
3772
3773 let mut phy_manager = PhyManager::new(
3774 test_values.monitor_proxy,
3775 recovery::lookup_recovery_profile(""),
3776 false,
3777 test_values.node,
3778 test_values.telemetry_sender,
3779 test_values.recovery_sender,
3780 );
3781
3782 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3784
3785 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").ap_ifaces.insert(123);
3787
3788 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3790
3791 phy_manager.record_defect(Defect::Iface(IfaceFailure::CanceledScan { iface_id: 123 }));
3793 phy_manager.record_defect(Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }));
3794 phy_manager.record_defect(Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 123 }));
3795 phy_manager.record_defect(Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 123 }));
3796
3797 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
3799 }
3800
3801 #[fuchsia::test]
3803 fn test_clients_do_not_record_ap_defects() {
3804 let _exec = TestExecutor::new();
3805 let test_values = test_setup();
3806
3807 let mut phy_manager = PhyManager::new(
3808 test_values.monitor_proxy,
3809 recovery::lookup_recovery_profile(""),
3810 false,
3811 test_values.node,
3812 test_values.telemetry_sender,
3813 test_values.recovery_sender,
3814 );
3815
3816 let _ = phy_manager.phys.insert(0, PhyContainer::new(vec![]));
3818
3819 let _ = phy_manager.phys.get_mut(&0).expect("missing PHY").client_ifaces.insert(123);
3821
3822 phy_manager.phys.get_mut(&0).expect("missing PHY").defects = EventHistory::new(u32::MAX);
3824
3825 phy_manager.record_defect(Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }));
3827
3828 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
3830 }
3831
3832 fn aggressive_test_recovery_profile(
3833 _phy_id: u16,
3834 _defect_history: &mut EventHistory<Defect>,
3835 _recovery_history: &mut EventHistory<RecoveryAction>,
3836 _latest_defect: Defect,
3837 ) -> Option<RecoveryAction> {
3838 Some(RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 }))
3839 }
3840
3841 #[test_case(
3842 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
3843 "recommend AP start recovery"
3844 )]
3845 #[test_case(
3846 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
3847 "recommend connection failure recovery"
3848 )]
3849 #[test_case(
3850 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
3851 "recommend empty scan recovery"
3852 )]
3853 #[test_case(
3854 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
3855 "recommend failed scan recovery"
3856 )]
3857 #[test_case(
3858 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
3859 "recommend canceled scan recovery"
3860 )]
3861 #[test_case(
3862 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
3863 "recommend iface destruction recovery"
3864 )]
3865 #[test_case(
3866 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
3867 "recommend iface creation recovery"
3868 )]
3869 #[fuchsia::test(add_test_attr = false)]
3870 fn test_recovery_action_sent_from_record_defect(defect: Defect) {
3871 let _exec = TestExecutor::new();
3872 let mut test_values = test_setup();
3873 let mut phy_manager = PhyManager::new(
3874 test_values.monitor_proxy,
3875 recovery::lookup_recovery_profile(""),
3876 false,
3877 test_values.node,
3878 test_values.telemetry_sender,
3879 test_values.recovery_sender,
3880 );
3881
3882 let mut phy_container = PhyContainer::new(vec![]);
3884 let _ = phy_container.ap_ifaces.insert(123);
3885 let _ = phy_container.client_ifaces.insert(456);
3886 let _ = phy_manager.phys.insert(0, phy_container);
3887
3888 phy_manager.recovery_profile = aggressive_test_recovery_profile;
3890
3891 phy_manager.record_defect(defect);
3893
3894 let recovery_action = test_values.recovery_receiver.try_next().unwrap().unwrap();
3896 assert_eq!(recovery_action.defect, defect);
3897 assert_eq!(
3898 recovery_action.action,
3899 RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3900 );
3901 }
3902
3903 #[test_case(
3904 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 123 }) ;
3905 "do not recommend AP start recovery"
3906 )]
3907 #[test_case(
3908 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
3909 "do not recommend connection failure recovery"
3910 )]
3911 #[test_case(
3912 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
3913 "do not recommend empty scan recovery"
3914 )]
3915 #[test_case(
3916 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
3917 "do not recommend failed scan recovery"
3918 )]
3919 #[test_case(
3920 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
3921 "do not recommend canceled scan recovery"
3922 )]
3923 #[test_case(
3924 Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }) ;
3925 "do not recommend iface destruction recovery"
3926 )]
3927 #[test_case(
3928 Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }) ;
3929 "do not recommend iface creation recovery"
3930 )]
3931 #[fuchsia::test(add_test_attr = false)]
3932 fn test_no_recovery_when_defect_contains_bad_ids(defect: Defect) {
3933 let _exec = TestExecutor::new();
3934 let mut test_values = test_setup();
3935
3936 let mut phy_manager = PhyManager::new(
3938 test_values.monitor_proxy,
3939 recovery::lookup_recovery_profile(""),
3940 false,
3941 test_values.node,
3942 test_values.telemetry_sender,
3943 test_values.recovery_sender,
3944 );
3945
3946 phy_manager.recovery_profile = aggressive_test_recovery_profile;
3948
3949 phy_manager.record_defect(defect);
3951
3952 assert!(test_values.recovery_receiver.try_next().is_err());
3954 }
3955
3956 #[test_case(
3957 recovery::RecoverySummary {
3958 defect: Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 0 }),
3959 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3960 },
3961 telemetry::RecoveryReason::ScanResultsEmpty(
3962 telemetry::ClientRecoveryMechanism::PhyReset
3963 ) ;
3964 "PHY reset for empty scan results"
3965 )]
3966 #[test_case(
3967 recovery::RecoverySummary {
3968 defect: Defect::Iface(IfaceFailure::CanceledScan { iface_id: 0 }),
3969 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3970 },
3971 telemetry::RecoveryReason::ScanCancellation(
3972 telemetry::ClientRecoveryMechanism::PhyReset
3973 ) ;
3974 "PHY reset for scan cancellation"
3975 )]
3976 #[test_case(
3977 recovery::RecoverySummary {
3978 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 0 }),
3979 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3980 },
3981 telemetry::RecoveryReason::ScanFailure(
3982 telemetry::ClientRecoveryMechanism::PhyReset
3983 ) ;
3984 "PHY reset for scan failure"
3985 )]
3986 #[test_case(
3987 recovery::RecoverySummary {
3988 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 0 }),
3989 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
3990 },
3991 telemetry::RecoveryReason::StartApFailure(
3992 telemetry::ApRecoveryMechanism::ResetPhy
3993 ) ;
3994 "PHY reset for start AP failure"
3995 )]
3996 #[test_case(
3997 recovery::RecoverySummary {
3998 defect: Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 0 }),
3999 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4000 },
4001 telemetry::RecoveryReason::ConnectFailure(
4002 telemetry::ClientRecoveryMechanism::PhyReset
4003 ) ;
4004 "PHY reset for connection failure"
4005 )]
4006 #[test_case(
4007 recovery::RecoverySummary {
4008 defect: Defect::Phy(PhyFailure::IfaceDestructionFailure { phy_id: 0 }),
4009 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4010 },
4011 telemetry::RecoveryReason::DestroyIfaceFailure(
4012 telemetry::PhyRecoveryMechanism::PhyReset
4013 ) ;
4014 "PHY reset for iface destruction failure"
4015 )]
4016 #[test_case(
4017 recovery::RecoverySummary {
4018 defect: Defect::Phy(PhyFailure::IfaceCreationFailure { phy_id: 0 }),
4019 action: RecoveryAction::PhyRecovery(PhyRecoveryOperation::ResetPhy { phy_id: 0 })
4020 },
4021 telemetry::RecoveryReason::CreateIfaceFailure(
4022 telemetry::PhyRecoveryMechanism::PhyReset
4023 ) ;
4024 "PHY reset for iface creation failure"
4025 )]
4026 #[fuchsia::test(add_test_attr = false)]
4027 fn test_log_recovery_action_sends_metrics(
4028 summary: recovery::RecoverySummary,
4029 expected_reason: telemetry::RecoveryReason,
4030 ) {
4031 let _exec = TestExecutor::new();
4032 let mut test_values = test_setup();
4033 let mut phy_manager = PhyManager::new(
4034 test_values.monitor_proxy,
4035 recovery::lookup_recovery_profile(""),
4036 false,
4037 test_values.node,
4038 test_values.telemetry_sender,
4039 test_values.recovery_sender,
4040 );
4041
4042 phy_manager.log_recovery_action(summary);
4044 assert_matches!(
4045 test_values.telemetry_receiver.try_next(),
4046 Ok(Some(TelemetryEvent::RecoveryEvent { reason } )) => {
4047 assert_eq!(reason, expected_reason);
4048 })
4049 }
4050
4051 #[test_case(
4052 Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 456 }) ;
4053 "recommend AP start recovery"
4054 )]
4055 #[test_case(
4056 Defect::Iface(IfaceFailure::ConnectionFailure { iface_id: 456 }) ;
4057 "recommend connection failure recovery"
4058 )]
4059 #[test_case(
4060 Defect::Iface(IfaceFailure::EmptyScanResults { iface_id: 456 }) ;
4061 "recommend empty scan recovery"
4062 )]
4063 #[test_case(
4064 Defect::Iface(IfaceFailure::FailedScan { iface_id: 456 }) ;
4065 "recommend failed scan recovery"
4066 )]
4067 #[test_case(
4068 Defect::Iface(IfaceFailure::CanceledScan { iface_id: 456 }) ;
4069 "recommend canceled scan recovery"
4070 )]
4071 #[fuchsia::test(add_test_attr = false)]
4072 fn log_defect_for_destroyed_iface(defect: Defect) {
4073 let _exec = TestExecutor::new();
4074 let test_values = test_setup();
4075
4076 let fake_phy_id = 123;
4077 let fake_iface_id = 456;
4078
4079 let mut phy_manager = PhyManager::new(
4082 test_values.monitor_proxy,
4083 recovery::lookup_recovery_profile(""),
4084 false,
4085 test_values.node,
4086 test_values.telemetry_sender,
4087 test_values.recovery_sender,
4088 );
4089 let mut phy_container = PhyContainer::new(vec![]);
4090 let _ = phy_container.destroyed_ifaces.insert(fake_iface_id);
4091 let _ = phy_manager.phys.insert(fake_phy_id, phy_container);
4092
4093 phy_manager.record_defect(defect);
4095 assert_eq!(phy_manager.phys[&fake_phy_id].defects.events.len(), 1);
4096 }
4097
4098 #[fuchsia::test]
4099 fn test_reset_request_fails() {
4100 let mut exec = TestExecutor::new();
4101 let test_values = test_setup();
4102
4103 drop(test_values.monitor_stream);
4105
4106 let fut = reset_phy(&test_values.monitor_proxy, 0);
4108 let mut fut = pin!(fut);
4109 assert_matches!(
4110 exec.run_until_stalled(&mut fut),
4111 Poll::Ready(Err(PhyManagerError::InternalError))
4112 );
4113 }
4114
4115 #[fuchsia::test]
4116 fn test_reset_fails() {
4117 let mut exec = TestExecutor::new();
4118 let mut test_values = test_setup();
4119
4120 let fut = reset_phy(&test_values.monitor_proxy, 0);
4122 let mut fut = pin!(fut);
4123 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4124
4125 assert_matches!(
4127 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4128 Poll::Ready(Some(Ok(
4129 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4130 ))) => {
4131 responder.send(Err(ZX_ERR_NOT_FOUND)).expect("sending fake reset response");
4132 }
4133 );
4134
4135 assert_matches!(
4137 exec.run_until_stalled(&mut fut),
4138 Poll::Ready(Err(PhyManagerError::PhyResetFailure))
4139 );
4140 }
4141
4142 #[fuchsia::test]
4143 fn test_reset_succeeds() {
4144 let mut exec = TestExecutor::new();
4145 let mut test_values = test_setup();
4146
4147 let fut = reset_phy(&test_values.monitor_proxy, 0);
4149 let mut fut = pin!(fut);
4150 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4151
4152 assert_matches!(
4154 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4155 Poll::Ready(Some(Ok(
4156 fidl_service::DeviceMonitorRequest::Reset { phy_id: 0, responder }
4157 ))) => {
4158 responder.send(Ok(())).expect("sending fake reset response");
4159 }
4160 );
4161
4162 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4164 }
4165
4166 #[fuchsia::test]
4167 fn test_disconnect_request_fails() {
4168 let mut exec = TestExecutor::new();
4169 let mut test_values = test_setup();
4170
4171 let fut = disconnect(&test_values.monitor_proxy, 0);
4173 let mut fut = pin!(fut);
4174 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4175
4176 let sme_server = assert_matches!(
4178 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4179 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4180 iface_id: 0, sme_server, responder
4181 }))) => {
4182 assert!(responder.send(Ok(())).is_ok());
4184 sme_server
4185 }
4186 );
4187
4188 drop(sme_server);
4190
4191 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4193 }
4194
4195 #[fuchsia::test]
4196 fn test_disconnect_succeeds() {
4197 let mut exec = TestExecutor::new();
4198 let mut test_values = test_setup();
4199
4200 let fut = disconnect(&test_values.monitor_proxy, 0);
4202 let mut fut = pin!(fut);
4203 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4204
4205 let sme_server = assert_matches!(
4207 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4208 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4209 iface_id: 0, sme_server, responder
4210 }))) => {
4211 assert!(responder.send(Ok(())).is_ok());
4213 sme_server
4214 }
4215 );
4216
4217 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4219 let mut sme_stream = sme_server.into_stream().into_future();
4220 assert_matches!(
4221 poll_sme_req(&mut exec, &mut sme_stream),
4222 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
4223 responder,
4224 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
4225 }) => {
4226 responder.send().expect("Failed to send disconnect response")
4227 }
4228 );
4229
4230 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4232 }
4233
4234 #[fuchsia::test]
4235 fn test_disconnect_cannot_get_sme() {
4236 let mut exec = TestExecutor::new();
4237 let test_values = test_setup();
4238
4239 drop(test_values.monitor_stream);
4241
4242 let fut = disconnect(&test_values.monitor_proxy, 0);
4244 let mut fut = pin!(fut);
4245 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4246 }
4247
4248 #[fuchsia::test]
4249 fn test_stop_ap_request_fails() {
4250 let mut exec = TestExecutor::new();
4251 let mut test_values = test_setup();
4252
4253 let fut = stop_ap(&test_values.monitor_proxy, 0);
4255 let mut fut = pin!(fut);
4256 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4257
4258 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4260 let sme_server = assert_matches!(
4261 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4262 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4263 iface_id: 0, sme_server, responder
4264 }))) => {
4265 assert!(responder.send(Ok(())).is_ok());
4267 sme_server
4268 }
4269 );
4270
4271 drop(sme_server);
4273
4274 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4276 }
4277
4278 #[fuchsia::test]
4279 fn test_stop_ap_fails() {
4280 let mut exec = TestExecutor::new();
4281 let mut test_values = test_setup();
4282
4283 let fut = stop_ap(&test_values.monitor_proxy, 0);
4285 let mut fut = pin!(fut);
4286 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4287
4288 let sme_server = assert_matches!(
4290 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4291 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4292 iface_id: 0, sme_server, responder
4293 }))) => {
4294 assert!(responder.send(Ok(())).is_ok());
4296 sme_server
4297 }
4298 );
4299
4300 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4302 let mut sme_stream = sme_server.into_stream().into_future();
4303 assert_matches!(
4304 poll_ap_sme_req(&mut exec, &mut sme_stream),
4305 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4306 responder,
4307 }) => {
4308 responder.send(fidl_sme::StopApResultCode::InternalError).expect("Failed to send stop AP response")
4309 }
4310 );
4311
4312 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4314 }
4315
4316 #[fuchsia::test]
4317 fn test_stop_ap_succeeds() {
4318 let mut exec = TestExecutor::new();
4319 let mut test_values = test_setup();
4320
4321 let fut = stop_ap(&test_values.monitor_proxy, 0);
4323 let mut fut = pin!(fut);
4324 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4325
4326 let sme_server = assert_matches!(
4328 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4329 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4330 iface_id: 0, sme_server, responder
4331 }))) => {
4332 assert!(responder.send(Ok(())).is_ok());
4334 sme_server
4335 }
4336 );
4337
4338 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4340 let mut sme_stream = sme_server.into_stream().into_future();
4341 assert_matches!(
4342 poll_ap_sme_req(&mut exec, &mut sme_stream),
4343 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4344 responder,
4345 }) => {
4346 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
4347 }
4348 );
4349
4350 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
4352 }
4353
4354 #[fuchsia::test]
4355 fn test_stop_ap_cannot_get_sme() {
4356 let mut exec = TestExecutor::new();
4357 let test_values = test_setup();
4358
4359 drop(test_values.monitor_stream);
4361
4362 let fut = stop_ap(&test_values.monitor_proxy, 0);
4364 let mut fut = pin!(fut);
4365 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(Err(_)));
4366 }
4367
4368 fn phy_manager_for_recovery_test(
4369 device_monitor: fidl_service::DeviceMonitorProxy,
4370 node: inspect::Node,
4371 telemetry_sender: TelemetrySender,
4372 recovery_action_sender: recovery::RecoveryActionSender,
4373 ) -> PhyManager {
4374 let mut phy_manager = PhyManager::new(
4375 device_monitor,
4376 recovery::lookup_recovery_profile("thresholded_recovery"),
4377 true,
4378 node,
4379 telemetry_sender,
4380 recovery_action_sender,
4381 );
4382
4383 let mut phy_container =
4385 PhyContainer::new(vec![fidl_common::WlanMacRole::Client, fidl_common::WlanMacRole::Ap]);
4386 assert!(phy_container.client_ifaces.insert(1,));
4387 assert!(phy_container.ap_ifaces.insert(2));
4388 assert!(phy_manager.phys.insert(0, phy_container).is_none());
4389
4390 phy_manager
4391 }
4392
4393 #[fuchsia::test]
4394 fn test_perform_recovery_destroy_nonexistent_iface() {
4395 let mut exec = TestExecutor::new();
4396 let mut test_values = test_setup();
4397 let mut phy_manager = phy_manager_for_recovery_test(
4398 test_values.monitor_proxy,
4399 test_values.node,
4400 test_values.telemetry_sender,
4401 test_values.recovery_sender,
4402 );
4403
4404 let summary = recovery::RecoverySummary {
4406 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 123 }),
4407 action: recovery::RecoveryAction::PhyRecovery(
4408 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 123 },
4409 ),
4410 };
4411
4412 {
4414 let fut = phy_manager.perform_recovery(summary);
4415 let mut fut = pin!(fut);
4416 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4417 }
4418
4419 assert_matches!(
4421 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4422 Poll::Pending
4423 );
4424 }
4425
4426 #[fuchsia::test]
4427 fn test_perform_recovery_destroy_client_iface_fails() {
4428 let mut exec = TestExecutor::new();
4429 let test_values = test_setup();
4430 let mut phy_manager = phy_manager_for_recovery_test(
4431 test_values.monitor_proxy,
4432 test_values.node,
4433 test_values.telemetry_sender,
4434 test_values.recovery_sender,
4435 );
4436
4437 drop(test_values.monitor_stream);
4439
4440 let summary = recovery::RecoverySummary {
4442 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4443 action: recovery::RecoveryAction::PhyRecovery(
4444 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4445 ),
4446 };
4447
4448 {
4449 let fut = phy_manager.perform_recovery(summary);
4450 let mut fut = pin!(fut);
4451 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4452 }
4453
4454 assert!(phy_manager.phys[&0].client_ifaces.contains(&1));
4456 }
4457
4458 #[fuchsia::test]
4459 fn test_perform_recovery_destroy_client_iface_succeeds() {
4460 let mut exec = TestExecutor::new();
4461 let mut test_values = test_setup();
4462 let mut phy_manager = phy_manager_for_recovery_test(
4463 test_values.monitor_proxy,
4464 test_values.node,
4465 test_values.telemetry_sender,
4466 test_values.recovery_sender,
4467 );
4468
4469 let summary = recovery::RecoverySummary {
4471 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4472 action: recovery::RecoveryAction::PhyRecovery(
4473 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 1 },
4474 ),
4475 };
4476
4477 {
4478 let fut = phy_manager.perform_recovery(summary);
4479 let mut fut = pin!(fut);
4480 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4481
4482 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4484
4485 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4487 }
4488
4489 assert!(!phy_manager.phys[&0].client_ifaces.contains(&1));
4491
4492 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&1));
4494 }
4495
4496 #[fuchsia::test]
4497 fn test_perform_recovery_destroy_ap_iface_fails() {
4498 let mut exec = TestExecutor::new();
4499 let test_values = test_setup();
4500 let mut phy_manager = phy_manager_for_recovery_test(
4501 test_values.monitor_proxy,
4502 test_values.node,
4503 test_values.telemetry_sender,
4504 test_values.recovery_sender,
4505 );
4506
4507 drop(test_values.monitor_stream);
4509
4510 let summary = recovery::RecoverySummary {
4512 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4513 action: recovery::RecoveryAction::PhyRecovery(
4514 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4515 ),
4516 };
4517
4518 {
4519 let fut = phy_manager.perform_recovery(summary);
4520 let mut fut = pin!(fut);
4521 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4522 }
4523
4524 assert!(phy_manager.phys[&0].ap_ifaces.contains(&2));
4526 }
4527
4528 #[fuchsia::test]
4529 fn test_perform_recovery_destroy_ap_iface_succeeds() {
4530 let mut exec = TestExecutor::new();
4531 let mut test_values = test_setup();
4532 let mut phy_manager = phy_manager_for_recovery_test(
4533 test_values.monitor_proxy,
4534 test_values.node,
4535 test_values.telemetry_sender,
4536 test_values.recovery_sender,
4537 );
4538
4539 let summary = recovery::RecoverySummary {
4541 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4542 action: recovery::RecoveryAction::PhyRecovery(
4543 recovery::PhyRecoveryOperation::DestroyIface { iface_id: 2 },
4544 ),
4545 };
4546
4547 {
4548 let fut = phy_manager.perform_recovery(summary);
4549 let mut fut = pin!(fut);
4550 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4551
4552 send_destroy_iface_response(&mut exec, &mut test_values.monitor_stream, ZX_OK);
4554
4555 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4557 }
4558
4559 assert!(!phy_manager.phys[&0].ap_ifaces.contains(&2));
4561
4562 assert!(phy_manager.phys[&0].destroyed_ifaces.contains(&2));
4564 }
4565
4566 #[ignore]
4568 #[test_case(Some("US".parse().unwrap()); "Cached country code")]
4569 #[test_case(None; "No cached country code")]
4570 #[fuchsia::test(add_test_attr = false)]
4571 fn test_perform_recovery_reset_requests_phy_reset(
4572 cached_country_code: Option<client_types::CountryCode>,
4573 ) {
4574 let mut exec = TestExecutor::new();
4575 let mut test_values = test_setup();
4576 let mut phy_manager = phy_manager_for_recovery_test(
4577 test_values.monitor_proxy,
4578 test_values.node,
4579 test_values.telemetry_sender,
4580 test_values.recovery_sender,
4581 );
4582
4583 phy_manager.saved_country_code = cached_country_code;
4585
4586 let summary = recovery::RecoverySummary {
4588 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4589 action: recovery::RecoveryAction::PhyRecovery(
4590 recovery::PhyRecoveryOperation::ResetPhy { phy_id: 0 },
4591 ),
4592 };
4593
4594 let fut = phy_manager.perform_recovery(summary);
4595 let mut fut = pin!(fut);
4596 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4597
4598 assert_matches!(
4600 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4601 Poll::Ready(Some(Ok(
4602 fidl_service::DeviceMonitorRequest::Reset {
4603 phy_id: 0,
4604 responder,
4605 }
4606 ))) => {
4607 responder
4608 .send(Ok(()))
4609 .expect("failed to send reset response.");
4610 }
4611 );
4612
4613 if let Some(cached_cc) = cached_country_code {
4615 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4616 assert_matches!(
4617 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4618 Poll::Ready(Some(Ok(
4619 fidl_service::DeviceMonitorRequest::SetCountry {
4620 req: fidl_service::SetCountryRequest {
4621 phy_id: 0,
4622 alpha2: cc_in_req,
4623 },
4624 responder,
4625 }
4626 ))) => {
4627 assert_eq!(cc_in_req, <[u8; 2]>::from(cached_cc));
4628 responder
4629 .send(zx::sys::ZX_OK)
4630 .expect("failed to send setCountry response.");
4631 }
4632 );
4633 }
4634
4635 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4637 }
4638
4639 #[fuchsia::test]
4640 fn test_perform_recovery_disconnect_issues_request() {
4641 let mut exec = TestExecutor::new();
4642 let mut test_values = test_setup();
4643 let mut phy_manager = phy_manager_for_recovery_test(
4644 test_values.monitor_proxy,
4645 test_values.node,
4646 test_values.telemetry_sender,
4647 test_values.recovery_sender,
4648 );
4649
4650 let summary = recovery::RecoverySummary {
4652 defect: Defect::Iface(IfaceFailure::FailedScan { iface_id: 1 }),
4653 action: recovery::RecoveryAction::IfaceRecovery(
4654 recovery::IfaceRecoveryOperation::Disconnect { iface_id: 1 },
4655 ),
4656 };
4657
4658 let fut = phy_manager.perform_recovery(summary);
4659 let mut fut = pin!(fut);
4660 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4661
4662 let sme_server = assert_matches!(
4665 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4666 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetClientSme {
4667 iface_id: 1, sme_server, responder
4668 }))) => {
4669 assert!(responder.send(Ok(())).is_ok());
4671 sme_server
4672 }
4673 );
4674
4675 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4677 let mut sme_stream = sme_server.into_stream().into_future();
4678 assert_matches!(
4679 poll_sme_req(&mut exec, &mut sme_stream),
4680 Poll::Ready(fidl_fuchsia_wlan_sme::ClientSmeRequest::Disconnect{
4681 responder,
4682 reason: fidl_fuchsia_wlan_sme::UserDisconnectReason::Recovery
4683 }) => {
4684 responder.send().expect("Failed to send disconnect response")
4685 }
4686 );
4687
4688 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4690 }
4691
4692 #[fuchsia::test]
4693 fn test_perform_recovery_stop_ap_issues_request() {
4694 let mut exec = TestExecutor::new();
4695 let mut test_values = test_setup();
4696 let mut phy_manager = phy_manager_for_recovery_test(
4697 test_values.monitor_proxy,
4698 test_values.node,
4699 test_values.telemetry_sender,
4700 test_values.recovery_sender,
4701 );
4702
4703 let summary = recovery::RecoverySummary {
4705 defect: Defect::Iface(IfaceFailure::ApStartFailure { iface_id: 2 }),
4706 action: recovery::RecoveryAction::IfaceRecovery(
4707 recovery::IfaceRecoveryOperation::StopAp { iface_id: 2 },
4708 ),
4709 };
4710
4711 let fut = phy_manager.perform_recovery(summary);
4712 let mut fut = pin!(fut);
4713 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4714
4715 let sme_server = assert_matches!(
4718 exec.run_until_stalled(&mut test_values.monitor_stream.next()),
4719 Poll::Ready(Some(Ok(fidl_fuchsia_wlan_device_service::DeviceMonitorRequest::GetApSme {
4720 iface_id: 2, sme_server, responder
4721 }))) => {
4722 assert!(responder.send(Ok(())).is_ok());
4724 sme_server
4725 }
4726 );
4727
4728 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Pending);
4730 let mut sme_stream = sme_server.into_stream().into_future();
4731 assert_matches!(
4732 poll_ap_sme_req(&mut exec, &mut sme_stream),
4733 Poll::Ready(fidl_fuchsia_wlan_sme::ApSmeRequest::Stop{
4734 responder,
4735 }) => {
4736 responder.send(fidl_sme::StopApResultCode::Success).expect("Failed to send stop AP response")
4737 }
4738 );
4739
4740 assert_matches!(exec.run_until_stalled(&mut fut), Poll::Ready(()));
4742 }
4743
4744 #[fuchsia::test]
4745 fn test_log_timeout_defect() {
4746 let _exec = TestExecutor::new();
4747 let mut test_values = test_setup();
4748 let mut phy_manager = phy_manager_for_recovery_test(
4749 test_values.monitor_proxy,
4750 test_values.node,
4751 test_values.telemetry_sender,
4752 test_values.recovery_sender,
4753 );
4754
4755 assert_eq!(phy_manager.phys[&0].defects.events.len(), 0);
4757
4758 phy_manager.record_defect(Defect::Iface(IfaceFailure::Timeout {
4760 iface_id: 1,
4761 source: telemetry::TimeoutSource::Scan,
4762 }));
4763
4764 assert_eq!(phy_manager.phys[&0].defects.events.len(), 1);
4766
4767 assert_matches!(
4769 test_values.telemetry_receiver.try_next(),
4770 Ok(Some(TelemetryEvent::SmeTimeout { source: telemetry::TimeoutSource::Scan }))
4771 )
4772 }
4773}