1use crate::client::{
6 self as client_sme, ConnectResult, ConnectTransactionEvent, ConnectTransactionStream,
7 RoamResult,
8};
9use crate::{MlmeEventStream, MlmeSink, MlmeStream};
10use fidl::endpoints::{RequestStream, ServerEnd};
11use fidl_fuchsia_wlan_common::BssDescription as BssDescriptionFidl;
12use fidl_fuchsia_wlan_sme::{self as fidl_sme, ClientSmeRequest, TelemetryRequest};
13use fuchsia_sync::Mutex;
14use futures::channel::mpsc;
15use futures::prelude::*;
16use futures::select;
17use ieee80211::MacAddrBytes;
18use log::error;
19use std::pin::pin;
20use std::sync::Arc;
21use wlan_common::scan::write_vmo;
22use {
23 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
24 fidl_fuchsia_wlan_mlme as fidl_mlme, fuchsia_inspect_auto_persist as auto_persist,
25};
26
27pub type Endpoint = ServerEnd<fidl_sme::ClientSmeMarker>;
28type Sme = client_sme::ClientSme;
29
30#[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
31pub fn serve(
32 cfg: crate::Config,
33 device_info: fidl_mlme::DeviceInfo,
34 security_support: fidl_common::SecuritySupport,
35 spectrum_management_support: fidl_common::SpectrumManagementSupport,
36 event_stream: MlmeEventStream,
37 new_fidl_clients: mpsc::UnboundedReceiver<Endpoint>,
38 new_telemetry_fidl_clients: mpsc::UnboundedReceiver<
39 fidl::endpoints::ServerEnd<fidl_sme::TelemetryMarker>,
40 >,
41 inspector: fuchsia_inspect::Inspector,
42 inspect_node: fuchsia_inspect::Node,
43 persistence_req_sender: auto_persist::PersistenceReqSender,
44) -> (MlmeSink, MlmeStream, impl Future<Output = Result<(), anyhow::Error>>) {
45 let wpa3_supported = security_support.mfp.supported
46 && (security_support.sae.driver_handler_supported
47 || security_support.sae.sme_handler_supported);
48 let cfg = client_sme::ClientConfig::from_config(cfg, wpa3_supported);
49 let (sme, mlme_sink, mlme_stream, time_stream) = Sme::new(
50 cfg,
51 device_info,
52 inspector,
53 inspect_node,
54 persistence_req_sender,
55 security_support,
56 spectrum_management_support,
57 );
58 let fut = async move {
59 let sme = Arc::new(Mutex::new(sme));
60 let mlme_sme = super::serve_mlme_sme(event_stream, Arc::clone(&sme), time_stream);
61 let sme_fidl = super::serve_fidl(&*sme, new_fidl_clients, handle_fidl_request);
62 let telemetry_fidl =
63 super::serve_fidl(&*sme, new_telemetry_fidl_clients, handle_telemetry_fidl_request);
64 let mlme_sme = pin!(mlme_sme);
65 let sme_fidl = pin!(sme_fidl);
66 select! {
67 mlme_sme = mlme_sme.fuse() => mlme_sme?,
68 sme_fidl = sme_fidl.fuse() => match sme_fidl? {},
69 telemetry_fidl = telemetry_fidl.fuse() => match telemetry_fidl? {},
70 }
71 Ok(())
72 };
73 (mlme_sink, mlme_stream, fut)
74}
75
76async fn handle_fidl_request(
77 sme: &Mutex<Sme>,
78 request: fidl_sme::ClientSmeRequest,
79) -> Result<(), fidl::Error> {
80 #[allow(clippy::unit_arg, reason = "mass allow for https://fxbug.dev/381896734")]
81 match request {
82 ClientSmeRequest::Scan { req, responder } => Ok(scan(sme, req, |result| match result {
83 Ok(scan_results) => responder.send(Ok(write_vmo(scan_results)?)).map_err(|e| e.into()),
84 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
85 })
86 .await
87 .unwrap_or_else(|e| error!("Error handling a scan transaction: {:?}", e))),
88 ClientSmeRequest::Connect { req, txn, .. } => Ok(connect(sme, txn, req)
89 .await
90 .unwrap_or_else(|e| error!("Error handling a connect transaction: {:?}", e))),
91 ClientSmeRequest::Roam { req, .. } => Ok(roam(sme, req)),
92 ClientSmeRequest::Disconnect { responder, reason } => {
93 disconnect(sme, reason, responder);
94 Ok(())
95 }
96 ClientSmeRequest::Status { responder } => responder.send(&status(sme)),
97 ClientSmeRequest::WmmStatus { responder } => wmm_status(sme, responder).await,
98 ClientSmeRequest::ScanForController { req, responder } => {
99 Ok(scan(sme, req, |result| match result {
100 Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
101 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
102 })
103 .await
104 .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
105 }
106 ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
107 Ok(set_mac_address(sme, mac_addr, responder).await?)
108 }
109 }
110}
111
112async fn handle_telemetry_fidl_request(
113 sme: &Mutex<Sme>,
114 request: TelemetryRequest,
115) -> Result<(), fidl::Error> {
116 match request {
117 TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
118 let support_fut = sme.lock().query_telemetry_support();
119 let support = support_fut
120 .await
121 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
122 .and_then(|result| result);
123 responder.send(support.as_ref().map_err(|e| *e))
124 }
125 TelemetryRequest::GetIfaceStats { responder, .. } => {
126 let iface_stats_fut = sme.lock().iface_stats();
127 let iface_stats = iface_stats_fut
128 .await
129 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
130 .and_then(|stats| match stats {
131 fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
132 fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
133 });
134 responder.send(iface_stats.as_ref().map_err(|e| *e))
135 }
136 TelemetryRequest::GetHistogramStats { responder, .. } => {
137 let histogram_stats_fut = sme.lock().histogram_stats();
138 let histogram_stats = histogram_stats_fut
139 .await
140 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
141 .and_then(|stats| match stats {
142 fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
143 fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
144 });
145 responder.send(histogram_stats.as_ref().map_err(|e| *e))
146 }
147 TelemetryRequest::GetSignalReport { responder, .. } => {
148 let signal_report_fut = sme.lock().signal_report();
149 let signal_report = signal_report_fut
150 .await
151 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
152 .and_then(|result| result);
153 responder.send(signal_report.as_ref().map_err(|e| *e))
154 }
155 TelemetryRequest::CloneInspectVmo { responder } => {
156 let inspect_vmo =
157 sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
158 responder.send(inspect_vmo)
159 }
160 }
161}
162
163async fn scan(
164 sme: &Mutex<Sme>,
165 request: fidl_sme::ScanRequest,
166 responder: impl FnOnce(
167 Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
168 ) -> Result<(), anyhow::Error>,
169) -> Result<(), anyhow::Error> {
170 let receiver = sme.lock().on_scan_command(request);
171 let receive_result = match receiver.await {
172 Ok(receive_result) => receive_result,
173 Err(e) => {
174 error!("Scan receiver error: {:?}", e);
175 responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
176 return Ok(());
177 }
178 };
179
180 match receive_result {
181 Ok(scan_results) => {
182 let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
183 responder(Ok(results))
184 }
185 Err(mlme_scan_result_code) => {
186 let scan_error_code = match mlme_scan_result_code {
187 fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
188 error!("Internal scan error: {:?}", mlme_scan_result_code);
189 fidl_sme::ScanErrorCode::InternalError
190 }
191 fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
192 fidl_mlme::ScanResultCode::InternalError => {
193 fidl_sme::ScanErrorCode::InternalMlmeError
194 }
195 fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
196 fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
197 fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
198 }
199 };
200 responder(Err(scan_error_code))
201 }
202 }?;
203 Ok(())
204}
205
206async fn connect(
207 sme: &Mutex<Sme>,
208 txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
209 req: fidl_sme::ConnectRequest,
210) -> Result<(), anyhow::Error> {
211 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
212 let handle = match txn {
213 None => None,
214 Some(txn) => Some(txn.into_stream().control_handle()),
215 };
216 let connect_txn_stream = sme.lock().on_connect_command(req);
217 serve_connect_txn_stream(handle, connect_txn_stream).await?;
218 Ok(())
219}
220
221async fn serve_connect_txn_stream(
222 handle: Option<fidl_sme::ConnectTransactionControlHandle>,
223 mut connect_txn_stream: ConnectTransactionStream,
224) -> Result<(), anyhow::Error> {
225 if let Some(handle) = handle {
226 loop {
227 match connect_txn_stream.next().await {
228 Some(event) => match event {
229 ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
230 let connect_result = convert_connect_result(&result, is_reconnect);
231 handle.send_on_connect_result(&connect_result)
232 }
233 ConnectTransactionEvent::OnRoamResult { result } => {
234 let roam_result = convert_roam_result(&result);
235 handle.send_on_roam_result(&roam_result)
236 }
237 ConnectTransactionEvent::OnDisconnect { info } => {
238 handle.send_on_disconnect(&info)
239 }
240 ConnectTransactionEvent::OnSignalReport { ind } => {
241 handle.send_on_signal_report(&ind)
242 }
243 ConnectTransactionEvent::OnChannelSwitched { info } => {
244 handle.send_on_channel_switched(&info)
245 }
246 }?,
247 None => return Ok(()),
249 }
250 }
251 }
252 Ok(())
253}
254
255fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
256 sme.lock().on_roam_command(req);
257}
258
259fn disconnect(
260 sme: &Mutex<Sme>,
261 policy_disconnect_reason: fidl_sme::UserDisconnectReason,
262 responder: fidl_sme::ClientSmeDisconnectResponder,
263) {
264 sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
265}
266
267fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
268 sme.lock().status().into()
269}
270
271async fn wmm_status(
272 sme: &Mutex<Sme>,
273 responder: fidl_sme::ClientSmeWmmStatusResponder,
274) -> Result<(), fidl::Error> {
275 let receiver = sme.lock().wmm_status();
276 let wmm_status = match receiver.await {
277 Ok(result) => result,
278 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
279 };
280 responder.send(wmm_status.as_ref().map_err(|e| *e))
281}
282
283async fn set_mac_address(
284 sme: &Mutex<Sme>,
285 mac_addr: [u8; 6],
286 responder: fidl_sme::ClientSmeSetMacAddressResponder,
287) -> Result<(), fidl::Error> {
288 let receiver = sme.lock().set_mac_address(mac_addr);
289 let resp = match receiver.await {
290 Ok(result) => result,
291 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
292 };
293 responder.send(resp)
294}
295
296fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
297 let (code, is_credential_rejected) = match result {
298 ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
299 ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
300 ConnectResult::Failed(failure) => {
301 (failure.status_code(), failure.likely_due_to_credential_rejected())
302 }
303 };
304 fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
305}
306
307fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
308 match result {
309 RoamResult::Success(bss) => {
310 let bss_description = Some(Box::new(BssDescriptionFidl::from(*bss.clone())));
311 fidl_sme::RoamResult {
312 bssid: bss.bssid.to_array(),
313 status_code: fidl_ieee80211::StatusCode::Success,
314 original_association_maintained: false,
316 bss_description,
317 disconnect_info: None,
318 is_credential_rejected: false,
319 }
320 }
321 RoamResult::Failed(failure) => {
322 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
323 fidl_sme::RoamResult {
324 bssid: failure.selected_bssid.to_array(),
325 status_code: failure.status_code,
326 original_association_maintained: false,
330 bss_description: match &failure.selected_bss {
331 Some(bss) => Some(Box::new(bss.clone().into())),
332 None => None,
333 },
334 disconnect_info: Some(Box::new(failure.disconnect_info)),
335 is_credential_rejected: failure.likely_due_to_credential_rejected(),
336 }
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
345 use assert_matches::assert_matches;
346 use fidl::endpoints::create_proxy_and_stream;
347 use fidl_fuchsia_wlan_mlme::ScanResultCode;
348 use fidl_fuchsia_wlan_sme::{self as fidl_sme};
349 use futures::stream::StreamFuture;
350 use futures::task::Poll;
351 use rand::Rng;
352 use rand::prelude::ThreadRng;
353 use std::pin::pin;
354 use test_case::test_case;
355 use wlan_common::random_bss_description;
356 use wlan_common::scan::{self, Incompatible};
357 use wlan_rsn::auth;
358 use {fidl_fuchsia_wlan_internal as fidl_internal, fuchsia_async as fasync};
359
360 #[test]
361 fn test_convert_connect_result() {
362 assert_eq!(
363 convert_connect_result(&ConnectResult::Success, false),
364 fidl_sme::ConnectResult {
365 code: fidl_ieee80211::StatusCode::Success,
366 is_credential_rejected: false,
367 is_reconnect: false,
368 }
369 );
370 assert_eq!(
371 convert_connect_result(&ConnectResult::Canceled, true),
372 fidl_sme::ConnectResult {
373 code: fidl_ieee80211::StatusCode::Canceled,
374 is_credential_rejected: false,
375 is_reconnect: true,
376 }
377 );
378 let connect_result =
379 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::ShouldWait));
380 assert_eq!(
381 convert_connect_result(&connect_result, false),
382 fidl_sme::ConnectResult {
383 code: fidl_ieee80211::StatusCode::Canceled,
384 is_credential_rejected: false,
385 is_reconnect: false,
386 }
387 );
388
389 let connect_result =
390 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
391 auth_method: Some(auth::MethodName::Psk),
392 reason: EstablishRsnaFailureReason::InternalError,
393 }));
394 assert_eq!(
395 convert_connect_result(&connect_result, false),
396 fidl_sme::ConnectResult {
397 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
398 is_credential_rejected: false,
399 is_reconnect: false,
400 }
401 );
402
403 let connect_result =
404 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
405 auth_method: Some(auth::MethodName::Psk),
406 reason: EstablishRsnaFailureReason::RsnaResponseTimeout(
407 wlan_rsn::Error::LikelyWrongCredential,
408 ),
409 }));
410 assert_eq!(
411 convert_connect_result(&connect_result, false),
412 fidl_sme::ConnectResult {
413 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
414 is_credential_rejected: true,
415 is_reconnect: false,
416 }
417 );
418
419 let connect_result =
420 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
421 auth_method: Some(auth::MethodName::Psk),
422 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
423 wlan_rsn::Error::LikelyWrongCredential,
424 ),
425 }));
426 assert_eq!(
427 convert_connect_result(&connect_result, false),
428 fidl_sme::ConnectResult {
429 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
430 is_credential_rejected: true,
431 is_reconnect: false,
432 }
433 );
434
435 let connect_result =
436 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
437 auth_method: Some(auth::MethodName::Psk),
438 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
439 wlan_rsn::Error::MissingGtkProvider,
440 ),
441 }));
442 assert_eq!(
443 convert_connect_result(&connect_result, false),
444 fidl_sme::ConnectResult {
445 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
446 is_credential_rejected: false,
447 is_reconnect: false,
448 }
449 );
450
451 let connect_result =
452 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::InternalError));
453 assert_eq!(
454 convert_connect_result(&connect_result, false),
455 fidl_sme::ConnectResult {
456 code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
457 is_credential_rejected: false,
458 is_reconnect: false,
459 }
460 );
461 }
462
463 #[test_case(1, true; "with 1 result")]
468 #[test_case(2, true; "with 2 results")]
469 #[test_case(30, true; "with 30 results")]
470 #[test_case(4000, true; "with 4000 results")]
471 #[test_case(50000, false; "with 50000 results")]
472 #[test_case(100000, false; "with 100000 results")]
473 fn scan_results_are_effectively_unbounded(number_of_scan_results: usize, randomize: bool) {
474 let mut exec = fasync::TestExecutor::new();
475 let (client_sme_proxy, mut client_sme_stream) =
476 create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
477
478 async fn request_and_collect_result(
480 client_sme_proxy: &fidl_sme::ClientSmeProxy,
481 ) -> fidl_sme::ClientSmeScanResult {
482 client_sme_proxy
483 .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {}))
484 .await
485 .expect("FIDL request failed")
486 }
487
488 let result_fut = request_and_collect_result(&client_sme_proxy);
489 let mut result_fut = pin!(result_fut);
490
491 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
492
493 let mut rng = rand::rng();
495 let scan_result_list = if randomize {
496 (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
497 } else {
498 vec![random_scan_result(&mut rng).into(); number_of_scan_results]
499 };
500 assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
501 Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
502 req: _, responder,
503 }))) => {
504 let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
505 responder.send(Ok(vmo)).expect("failed to send scan results");
506 }
507 );
508
509 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
511 assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
512 })
513 }
514
515 #[test]
516 fn test_serve_connect_txn_stream() {
517 let mut exec = fasync::TestExecutor::new();
518
519 let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
520 let (fidl_client_proxy, fidl_connect_txn_stream) =
521 create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
522 let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
523 let mut fidl_client_fut = pin!(fidl_client_fut);
524 let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
525
526 let test_fut =
527 serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
528 let mut test_fut = pin!(test_fut);
529
530 sme_proxy
532 .unbounded_send(ConnectTransactionEvent::OnConnectResult {
533 result: ConnectResult::Success,
534 is_reconnect: true,
535 })
536 .expect("expect sending ConnectTransactionEvent to succeed");
537 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
538 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
539 assert_matches!(
540 event,
541 fidl_sme::ConnectTransactionEvent::OnConnectResult {
542 result: fidl_sme::ConnectResult {
543 code: fidl_ieee80211::StatusCode::Success,
544 is_credential_rejected: false,
545 is_reconnect: true,
546 }
547 }
548 );
549
550 let input_info = fidl_sme::DisconnectInfo {
552 is_sme_reconnecting: true,
553 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
554 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
555 mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
556 }),
557 };
558 sme_proxy
559 .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
560 .expect("expect sending ConnectTransactionEvent to succeed");
561 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
562 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
563 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
564 assert_eq!(input_info, output_info);
565 });
566
567 let input_ind = fidl_internal::SignalReportIndication { rssi_dbm: -40, snr_db: 30 };
569 sme_proxy
570 .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind })
571 .expect("expect sending ConnectTransactionEvent to succeed");
572 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
573 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
574 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
575 assert_eq!(input_ind, ind);
576 });
577
578 let input_info = fidl_internal::ChannelSwitchInfo { new_channel: 8 };
580 sme_proxy
581 .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info })
582 .expect("expect sending ConnectTransactionEvent to succeed");
583 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
584 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
585 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
586 assert_eq!(input_info, info);
587 });
588
589 std::mem::drop(sme_proxy);
591 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
592 }
593
594 fn poll_stream_fut<S: Stream + std::marker::Unpin>(
595 exec: &mut fasync::TestExecutor,
596 stream_fut: &mut StreamFuture<S>,
597 ) -> Poll<Option<S::Item>> {
598 exec.run_until_stalled(stream_fut).map(|(item, stream)| {
599 *stream_fut = stream.into_future();
600 item
601 })
602 }
603
604 fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
606 use wlan_common::security::SecurityDescriptor;
607
608 wlan_common::scan::ScanResult {
610 compatibility: match rng.random_range(0..4) {
611 0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
612 1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
613 2 => wlan_common::scan::Compatible::expect_ok([
614 SecurityDescriptor::WPA2_PERSONAL,
615 SecurityDescriptor::WPA3_PERSONAL,
616 ]),
617 _ => Incompatible::unknown(),
618 },
619 timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
620 bss_description: random_bss_description!(),
621 }
622 }
623}