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) -> (MlmeSink, MlmeStream, impl Future<Output = Result<(), anyhow::Error>>) {
42 let wpa3_supported =
43 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
44 && security_support.sae.as_ref().is_some_and(|sae| {
45 sae.driver_handler_supported.unwrap_or(false)
46 || sae.sme_handler_supported.unwrap_or(false)
47 });
48 let owe_supported =
49 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
50 && security_support.owe.as_ref().is_some_and(|owe| owe.supported.unwrap_or(false));
51 let cfg = client_sme::ClientConfig::from_config(cfg, wpa3_supported, owe_supported);
52 let (sme, mlme_sink, mlme_stream, time_stream) = Sme::new(
53 cfg,
54 device_info,
55 inspector,
56 inspect_node,
57 security_support,
58 spectrum_management_support,
59 );
60 let fut = async move {
61 let sme = Arc::new(Mutex::new(sme));
62 let mlme_sme = super::serve_mlme_sme(event_stream, Arc::clone(&sme), time_stream);
63 let sme_fidl = super::serve_fidl(&*sme, new_fidl_clients, handle_fidl_request);
64 let telemetry_fidl =
65 super::serve_fidl(&*sme, new_telemetry_fidl_clients, handle_telemetry_fidl_request);
66 let mlme_sme = pin!(mlme_sme);
67 let sme_fidl = pin!(sme_fidl);
68 select! {
69 mlme_sme = mlme_sme.fuse() => mlme_sme?,
70 sme_fidl = sme_fidl.fuse() => match sme_fidl? {},
71 telemetry_fidl = telemetry_fidl.fuse() => match telemetry_fidl? {},
72 }
73 Ok(())
74 };
75 (mlme_sink, mlme_stream, fut)
76}
77
78async fn handle_fidl_request(
79 sme: &Mutex<Sme>,
80 request: fidl_sme::ClientSmeRequest,
81) -> Result<(), fidl::Error> {
82 #[allow(clippy::unit_arg, reason = "mass allow for https://fxbug.dev/381896734")]
83 match request {
84 ClientSmeRequest::Scan { req, responder } => Ok(scan(sme, req, |result| match result {
85 Ok(scan_results) => responder.send(Ok(write_vmo(scan_results)?)).map_err(|e| e.into()),
86 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
87 })
88 .await
89 .unwrap_or_else(|e| error!("Error handling a scan transaction: {:?}", e))),
90 ClientSmeRequest::Connect { req, txn, .. } => Ok(connect(sme, txn, req)
91 .await
92 .unwrap_or_else(|e| error!("Error handling a connect transaction: {:?}", e))),
93 ClientSmeRequest::Roam { req, .. } => Ok(roam(sme, req)),
94 ClientSmeRequest::Disconnect { responder, reason } => {
95 disconnect(sme, reason, responder);
96 Ok(())
97 }
98 ClientSmeRequest::Status { responder } => responder.send(&status(sme)),
99 ClientSmeRequest::WmmStatus { responder } => wmm_status(sme, responder).await,
100 ClientSmeRequest::StartScheduledScan { req, txn, responder } => {
101 start_scheduled_scan(sme, req, txn, responder).await
102 }
103
104 ClientSmeRequest::GetScheduledScanEnabled { responder } => {
105 let receiver = sme.lock().on_get_scheduled_scan_enabled_command();
106 let resp = match receiver.await {
107 Ok(result) => result,
108 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
109 };
110 responder
111 .send(resp.as_ref().map(|r| !r.active_txn_ids.is_empty()).map_err(|e| *e))
112 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
113 Ok(())
114 }
115 ClientSmeRequest::ScanForController { req, responder } => {
116 Ok(scan(sme, req, |result| match result {
117 Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
118 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
119 })
120 .await
121 .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
122 }
123 ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
124 Ok(set_mac_address(sme, mac_addr, responder).await?)
125 }
126 ClientSmeRequest::InstallApfPacketFilter { program, responder } => {
127 let receiver = sme.lock().install_apf_packet_filter(program);
128 let resp = match receiver.await {
129 Ok(result) => result,
130 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
131 };
132 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
133 Ok(())
134 }
135 ClientSmeRequest::ReadApfPacketFilterData { responder } => {
136 let receiver = sme.lock().read_apf_packet_filter_data();
137 let resp = match receiver.await {
138 Ok(result) => result.map(|r| r.memory),
139 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
140 };
141 responder
142 .send(resp.as_ref().map(|m| &m[..]).map_err(|e| *e))
143 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
144 Ok(())
145 }
146 ClientSmeRequest::SetApfPacketFilterEnabled { enabled, responder } => {
147 let receiver = sme.lock().set_apf_packet_filter_enabled(enabled);
148 let resp = match receiver.await {
149 Ok(result) => result,
150 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
151 };
152 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
153 Ok(())
154 }
155 ClientSmeRequest::GetApfPacketFilterEnabled { responder } => {
156 let receiver = sme.lock().get_apf_packet_filter_enabled();
157 let resp = match receiver.await {
158 Ok(result) => result.map(|r| r.enabled),
159 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
160 };
161 responder
162 .send(resp.as_ref().map(|e| *e).map_err(|e| *e))
163 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
164 Ok(())
165 }
166 }
167}
168
169async fn handle_telemetry_fidl_request(
170 sme: &Mutex<Sme>,
171 request: TelemetryRequest,
172) -> Result<(), fidl::Error> {
173 match request {
174 TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
175 let support_fut = sme.lock().query_telemetry_support();
176 let support = support_fut
177 .await
178 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
179 .and_then(|result| result);
180 responder.send(support.as_ref().map_err(|e| *e))
181 }
182 TelemetryRequest::GetIfaceStats { responder, .. } => {
183 let iface_stats_fut = sme.lock().iface_stats();
184 let iface_stats = iface_stats_fut
185 .await
186 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
187 .and_then(|stats| match stats {
188 fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
189 fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
190 });
191 responder.send(iface_stats.as_ref().map_err(|e| *e))
192 }
193 TelemetryRequest::GetHistogramStats { responder, .. } => {
194 let histogram_stats_fut = sme.lock().histogram_stats();
195 let histogram_stats = histogram_stats_fut
196 .await
197 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
198 .and_then(|stats| match stats {
199 fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
200 fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
201 });
202 responder.send(histogram_stats.as_ref().map_err(|e| *e))
203 }
204 TelemetryRequest::GetSignalReport { responder, .. } => {
205 let signal_report_fut = sme.lock().signal_report();
206 let signal_report = signal_report_fut
207 .await
208 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
209 .and_then(|result| result);
210 responder.send(signal_report.as_ref().map_err(|e| *e))
211 }
212 TelemetryRequest::CloneInspectVmo { responder } => {
213 let inspect_vmo =
214 sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
215 responder.send(inspect_vmo)
216 }
217 }
218}
219
220async fn scan(
221 sme: &Mutex<Sme>,
222 request: fidl_sme::ScanRequest,
223 responder: impl FnOnce(
224 Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
225 ) -> Result<(), anyhow::Error>,
226) -> Result<(), anyhow::Error> {
227 let receiver = sme.lock().on_scan_command(request);
228 let receive_result = match receiver.await {
229 Ok(receive_result) => receive_result,
230 Err(e) => {
231 error!("Scan receiver error: {:?}", e);
232 responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
233 return Ok(());
234 }
235 };
236
237 match receive_result {
238 Ok(scan_results) => {
239 let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
240 responder(Ok(results))
241 }
242 Err(mlme_scan_result_code) => {
243 let scan_error_code = match mlme_scan_result_code {
244 fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
245 error!("Internal scan error: {:?}", mlme_scan_result_code);
246 fidl_sme::ScanErrorCode::InternalError
247 }
248 fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
249 fidl_mlme::ScanResultCode::InternalError => {
250 fidl_sme::ScanErrorCode::InternalMlmeError
251 }
252 fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
253 fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
254 fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
255 }
256 };
257 responder(Err(scan_error_code))
258 }
259 }?;
260 Ok(())
261}
262
263async fn connect(
264 sme: &Mutex<Sme>,
265 txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
266 req: fidl_sme::ConnectRequest,
267) -> Result<(), anyhow::Error> {
268 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
269 let handle = match txn {
270 None => None,
271 Some(txn) => Some(txn.into_stream().control_handle()),
272 };
273 let connect_txn_stream = sme.lock().on_connect_command(req);
274 serve_connect_txn_stream(handle, connect_txn_stream).await?;
275 Ok(())
276}
277
278async fn serve_sched_scan_session(
281 txn_handle: fidl_sme::ScheduledScanTransactionControlHandle,
282 txn_stream: fidl_sme::ScheduledScanTransactionRequestStream,
283 session: ScheduledScanReceiver,
284) -> Result<(), anyhow::Error> {
285 let mut cancellation_stream = txn_stream.fuse();
287 let mut session_stream = session.fuse();
288 loop {
289 futures::select! {
290 scan_results = session_stream.next() => {
291 if let Some(vmo) = scan_results {
292 txn_handle.send_on_scheduled_scan_matches_available(vmo)?;
293 } else {
294 txn_handle.shutdown_with_epitaph(Ok(()));
296
297 break;
298 }
299 },
300 _ = cancellation_stream.next() => {
301 break;
304 }
305 }
306 }
307 Ok(())
308}
309
310async fn start_scheduled_scan(
312 sme: &Mutex<Sme>,
313 req: fidl_common::ScheduledScanRequest,
314 txn_server: ServerEnd<fidl_sme::ScheduledScanTransactionMarker>,
315 responder: fidl_sme::ClientSmeStartScheduledScanResponder,
316) -> Result<(), fidl::Error> {
317 let txn_receiver_stream = txn_server.into_stream();
318 let txn_sender_handle = txn_receiver_stream.control_handle();
319 let (receiver, session) = sme.lock().on_start_scheduled_scan_command(req);
320 match receiver.await.unwrap_or_else(|_| Err(zx::Status::CANCELED.into_raw())) {
321 Ok(()) => {
322 responder.send(Ok(()))?;
323 serve_sched_scan_session(txn_sender_handle, txn_receiver_stream, session)
324 .await
325 .unwrap_or_else(|e| {
326 error!("Error serving sched scan txn stream: {:?}", e);
327 });
328 }
329 Err(status) => {
330 responder.send(Err(status))?;
331 txn_sender_handle.shutdown_with_epitaph(zx::Status::from_raw(status));
332 }
333 }
334 Ok(())
335}
336
337async fn serve_connect_txn_stream(
338 handle: Option<fidl_sme::ConnectTransactionControlHandle>,
339 mut connect_txn_stream: ConnectTransactionStream,
340) -> Result<(), anyhow::Error> {
341 if let Some(handle) = handle {
342 loop {
343 match connect_txn_stream.next().await {
344 Some(event) => match event {
345 ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
346 let connect_result = convert_connect_result(&result, is_reconnect);
347 handle.send_on_connect_result(&connect_result)
348 }
349 ConnectTransactionEvent::OnRoamResult { result } => {
350 let roam_result = convert_roam_result(&result);
351 handle.send_on_roam_result(&roam_result)
352 }
353 ConnectTransactionEvent::OnDisconnect { info } => {
354 handle.send_on_disconnect(&info)
355 }
356 ConnectTransactionEvent::OnSignalReport { ind } => {
357 handle.send_on_signal_report(&ind)
358 }
359 ConnectTransactionEvent::OnChannelSwitched { info } => {
360 handle.send_on_channel_switched(&info)
361 }
362 }?,
363 None => return Ok(()),
365 }
366 }
367 }
368 Ok(())
369}
370
371fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
372 sme.lock().on_roam_command(req);
373}
374
375fn disconnect(
376 sme: &Mutex<Sme>,
377 policy_disconnect_reason: fidl_sme::UserDisconnectReason,
378 responder: fidl_sme::ClientSmeDisconnectResponder,
379) {
380 sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
381}
382
383fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
384 sme.lock().status().into()
385}
386
387async fn wmm_status(
388 sme: &Mutex<Sme>,
389 responder: fidl_sme::ClientSmeWmmStatusResponder,
390) -> Result<(), fidl::Error> {
391 let receiver = sme.lock().wmm_status();
392 let wmm_status = match receiver.await {
393 Ok(result) => result,
394 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
395 };
396 responder.send(wmm_status.as_ref().map_err(|e| *e))
397}
398
399async fn set_mac_address(
400 sme: &Mutex<Sme>,
401 mac_addr: [u8; 6],
402 responder: fidl_sme::ClientSmeSetMacAddressResponder,
403) -> Result<(), fidl::Error> {
404 let receiver = sme.lock().set_mac_address(mac_addr);
405 let resp = match receiver.await {
406 Ok(Ok(())) => {
407 sme.lock().update_mac_address(mac_addr);
408 Ok(())
409 }
410 Ok(result @ Err(_)) => result,
411 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
412 };
413 responder.send(resp)
414}
415
416fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
417 let (code, is_credential_rejected) = match result {
418 ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
419 ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
420 ConnectResult::Failed(failure) => {
421 (failure.status_code(), failure.likely_due_to_credential_rejected())
422 }
423 };
424 fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
425}
426
427fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
428 match result {
429 RoamResult::Success(bss) => {
430 let bss_description =
431 Some(Box::new(fidl_ieee80211::BssDescription::from(*bss.clone())));
432 fidl_sme::RoamResult {
433 bssid: bss.bssid.to_array(),
434 status_code: fidl_ieee80211::StatusCode::Success,
435 original_association_maintained: false,
437 bss_description,
438 disconnect_info: None,
439 is_credential_rejected: false,
440 }
441 }
442 RoamResult::Failed(failure) => {
443 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
444 fidl_sme::RoamResult {
445 bssid: failure.selected_bssid.to_array(),
446 status_code: failure.status_code,
447 original_association_maintained: false,
451 bss_description: match &failure.selected_bss {
452 Some(bss) => Some(Box::new(bss.clone().into())),
453 None => None,
454 },
455 disconnect_info: Some(Box::new(failure.disconnect_info)),
456 is_credential_rejected: failure.likely_due_to_credential_rejected(),
457 }
458 }
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465 use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
466 use crate::test_utils;
467 use assert_matches::assert_matches;
468 use fidl::endpoints::create_proxy_and_stream;
469 use fidl_fuchsia_wlan_internal as fidl_internal;
470 use fidl_fuchsia_wlan_mlme::ScanResultCode;
471 use fidl_fuchsia_wlan_sme::{self as fidl_sme};
472 use fuchsia_async as fasync;
473 use futures::stream::StreamFuture;
474 use futures::task::Poll;
475 use rand::Rng;
476 use rand::prelude::ThreadRng;
477 use std::pin::pin;
478 use test_case::test_case;
479 use wlan_common::random_bss_description;
480 use wlan_common::scan::{self, Incompatible};
481 use wlan_rsn::auth;
482
483 #[test]
484 fn test_convert_connect_result() {
485 assert_eq!(
486 convert_connect_result(&ConnectResult::Success, false),
487 fidl_sme::ConnectResult {
488 code: fidl_ieee80211::StatusCode::Success,
489 is_credential_rejected: false,
490 is_reconnect: false,
491 }
492 );
493 assert_eq!(
494 convert_connect_result(&ConnectResult::Canceled, true),
495 fidl_sme::ConnectResult {
496 code: fidl_ieee80211::StatusCode::Canceled,
497 is_credential_rejected: false,
498 is_reconnect: true,
499 }
500 );
501 let connect_result =
502 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::ShouldWait));
503 assert_eq!(
504 convert_connect_result(&connect_result, false),
505 fidl_sme::ConnectResult {
506 code: fidl_ieee80211::StatusCode::Canceled,
507 is_credential_rejected: false,
508 is_reconnect: false,
509 }
510 );
511
512 let connect_result =
513 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
514 auth_method: Some(auth::MethodName::Psk),
515 reason: EstablishRsnaFailureReason::InternalError,
516 }));
517 assert_eq!(
518 convert_connect_result(&connect_result, false),
519 fidl_sme::ConnectResult {
520 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
521 is_credential_rejected: false,
522 is_reconnect: false,
523 }
524 );
525
526 let connect_result =
527 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
528 auth_method: Some(auth::MethodName::Psk),
529 reason: EstablishRsnaFailureReason::RsnaResponseTimeout(
530 wlan_rsn::Error::LikelyWrongCredential,
531 ),
532 }));
533 assert_eq!(
534 convert_connect_result(&connect_result, false),
535 fidl_sme::ConnectResult {
536 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
537 is_credential_rejected: true,
538 is_reconnect: false,
539 }
540 );
541
542 let connect_result =
543 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
544 auth_method: Some(auth::MethodName::Psk),
545 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
546 wlan_rsn::Error::LikelyWrongCredential,
547 ),
548 }));
549 assert_eq!(
550 convert_connect_result(&connect_result, false),
551 fidl_sme::ConnectResult {
552 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
553 is_credential_rejected: true,
554 is_reconnect: false,
555 }
556 );
557
558 let connect_result =
559 ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
560 auth_method: Some(auth::MethodName::Psk),
561 reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
562 wlan_rsn::Error::MissingGtkProvider,
563 ),
564 }));
565 assert_eq!(
566 convert_connect_result(&connect_result, false),
567 fidl_sme::ConnectResult {
568 code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
569 is_credential_rejected: false,
570 is_reconnect: false,
571 }
572 );
573
574 let connect_result =
575 ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::InternalError));
576 assert_eq!(
577 convert_connect_result(&connect_result, false),
578 fidl_sme::ConnectResult {
579 code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
580 is_credential_rejected: false,
581 is_reconnect: false,
582 }
583 );
584 }
585
586 #[test_case(1, true; "with 1 result")]
591 #[test_case(2, true; "with 2 results")]
592 #[test_case(30, true; "with 30 results")]
593 #[test_case(4000, true; "with 4000 results")]
594 #[test_case(50000, false; "with 50000 results")]
595 #[test_case(100000, false; "with 100000 results")]
596 fn scan_results_are_effectively_unbounded(number_of_scan_results: usize, randomize: bool) {
597 let mut exec = fasync::TestExecutor::new();
598 let (client_sme_proxy, mut client_sme_stream) =
599 create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
600
601 async fn request_and_collect_result(
603 client_sme_proxy: &fidl_sme::ClientSmeProxy,
604 ) -> fidl_sme::ClientSmeScanResult {
605 client_sme_proxy
606 .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
607 channels: vec![],
608 }))
609 .await
610 .expect("FIDL request failed")
611 }
612
613 let result_fut = request_and_collect_result(&client_sme_proxy);
614 let mut result_fut = pin!(result_fut);
615
616 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
617
618 let mut rng = rand::rng();
620 let scan_result_list = if randomize {
621 (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
622 } else {
623 vec![random_scan_result(&mut rng).into(); number_of_scan_results]
624 };
625 assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
626 Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
627 req: _, responder,
628 }))) => {
629 let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
630 responder.send(Ok(vmo)).expect("failed to send scan results");
631 }
632 );
633
634 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
636 assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
637 })
638 }
639
640 #[test]
641 fn test_serve_connect_txn_stream() {
642 let mut exec = fasync::TestExecutor::new();
643
644 let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
645 let (fidl_client_proxy, fidl_connect_txn_stream) =
646 create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
647 let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
648 let mut fidl_client_fut = pin!(fidl_client_fut);
649 let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
650
651 let test_fut =
652 serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
653 let mut test_fut = pin!(test_fut);
654
655 sme_proxy
657 .unbounded_send(ConnectTransactionEvent::OnConnectResult {
658 result: ConnectResult::Success,
659 is_reconnect: true,
660 })
661 .expect("expect sending ConnectTransactionEvent to succeed");
662 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
663 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
664 assert_matches!(
665 event,
666 fidl_sme::ConnectTransactionEvent::OnConnectResult {
667 result: fidl_sme::ConnectResult {
668 code: fidl_ieee80211::StatusCode::Success,
669 is_credential_rejected: false,
670 is_reconnect: true,
671 }
672 }
673 );
674
675 let input_info = fidl_sme::DisconnectInfo {
677 is_sme_reconnecting: true,
678 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
679 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
680 mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
681 }),
682 };
683 sme_proxy
684 .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
685 .expect("expect sending ConnectTransactionEvent to succeed");
686 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
687 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
688 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
689 assert_eq!(input_info, output_info);
690 });
691
692 let input_ind = fidl_internal::SignalReportIndication { rssi_dbm: -40, snr_db: 30 };
694 sme_proxy
695 .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind })
696 .expect("expect sending ConnectTransactionEvent to succeed");
697 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
698 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
699 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
700 assert_eq!(input_ind, ind);
701 });
702
703 let input_info = fidl_internal::ChannelSwitchInfo {
705 new_primary_channel: fidl_ieee80211::ChannelNumber {
706 band: fidl_ieee80211::WlanBand::TwoGhz,
707 number: 8,
708 },
709 bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
710 vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
711 band: fidl_ieee80211::WlanBand::TwoGhz,
712 number: 0,
713 },
714 };
715 sme_proxy
716 .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info.clone() })
717 .expect("expect sending ConnectTransactionEvent to succeed");
718 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
719 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
720 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
721 assert_eq!(input_info, info);
722 });
723
724 std::mem::drop(sme_proxy);
726 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
727 }
728
729 fn poll_stream_fut<S: Stream + std::marker::Unpin>(
730 exec: &mut fasync::TestExecutor,
731 stream_fut: &mut StreamFuture<S>,
732 ) -> Poll<Option<S::Item>> {
733 exec.run_until_stalled(stream_fut).map(|(item, stream)| {
734 *stream_fut = stream.into_future();
735 item
736 })
737 }
738
739 fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
741 use wlan_common::security::SecurityDescriptor;
742
743 wlan_common::scan::ScanResult {
745 compatibility: match rng.random_range(0..4) {
746 0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
747 1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
748 2 => wlan_common::scan::Compatible::expect_ok([
749 SecurityDescriptor::WPA2_PERSONAL,
750 SecurityDescriptor::WPA3_PERSONAL,
751 ]),
752 _ => Incompatible::unknown(),
753 },
754 timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
755 bss_description: random_bss_description!(),
756 }
757 }
758
759 #[test]
760 fn test_handle_fidl_request_apf() {
761 let mut exec = fasync::TestExecutor::new();
762 let inspector = fuchsia_inspect::Inspector::default();
763 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
764 client_sme::ClientConfig::default(),
765 test_utils::fake_device_info([0; 6].into()),
766 inspector.clone(),
767 inspector.root().create_child("sme"),
768 wlan_common::test_utils::fake_features::fake_security_support(),
769 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
770 );
771 let sme = Mutex::new(sme);
772
773 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
775 let program = vec![1, 2, 3];
776 let mut install_fut = proxy.install_apf_packet_filter(&program);
777 let mut stream = pin!(stream);
778
779 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
781 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
782 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
783
784 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::InstallApfPacketFilter(req, responder))) => {
786 assert_eq!(req.program, program);
787 responder.respond(Ok(()));
788 });
789
790 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
791 });
792 assert_matches!(exec.run_until_stalled(&mut install_fut), Poll::Ready(Ok(Ok(()))));
793
794 let mut read_fut = proxy.read_apf_packet_filter_data();
796 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
797 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
798 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
799
800 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::ReadApfPacketFilterData(responder))) => {
802 responder.respond(Ok(fidl_mlme::MlmeReadApfPacketFilterDataResponse {
803 memory: vec![4, 5, 6],
804 }));
805 });
806
807 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
808 });
809 assert_matches!(exec.run_until_stalled(&mut read_fut), Poll::Ready(Ok(Ok(data))) => {
810 assert_eq!(data, vec![4, 5, 6]);
811 });
812
813 let mut set_enabled_fut = proxy.set_apf_packet_filter_enabled(true);
815 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
816 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
817 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
818
819 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetApfPacketFilterEnabled(req, responder))) => {
821 assert!(req.enabled);
822 responder.respond(Ok(()));
823 });
824
825 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
826 });
827 assert_matches!(exec.run_until_stalled(&mut set_enabled_fut), Poll::Ready(Ok(Ok(()))));
828
829 let mut get_enabled_fut = proxy.get_apf_packet_filter_enabled();
831 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
832 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
833 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
834
835 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::GetApfPacketFilterEnabled(responder))) => {
837 responder.respond(Ok(fidl_mlme::MlmeGetApfPacketFilterEnabledResponse {
838 enabled: true,
839 }));
840 });
841
842 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
843 });
844 assert_matches!(exec.run_until_stalled(&mut get_enabled_fut), Poll::Ready(Ok(Ok(true))));
845 }
846
847 #[test_case(Ok(()); "ok")]
848 #[test_case(Err(123); "err")]
849 fn test_handle_fidl_request_set_mac_address(set_mac_address_result: Result<(), i32>) {
850 let mut exec = fasync::TestExecutor::new();
851 let inspector = fuchsia_inspect::Inspector::default();
852 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
853 client_sme::ClientConfig::default(),
854 test_utils::fake_device_info([0; 6].into()),
855 inspector.clone(),
856 inspector.root().create_child("sme"),
857 wlan_common::test_utils::fake_features::fake_security_support(),
858 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
859 );
860 let sme = Mutex::new(sme);
861
862 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
864 let new_mac = [1, 2, 3, 4, 5, 6];
865 let mut set_mac_fut = proxy.set_mac_address(&new_mac);
866 let mut stream = pin!(stream);
867
868 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
870 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
871 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
872
873 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(mac, responder))) => {
874 assert_eq!(mac, new_mac);
875 responder.respond(set_mac_address_result);
876 });
877
878 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
879 });
880
881 if set_mac_address_result.is_ok() {
882 assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Ok(()))));
883 assert_eq!(sme.lock().device_info().sta_addr, new_mac);
885 } else {
886 assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Err(123))));
887 assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
888 }
889 }
890
891 #[test]
892 fn test_handle_fidl_request_set_mac_address_cancelled() {
893 let mut exec = fasync::TestExecutor::new();
894 let inspector = fuchsia_inspect::Inspector::default();
895 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
896 client_sme::ClientConfig::default(),
897 test_utils::fake_device_info([0; 6].into()),
898 inspector.clone(),
899 inspector.root().create_child("sme"),
900 wlan_common::test_utils::fake_features::fake_security_support(),
901 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
902 );
903 let sme = Mutex::new(sme);
904
905 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
907 let new_mac = [1, 2, 3, 4, 5, 6];
908 let mut set_mac_fut = proxy.set_mac_address(&new_mac);
909 let mut stream = pin!(stream);
910
911 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
913 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
914 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
915
916 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(_mac, _responder))) => {
917 });
919
920 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
921 });
922
923 assert_matches!(
924 exec.run_until_stalled(&mut set_mac_fut),
925 Poll::Ready(Ok(Err(zx::sys::ZX_ERR_CANCELED)))
926 );
927 assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
928 }
929}