1use crate::client::{
6 self as client_sme, ConnectResult, ConnectTransactionEvent, ConnectTransactionStream,
7 RoamResult, ScheduledScanReceiver,
8};
9use crate::{MlmeEventStream, MlmeSink, MlmeStream};
10use fidl::endpoints::{RequestStream, ServerEnd};
11use fidl_fuchsia_wlan_common as fidl_common;
12use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
13use fidl_fuchsia_wlan_mlme as fidl_mlme;
14use fidl_fuchsia_wlan_sme::{self as fidl_sme, ClientSmeRequest, TelemetryRequest};
15use fuchsia_sync::Mutex;
16use futures::channel::mpsc;
17use futures::prelude::*;
18use futures::select;
19use ieee80211::MacAddrBytes;
20use log::error;
21use std::pin::pin;
22use std::sync::Arc;
23use wlan_common::scan::write_vmo;
24
25pub type Endpoint = ServerEnd<fidl_sme::ClientSmeMarker>;
26type Sme = client_sme::ClientSme;
27
28#[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
29pub fn serve(
30 cfg: crate::Config,
31 device_info: fidl_mlme::DeviceInfo,
32 security_support: fidl_common::SecuritySupport,
33 spectrum_management_support: fidl_common::SpectrumManagementSupport,
34 event_stream: MlmeEventStream,
35 new_fidl_clients: mpsc::UnboundedReceiver<Endpoint>,
36 new_telemetry_fidl_clients: mpsc::UnboundedReceiver<
37 fidl::endpoints::ServerEnd<fidl_sme::TelemetryMarker>,
38 >,
39 inspector: fuchsia_inspect::Inspector,
40 inspect_node: fuchsia_inspect::Node,
41 power_manager: Arc<dyn wlan_power_manager::SyncPowerManager>,
42) -> (MlmeSink, MlmeStream, impl Future<Output = Result<(), anyhow::Error>>) {
43 let wpa3_supported =
44 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
45 && security_support.sae.as_ref().is_some_and(|sae| {
46 sae.driver_handler_supported.unwrap_or(false)
47 || sae.sme_handler_supported.unwrap_or(false)
48 });
49 let owe_supported =
50 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
51 && security_support.owe.as_ref().is_some_and(|owe| owe.supported.unwrap_or(false));
52 let cfg = client_sme::ClientConfig::from_config(cfg, wpa3_supported, owe_supported);
53 let (sme, mlme_sink, mlme_stream, time_stream) = Sme::new(
54 cfg,
55 device_info,
56 inspector,
57 inspect_node,
58 security_support,
59 spectrum_management_support,
60 power_manager,
61 );
62 let fut = async move {
63 let sme = Arc::new(Mutex::new(sme));
64 let mlme_sme = super::serve_mlme_sme(event_stream, Arc::clone(&sme), time_stream);
65 let sme_fidl = super::serve_fidl(&*sme, new_fidl_clients, handle_fidl_request);
66 let telemetry_fidl =
67 super::serve_fidl(&*sme, new_telemetry_fidl_clients, handle_telemetry_fidl_request);
68 let mlme_sme = pin!(mlme_sme);
69 let sme_fidl = pin!(sme_fidl);
70 select! {
71 mlme_sme = mlme_sme.fuse() => mlme_sme?,
72 sme_fidl = sme_fidl.fuse() => match sme_fidl? {},
73 telemetry_fidl = telemetry_fidl.fuse() => match telemetry_fidl? {},
74 }
75 Ok(())
76 };
77 (mlme_sink, mlme_stream, fut)
78}
79
80async fn handle_fidl_request(
81 sme: &Mutex<Sme>,
82 request: fidl_sme::ClientSmeRequest,
83) -> Result<(), fidl::Error> {
84 #[allow(clippy::unit_arg, reason = "mass allow for https://fxbug.dev/381896734")]
85 match request {
86 ClientSmeRequest::Scan { req, responder } => Ok(scan(sme, req, |result| match result {
87 Ok(scan_results) => responder.send(Ok(write_vmo(scan_results)?)).map_err(|e| e.into()),
88 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
89 })
90 .await
91 .unwrap_or_else(|e| error!("Error handling a scan transaction: {:?}", e))),
92 ClientSmeRequest::Connect { req, txn, .. } => Ok(connect(sme, txn, req)
93 .await
94 .unwrap_or_else(|e| error!("Error handling a connect transaction: {:?}", e))),
95 ClientSmeRequest::Roam { req, .. } => Ok(roam(sme, req)),
96 ClientSmeRequest::Disconnect { responder, reason } => {
97 disconnect(sme, reason, responder);
98 Ok(())
99 }
100 ClientSmeRequest::Status { responder } => responder.send(&status(sme)),
101 ClientSmeRequest::WmmStatus { responder } => wmm_status(sme, responder).await,
102 ClientSmeRequest::StartScheduledScan { req, txn, responder } => {
103 start_scheduled_scan(sme, req, txn, responder).await
104 }
105
106 ClientSmeRequest::GetScheduledScanEnabled { responder } => {
107 let receiver = sme.lock().on_get_scheduled_scan_enabled_command();
108 let resp = match receiver.await {
109 Ok(result) => result,
110 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
111 };
112 responder
113 .send(resp.as_ref().map(|r| !r.active_txn_ids.is_empty()).map_err(|e| *e))
114 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
115 Ok(())
116 }
117 ClientSmeRequest::ScanForController { req, responder } => {
118 Ok(scan(sme, req, |result| match result {
119 Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
120 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
121 })
122 .await
123 .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
124 }
125 ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
126 Ok(set_mac_address(sme, mac_addr, responder).await?)
127 }
128 ClientSmeRequest::InstallApfPacketFilter { program, responder } => {
129 let receiver = sme.lock().install_apf_packet_filter(program);
130 let resp = match receiver.await {
131 Ok(result) => result,
132 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
133 };
134 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
135 Ok(())
136 }
137 ClientSmeRequest::ReadApfPacketFilterData { responder } => {
138 let receiver = sme.lock().read_apf_packet_filter_data();
139 let resp = match receiver.await {
140 Ok(result) => result.map(|r| r.memory),
141 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
142 };
143 responder
144 .send(resp.as_ref().map(|m| &m[..]).map_err(|e| *e))
145 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
146 Ok(())
147 }
148 ClientSmeRequest::SetApfPacketFilterEnabled { enabled, responder } => {
149 let receiver = sme.lock().set_apf_packet_filter_enabled(enabled);
150 let resp = match receiver.await {
151 Ok(result) => result,
152 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
153 };
154 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
155 Ok(())
156 }
157 ClientSmeRequest::GetApfPacketFilterEnabled { responder } => {
158 let receiver = sme.lock().get_apf_packet_filter_enabled();
159 let resp = match receiver.await {
160 Ok(result) => result.map(|r| r.enabled),
161 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
162 };
163 responder
164 .send(resp.as_ref().map(|e| *e).map_err(|e| *e))
165 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
166 Ok(())
167 }
168 ClientSmeRequest::StartRssiMonitor { min_rssi_dbm, max_rssi_dbm, responder } => {
169 let receiver = sme.lock().start_rssi_monitor(min_rssi_dbm, max_rssi_dbm);
170 let resp = match receiver.await {
171 Ok(result) => result,
172 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
173 };
174 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
175 Ok(())
176 }
177 ClientSmeRequest::StopRssiMonitor { responder } => {
178 let receiver = sme.lock().stop_rssi_monitor();
179 let resp = match receiver.await {
180 Ok(result) => result,
181 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
182 };
183 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
184 Ok(())
185 }
186 }
187}
188
189async fn handle_telemetry_fidl_request(
190 sme: &Mutex<Sme>,
191 request: TelemetryRequest,
192) -> Result<(), fidl::Error> {
193 match request {
194 TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
195 let support_fut = sme.lock().query_telemetry_support();
196 let support = support_fut
197 .await
198 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
199 .and_then(|result| result);
200 responder.send(support.as_ref().map_err(|e| *e))
201 }
202 TelemetryRequest::GetIfaceStats { responder, .. } => {
203 let iface_stats_fut = sme.lock().iface_stats();
204 let iface_stats = iface_stats_fut
205 .await
206 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
207 .and_then(|stats| match stats {
208 fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
209 fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
210 });
211 responder.send(iface_stats.as_ref().map_err(|e| *e))
212 }
213 TelemetryRequest::GetHistogramStats { responder, .. } => {
214 let histogram_stats_fut = sme.lock().histogram_stats();
215 let histogram_stats = histogram_stats_fut
216 .await
217 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
218 .and_then(|stats| match stats {
219 fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
220 fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
221 });
222 responder.send(histogram_stats.as_ref().map_err(|e| *e))
223 }
224 TelemetryRequest::GetSignalReport { responder, .. } => {
225 let signal_report_fut = sme.lock().signal_report();
226 let signal_report = signal_report_fut
227 .await
228 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
229 .and_then(|result| result);
230 if let Ok(ref report) = signal_report
231 && let Some(ref conn_report) = report.connection_signal_report
232 {
233 sme.lock().on_signal_report(conn_report);
234 }
235 responder.send(signal_report.as_ref().map_err(|e| *e))
236 }
237 TelemetryRequest::CloneInspectVmo { responder } => {
238 let inspect_vmo =
239 sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
240 responder.send(inspect_vmo)
241 }
242 }
243}
244
245async fn scan(
246 sme: &Mutex<Sme>,
247 request: fidl_sme::ScanRequest,
248 responder: impl FnOnce(
249 Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
250 ) -> Result<(), anyhow::Error>,
251) -> Result<(), anyhow::Error> {
252 let receiver = sme.lock().on_scan_command(request);
253 let receive_result = match receiver.await {
254 Ok(receive_result) => receive_result,
255 Err(e) => {
256 error!("Scan receiver error: {:?}", e);
257 responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
258 return Ok(());
259 }
260 };
261
262 match receive_result {
263 Ok(scan_results) => {
264 let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
265 responder(Ok(results))
266 }
267 Err(mlme_scan_result_code) => {
268 let scan_error_code = match mlme_scan_result_code {
269 fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
270 error!("Internal scan error: {:?}", mlme_scan_result_code);
271 fidl_sme::ScanErrorCode::InternalError
272 }
273 fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
274 fidl_mlme::ScanResultCode::InternalError => {
275 fidl_sme::ScanErrorCode::InternalMlmeError
276 }
277 fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
278 fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
279 fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
280 }
281 };
282 responder(Err(scan_error_code))
283 }
284 }?;
285 Ok(())
286}
287
288async fn connect(
289 sme: &Mutex<Sme>,
290 txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
291 req: fidl_sme::ConnectRequest,
292) -> Result<(), anyhow::Error> {
293 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
294 let handle = match txn {
295 None => None,
296 Some(txn) => Some(txn.into_stream().control_handle()),
297 };
298 let connect_txn_stream = sme.lock().on_connect_command(req);
299 serve_connect_txn_stream(handle, connect_txn_stream).await?;
300 Ok(())
301}
302
303async fn serve_sched_scan_session(
306 txn_handle: fidl_sme::ScheduledScanTransactionControlHandle,
307 txn_stream: fidl_sme::ScheduledScanTransactionRequestStream,
308 session: ScheduledScanReceiver,
309) -> Result<(), anyhow::Error> {
310 let mut cancellation_stream = txn_stream.fuse();
312 let mut session_stream = session.fuse();
313 loop {
314 futures::select! {
315 scan_results = session_stream.next() => {
316 if let Some(vmo) = scan_results {
317 txn_handle.send_on_scheduled_scan_matches_available(vmo)?;
318 } else {
319 txn_handle.shutdown_with_epitaph(Ok(()));
321 break;
322 }
323 },
324 _ = cancellation_stream.next() => {
325 break;
328 }
329 }
330 }
331 Ok(())
332}
333
334async fn start_scheduled_scan(
336 sme: &Mutex<Sme>,
337 req: fidl_common::ScheduledScanRequest,
338 txn_server: ServerEnd<fidl_sme::ScheduledScanTransactionMarker>,
339 responder: fidl_sme::ClientSmeStartScheduledScanResponder,
340) -> Result<(), fidl::Error> {
341 let txn_receiver_stream = txn_server.into_stream();
342 let txn_sender_handle = txn_receiver_stream.control_handle();
343 let (receiver, session) = sme.lock().on_start_scheduled_scan_command(req);
344 match receiver.await.unwrap_or_else(|_| Err(zx::Status::CANCELED.into_raw())) {
345 Ok(()) => {
346 responder.send(Ok(()))?;
347 serve_sched_scan_session(txn_sender_handle, txn_receiver_stream, session)
348 .await
349 .unwrap_or_else(|e| {
350 error!("Error serving sched scan txn stream: {:?}", e);
351 });
352 }
353 Err(status) => {
354 responder.send(Err(status))?;
355 txn_sender_handle.shutdown_with_epitaph(zx::Status::err_from_raw(status));
356 }
357 }
358 Ok(())
359}
360
361async fn serve_connect_txn_stream(
362 handle: Option<fidl_sme::ConnectTransactionControlHandle>,
363 mut connect_txn_stream: ConnectTransactionStream,
364) -> Result<(), anyhow::Error> {
365 if let Some(handle) = handle {
366 loop {
367 match connect_txn_stream.next().await {
368 Some(event) => match event {
369 ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
370 let connect_result = convert_connect_result(&result, is_reconnect);
371 handle.send_on_connect_result(&connect_result)
372 }
373 ConnectTransactionEvent::OnRoamResult { result } => {
374 let roam_result = convert_roam_result(&result);
375 handle.send_on_roam_result(&roam_result)
376 }
377 ConnectTransactionEvent::OnDisconnect { info } => {
378 handle.send_on_disconnect(&info)
379 }
380 ConnectTransactionEvent::OnSignalReport { ind } => {
381 handle.send_on_signal_report(&ind)
382 }
383 ConnectTransactionEvent::OnRssiThresholdBreached { cur_rssi_dbm } => {
384 handle.send_on_rssi_threshold_breached(cur_rssi_dbm)
385 }
386 ConnectTransactionEvent::OnChannelSwitched { info } => {
387 handle.send_on_channel_switched(&info)
388 }
389 }?,
390 None => return Ok(()),
392 }
393 }
394 }
395 Ok(())
396}
397
398fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
399 sme.lock().on_roam_command(req);
400}
401
402fn disconnect(
403 sme: &Mutex<Sme>,
404 policy_disconnect_reason: fidl_sme::UserDisconnectReason,
405 responder: fidl_sme::ClientSmeDisconnectResponder,
406) {
407 sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
408}
409
410fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
411 sme.lock().status().into()
412}
413
414async fn wmm_status(
415 sme: &Mutex<Sme>,
416 responder: fidl_sme::ClientSmeWmmStatusResponder,
417) -> Result<(), fidl::Error> {
418 let receiver = sme.lock().wmm_status();
419 let wmm_status = match receiver.await {
420 Ok(result) => result,
421 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
422 };
423 responder.send(wmm_status.as_ref().map_err(|e| *e))
424}
425
426async fn set_mac_address(
427 sme: &Mutex<Sme>,
428 mac_addr: [u8; 6],
429 responder: fidl_sme::ClientSmeSetMacAddressResponder,
430) -> Result<(), fidl::Error> {
431 let receiver = sme.lock().set_mac_address(mac_addr);
432 let resp = match receiver.await {
433 Ok(Ok(())) => {
434 sme.lock().update_mac_address(mac_addr);
435 Ok(())
436 }
437 Ok(result @ Err(_)) => result,
438 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
439 };
440 responder.send(resp)
441}
442
443fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
444 let (code, is_credential_rejected) = match result {
445 ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
446 ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
447 ConnectResult::Failed(failure) => {
448 (failure.status_code(), failure.likely_due_to_credential_rejected())
449 }
450 };
451 fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
452}
453
454fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
455 match result {
456 RoamResult::Success(bss) => {
457 let bss_description =
458 Some(Box::new(fidl_ieee80211::BssDescription::from(*bss.clone())));
459 fidl_sme::RoamResult {
460 bssid: bss.bssid.to_array(),
461 status_code: fidl_ieee80211::StatusCode::Success,
462 original_association_maintained: false,
464 bss_description,
465 disconnect_info: None,
466 is_credential_rejected: false,
467 }
468 }
469 RoamResult::Failed(failure) => {
470 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
471 fidl_sme::RoamResult {
472 bssid: failure.selected_bssid.to_array(),
473 status_code: failure.status_code,
474 original_association_maintained: false,
478 bss_description: match &failure.selected_bss {
479 Some(bss) => Some(Box::new(bss.clone().into())),
480 None => None,
481 },
482 disconnect_info: Some(Box::new(failure.disconnect_info)),
483 is_credential_rejected: failure.likely_due_to_credential_rejected(),
484 }
485 }
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use super::*;
492 use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
493 use crate::test_utils;
494 use assert_matches::assert_matches;
495 use fidl::endpoints::create_proxy_and_stream;
496 use fidl_fuchsia_wlan_internal as fidl_internal;
497 use fidl_fuchsia_wlan_mlme::ScanResultCode;
498 use fidl_fuchsia_wlan_sme::{self as fidl_sme};
499 use fidl_fuchsia_wlan_stats as fidl_stats;
500 use fuchsia_async as fasync;
501 use futures::stream::StreamFuture;
502 use futures::task::Poll;
503 use rand::RngExt as _;
504 use rand::prelude::ThreadRng;
505 use std::pin::pin;
506 use test_case::test_case;
507 use wlan_common::random_bss_description;
508 use wlan_common::scan::{self, Incompatible};
509 use wlan_rsn::auth;
510
511 #[test]
512 fn test_convert_connect_result() {
513 assert_eq!(
514 convert_connect_result(&ConnectResult::Success, false),
515 fidl_sme::ConnectResult {
516 code: fidl_ieee80211::StatusCode::Success,
517 is_credential_rejected: false,
518 is_reconnect: false,
519 }
520 );
521 assert_eq!(
522 convert_connect_result(&ConnectResult::Canceled, true),
523 fidl_sme::ConnectResult {
524 code: fidl_ieee80211::StatusCode::Canceled,
525 is_credential_rejected: false,
526 is_reconnect: true,
527 }
528 );
529 let connect_result =
530 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::ShouldWait));
531 assert_eq!(
532 convert_connect_result(&connect_result, false),
533 fidl_sme::ConnectResult {
534 code: fidl_ieee80211::StatusCode::Canceled,
535 is_credential_rejected: false,
536 is_reconnect: false,
537 }
538 );
539
540 let connect_result =
541 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
542 auth_method: Some(auth::MethodName::Psk),
543 reason: EstablishRsnaFailureReason::InternalError,
544 }));
545 assert_eq!(
546 convert_connect_result(&connect_result, false),
547 fidl_sme::ConnectResult {
548 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
549 is_credential_rejected: false,
550 is_reconnect: false,
551 }
552 );
553
554 let connect_result =
555 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
556 auth_method: Some(auth::MethodName::Psk),
557 reason: EstablishRsnaFailureReason::RsnaResponseTimeout(
558 wlan_rsn::Error::LikelyWrongCredential,
559 ),
560 }));
561 assert_eq!(
562 convert_connect_result(&connect_result, false),
563 fidl_sme::ConnectResult {
564 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
565 is_credential_rejected: true,
566 is_reconnect: false,
567 }
568 );
569
570 let connect_result =
571 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
572 auth_method: Some(auth::MethodName::Psk),
573 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
574 wlan_rsn::Error::LikelyWrongCredential,
575 ),
576 }));
577 assert_eq!(
578 convert_connect_result(&connect_result, false),
579 fidl_sme::ConnectResult {
580 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
581 is_credential_rejected: true,
582 is_reconnect: false,
583 }
584 );
585
586 let connect_result =
587 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
588 auth_method: Some(auth::MethodName::Psk),
589 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
590 wlan_rsn::Error::MissingGtkProvider,
591 ),
592 }));
593 assert_eq!(
594 convert_connect_result(&connect_result, false),
595 fidl_sme::ConnectResult {
596 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
597 is_credential_rejected: false,
598 is_reconnect: false,
599 }
600 );
601
602 let connect_result =
603 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::InternalError));
604 assert_eq!(
605 convert_connect_result(&connect_result, false),
606 fidl_sme::ConnectResult {
607 code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
608 is_credential_rejected: false,
609 is_reconnect: false,
610 }
611 );
612 }
613
614 #[test_case(1, true; "with 1 result")]
619 #[test_case(2, true; "with 2 results")]
620 #[test_case(30, true; "with 30 results")]
621 #[test_case(4000, true; "with 4000 results")]
622 #[test_case(50000, false; "with 50000 results")]
623 #[test_case(100000, false; "with 100000 results")]
624 fn scan_results_are_effectively_unbounded(number_of_scan_results: usize, randomize: bool) {
625 let mut exec = fasync::TestExecutor::new();
626 let (client_sme_proxy, mut client_sme_stream) =
627 create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
628
629 async fn request_and_collect_result(
631 client_sme_proxy: &fidl_sme::ClientSmeProxy,
632 ) -> fidl_sme::ClientSmeScanResult {
633 client_sme_proxy
634 .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
635 channels: vec![],
636 }))
637 .await
638 .expect("FIDL request failed")
639 }
640
641 let result_fut = request_and_collect_result(&client_sme_proxy);
642 let mut result_fut = pin!(result_fut);
643
644 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
645
646 let mut rng = rand::rng();
648 let scan_result_list = if randomize {
649 (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
650 } else {
651 vec![random_scan_result(&mut rng).into(); number_of_scan_results]
652 };
653 assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
654 Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
655 req: _, responder,
656 }))) => {
657 let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
658 responder.send(Ok(vmo)).expect("failed to send scan results");
659 }
660 );
661
662 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
664 assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
665 })
666 }
667
668 #[test]
669 fn test_serve_connect_txn_stream() {
670 let mut exec = fasync::TestExecutor::new();
671
672 let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
673 let (fidl_client_proxy, fidl_connect_txn_stream) =
674 create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
675 let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
676 let mut fidl_client_fut = pin!(fidl_client_fut);
677 let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
678
679 let test_fut =
680 serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
681 let mut test_fut = pin!(test_fut);
682
683 sme_proxy
685 .unbounded_send(ConnectTransactionEvent::OnConnectResult {
686 result: ConnectResult::Success,
687 is_reconnect: true,
688 })
689 .expect("expect sending ConnectTransactionEvent to succeed");
690 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
691 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
692 assert_matches!(
693 event,
694 fidl_sme::ConnectTransactionEvent::OnConnectResult {
695 result: fidl_sme::ConnectResult {
696 code: fidl_ieee80211::StatusCode::Success,
697 is_credential_rejected: false,
698 is_reconnect: true,
699 }
700 }
701 );
702
703 let input_info = fidl_sme::DisconnectInfo {
705 is_sme_reconnecting: true,
706 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
707 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
708 mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
709 }),
710 };
711 sme_proxy
712 .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
713 .expect("expect sending ConnectTransactionEvent to succeed");
714 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
715 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
716 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
717 assert_eq!(input_info, output_info);
718 });
719
720 let input_ind = fidl_internal::SignalReportIndication {
722 rssi_dbm: Some(-40),
723 snr_db: Some(30),
724 tx_rate_500kbps: Some(0),
725 ..Default::default()
726 };
727 sme_proxy
728 .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind.clone() })
729 .expect("expect sending ConnectTransactionEvent to succeed");
730 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
731 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
732 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
733 assert_eq!(input_ind, ind);
734 });
735
736 let input_info = fidl_internal::ChannelSwitchInfo {
738 new_primary_channel: fidl_ieee80211::ChannelNumber {
739 band: fidl_ieee80211::WlanBand::TwoGhz,
740 number: 8,
741 },
742 bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
743 vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
744 band: fidl_ieee80211::WlanBand::TwoGhz,
745 number: 0,
746 },
747 };
748 sme_proxy
749 .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info.clone() })
750 .expect("expect sending ConnectTransactionEvent to succeed");
751 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
752 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
753 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
754 assert_eq!(input_info, info);
755 });
756
757 std::mem::drop(sme_proxy);
759 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
760 }
761
762 fn poll_stream_fut<S: Stream + std::marker::Unpin>(
763 exec: &mut fasync::TestExecutor,
764 stream_fut: &mut StreamFuture<S>,
765 ) -> Poll<Option<S::Item>> {
766 exec.run_until_stalled(stream_fut).map(|(item, stream)| {
767 *stream_fut = stream.into_future();
768 item
769 })
770 }
771
772 fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
774 use wlan_common::security::SecurityDescriptor;
775
776 wlan_common::scan::ScanResult {
778 compatibility: match rng.random_range(0..4) {
779 0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
780 1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
781 2 => wlan_common::scan::Compatible::expect_ok([
782 SecurityDescriptor::WPA2_PERSONAL,
783 SecurityDescriptor::WPA3_PERSONAL,
784 ]),
785 _ => Incompatible::unknown(),
786 },
787 timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
788 bss_description: random_bss_description!(),
789 }
790 }
791
792 #[test]
793 fn test_handle_fidl_request_apf() {
794 let mut exec = fasync::TestExecutor::new();
795 let inspector = fuchsia_inspect::Inspector::default();
796 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
797 client_sme::ClientConfig::default(),
798 test_utils::fake_device_info([0; 6].into()),
799 inspector.clone(),
800 inspector.root().create_child("sme"),
801 wlan_common::test_utils::fake_features::fake_security_support(),
802 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
803 Arc::new(()),
804 );
805 let sme = Mutex::new(sme);
806
807 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
809 let program = vec![1, 2, 3];
810 let mut install_fut = proxy.install_apf_packet_filter(&program);
811 let mut stream = pin!(stream);
812
813 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
815 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
816 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
817
818 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::InstallApfPacketFilter(req, responder))) => {
820 assert_eq!(req.program, program);
821 responder.respond(Ok(()));
822 });
823
824 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
825 });
826 assert_matches!(exec.run_until_stalled(&mut install_fut), Poll::Ready(Ok(Ok(()))));
827
828 let mut read_fut = proxy.read_apf_packet_filter_data();
830 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
831 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
832 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
833
834 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::ReadApfPacketFilterData(responder))) => {
836 responder.respond(Ok(fidl_mlme::MlmeReadApfPacketFilterDataResponse {
837 memory: vec![4, 5, 6],
838 }));
839 });
840
841 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
842 });
843 assert_matches!(exec.run_until_stalled(&mut read_fut), Poll::Ready(Ok(Ok(data))) => {
844 assert_eq!(data, vec![4, 5, 6]);
845 });
846
847 let mut set_enabled_fut = proxy.set_apf_packet_filter_enabled(true);
849 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
850 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
851 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
852
853 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetApfPacketFilterEnabled(req, responder))) => {
855 assert!(req.enabled);
856 responder.respond(Ok(()));
857 });
858
859 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
860 });
861 assert_matches!(exec.run_until_stalled(&mut set_enabled_fut), Poll::Ready(Ok(Ok(()))));
862
863 let mut get_enabled_fut = proxy.get_apf_packet_filter_enabled();
865 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
866 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
867 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
868
869 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::GetApfPacketFilterEnabled(responder))) => {
871 responder.respond(Ok(fidl_mlme::MlmeGetApfPacketFilterEnabledResponse {
872 enabled: true,
873 }));
874 });
875
876 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
877 });
878 assert_matches!(exec.run_until_stalled(&mut get_enabled_fut), Poll::Ready(Ok(Ok(true))));
879 }
880
881 #[test_case(Ok(()); "ok")]
882 #[test_case(Err(123); "err")]
883 fn test_handle_fidl_request_set_mac_address(set_mac_address_result: Result<(), i32>) {
884 let mut exec = fasync::TestExecutor::new();
885 let inspector = fuchsia_inspect::Inspector::default();
886 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
887 client_sme::ClientConfig::default(),
888 test_utils::fake_device_info([0; 6].into()),
889 inspector.clone(),
890 inspector.root().create_child("sme"),
891 wlan_common::test_utils::fake_features::fake_security_support(),
892 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
893 Arc::new(()),
894 );
895 let sme = Mutex::new(sme);
896
897 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
899 let new_mac = [1, 2, 3, 4, 5, 6];
900 let mut set_mac_fut = proxy.set_mac_address(&new_mac);
901 let mut stream = pin!(stream);
902
903 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
905 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
906 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
907
908 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(mac, responder))) => {
909 assert_eq!(mac, new_mac);
910 responder.respond(set_mac_address_result);
911 });
912
913 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
914 });
915
916 if set_mac_address_result.is_ok() {
917 assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Ok(()))));
918 assert_eq!(sme.lock().device_info().sta_addr, new_mac);
920 } else {
921 assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Err(123))));
922 assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
923 }
924 }
925
926 #[test]
927 fn test_handle_fidl_request_set_mac_address_cancelled() {
928 let mut exec = fasync::TestExecutor::new();
929 let inspector = fuchsia_inspect::Inspector::default();
930 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
931 client_sme::ClientConfig::default(),
932 test_utils::fake_device_info([0; 6].into()),
933 inspector.clone(),
934 inspector.root().create_child("sme"),
935 wlan_common::test_utils::fake_features::fake_security_support(),
936 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
937 Arc::new(()),
938 );
939 let sme = Mutex::new(sme);
940
941 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
943 let new_mac = [1, 2, 3, 4, 5, 6];
944 let mut set_mac_fut = proxy.set_mac_address(&new_mac);
945 let mut stream = pin!(stream);
946
947 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
949 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
950 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
951
952 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(_mac, _responder))) => {
953 });
955
956 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
957 });
958
959 assert_matches!(
960 exec.run_until_stalled(&mut set_mac_fut),
961 Poll::Ready(Ok(Err(zx::sys::ZX_ERR_CANCELED)))
962 );
963 assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
964 }
965
966 #[test]
967 fn test_handle_fidl_request_start_and_stop_rssi_monitor() {
968 let mut exec = fasync::TestExecutor::new();
969 let inspector = fuchsia_inspect::Inspector::default();
970 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
971 client_sme::ClientConfig::default(),
972 test_utils::fake_device_info([0; 6].into()),
973 inspector.clone(),
974 inspector.root().create_child("sme"),
975 wlan_common::test_utils::fake_features::fake_security_support(),
976 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
977 Arc::new(()),
978 );
979 let sme = Mutex::new(sme);
980
981 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
982 let mut stream = pin!(stream);
983
984 let mut start_fut = proxy.start_rssi_monitor(-80, -50);
985 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
986 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
987 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
988
989 let (req, responder) = assert_matches!(
990 exec.run_until_stalled(&mut mlme_stream.next()),
991 Poll::Ready(Some(crate::MlmeRequest::StartRssiMonitor(req, responder))) => (req, responder)
992 );
993 assert_eq!(req.min_rssi_dbm, -80);
994 assert_eq!(req.max_rssi_dbm, -50);
995 responder.respond(Ok(()));
996
997 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
998 });
999 assert_matches!(exec.run_until_stalled(&mut start_fut), Poll::Ready(Ok(Ok(()))));
1000
1001 let mut stop_fut = proxy.stop_rssi_monitor();
1002 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
1003 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
1004 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
1005
1006 let responder = assert_matches!(
1007 exec.run_until_stalled(&mut mlme_stream.next()),
1008 Poll::Ready(Some(crate::MlmeRequest::StopRssiMonitor(responder))) => responder
1009 );
1010 responder.respond(Ok(()));
1011
1012 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
1013 });
1014 assert_matches!(exec.run_until_stalled(&mut stop_fut), Poll::Ready(Ok(Ok(()))));
1015 }
1016
1017 #[test]
1018 fn test_serve_connect_txn_stream_on_rssi_threshold_breached() {
1019 let mut exec = fasync::TestExecutor::new();
1020 let (txn_proxy, txn_stream) =
1021 create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
1022 let handle = txn_stream.control_handle();
1023 let (mut sink, stream) = client_sme::ConnectTransactionSink::new_unbounded();
1024 let mut serve_fut = pin!(serve_connect_txn_stream(Some(handle), stream));
1025 let mut event_stream = txn_proxy.take_event_stream();
1026
1027 sink.send(ConnectTransactionEvent::OnRssiThresholdBreached { cur_rssi_dbm: -75 });
1028 assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1029 assert_matches!(
1030 exec.run_until_stalled(&mut event_stream.next()),
1031 Poll::Ready(Some(Ok(fidl_sme::ConnectTransactionEvent::OnRssiThresholdBreached {
1032 cur_rssi_dbm: -75
1033 })))
1034 );
1035 }
1036
1037 #[test]
1038 fn test_handle_telemetry_fidl_request_get_signal_report_updates_status() {
1039 use crate::Station;
1040 use wlan_common::fake_fidl_bss_description;
1041
1042 let mut exec = fasync::TestExecutor::new();
1043 let inspector = fuchsia_inspect::Inspector::default();
1044 let (mut sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
1045 client_sme::ClientConfig::default(),
1046 test_utils::fake_device_info([0; 6].into()),
1047 inspector.clone(),
1048 inspector.root().create_child("sme"),
1049 wlan_common::test_utils::fake_features::fake_security_support(),
1050 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
1051 Arc::new(()),
1052 );
1053
1054 let ssid = ieee80211::Ssid::try_from("foo").unwrap();
1055 let bssid = [1, 2, 3, 4, 5, 6];
1056 let _txn = sme.on_connect_command(fidl_sme::ConnectRequest {
1057 ssid: ssid.to_vec(),
1058 bss_description: fake_fidl_bss_description!(
1059 Open,
1060 ssid: ssid,
1061 bssid: bssid,
1062 rssi_dbm: -60,
1063 snr_db: 20,
1064 ),
1065 multiple_bss_candidates: false,
1066 authentication: fidl_internal::Authentication {
1067 protocol: fidl_internal::Protocol::Open,
1068 credentials: None,
1069 },
1070 deprecated_scan_type: fidl_common::ScanType::Passive,
1071 });
1072 assert_matches!(
1073 exec.run_until_stalled(&mut mlme_stream.next()),
1074 Poll::Ready(Some(crate::MlmeRequest::Connect(_)))
1075 );
1076 sme.on_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
1077 resp: fidl_mlme::ConnectConfirm {
1078 peer_sta_address: bssid,
1079 result_code: fidl_ieee80211::StatusCode::Success,
1080 association_id: 1,
1081 association_ies: vec![],
1082 },
1083 });
1084
1085 let sme = Mutex::new(sme);
1086 let (telemetry_proxy, telemetry_server) =
1087 fidl::endpoints::create_endpoints::<fidl_sme::TelemetryMarker>();
1088 let telemetry_proxy = telemetry_proxy.into_proxy();
1089 let mut serve_fut = pin!(super::super::serve_fidl_endpoint(
1090 &sme,
1091 telemetry_server,
1092 handle_telemetry_fidl_request,
1093 ));
1094
1095 let mut get_signal_fut = telemetry_proxy.get_signal_report();
1096 assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1097 let responder = assert_matches!(
1098 exec.run_until_stalled(&mut mlme_stream.next()),
1099 Poll::Ready(Some(crate::MlmeRequest::GetSignalReport(responder))) => responder
1100 );
1101 let expected_report = fidl_stats::SignalReport {
1102 connection_signal_report: Some(fidl_stats::ConnectionSignalReport {
1103 rssi_dbm: Some(-45),
1104 snr_db: Some(35),
1105 ..Default::default()
1106 }),
1107 ..Default::default()
1108 };
1109 responder.respond(Ok(expected_report.clone()));
1110 assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1111 assert_matches!(
1112 exec.run_until_stalled(&mut get_signal_fut),
1113 Poll::Ready(Ok(Ok(report))) if report == expected_report
1114 );
1115 assert_matches!(sme.lock().status(), client_sme::ClientSmeStatus::Connected(ap_info) => {
1116 assert_eq!((ap_info.rssi_dbm, ap_info.snr_db), (-45, 35));
1117 });
1118 }
1119}