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