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