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,
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) -> (MlmeSink, MlmeStream, impl Future<Output = Result<(), anyhow::Error>>) {
44 let wpa3_supported =
45 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
46 && security_support.sae.as_ref().is_some_and(|sae| {
47 sae.driver_handler_supported.unwrap_or(false)
48 || sae.sme_handler_supported.unwrap_or(false)
49 });
50 let owe_supported =
51 security_support.mfp.as_ref().is_some_and(|mfp| mfp.supported.unwrap_or(false))
52 && security_support.owe.as_ref().is_some_and(|owe| owe.supported.unwrap_or(false));
53 let cfg = client_sme::ClientConfig::from_config(cfg, wpa3_supported, owe_supported);
54 let (sme, mlme_sink, mlme_stream, time_stream) = Sme::new(
55 cfg,
56 device_info,
57 inspector,
58 inspect_node,
59 security_support,
60 spectrum_management_support,
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::ScanForController { req, responder } => {
103 Ok(scan(sme, req, |result| match result {
104 Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
105 Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
106 })
107 .await
108 .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
109 }
110 ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
111 Ok(set_mac_address(sme, mac_addr, responder).await?)
112 }
113 ClientSmeRequest::InstallApfPacketFilter { program, responder } => {
114 let receiver = sme.lock().install_apf_packet_filter(program);
115 let resp = match receiver.await {
116 Ok(result) => result,
117 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
118 };
119 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
120 Ok(())
121 }
122 ClientSmeRequest::ReadApfPacketFilterData { responder } => {
123 let receiver = sme.lock().read_apf_packet_filter_data();
124 let resp = match receiver.await {
125 Ok(result) => result.map(|r| r.memory),
126 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
127 };
128 responder
129 .send(resp.as_ref().map(|m| &m[..]).map_err(|e| *e))
130 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
131 Ok(())
132 }
133 ClientSmeRequest::SetApfPacketFilterEnabled { enabled, responder } => {
134 let receiver = sme.lock().set_apf_packet_filter_enabled(enabled);
135 let resp = match receiver.await {
136 Ok(result) => result,
137 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
138 };
139 responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
140 Ok(())
141 }
142 ClientSmeRequest::GetApfPacketFilterEnabled { responder } => {
143 let receiver = sme.lock().get_apf_packet_filter_enabled();
144 let resp = match receiver.await {
145 Ok(result) => result.map(|r| r.enabled),
146 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
147 };
148 responder
149 .send(resp.as_ref().map(|e| *e).map_err(|e| *e))
150 .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
151 Ok(())
152 }
153 }
154}
155
156async fn handle_telemetry_fidl_request(
157 sme: &Mutex<Sme>,
158 request: TelemetryRequest,
159) -> Result<(), fidl::Error> {
160 match request {
161 TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
162 let support_fut = sme.lock().query_telemetry_support();
163 let support = support_fut
164 .await
165 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
166 .and_then(|result| result);
167 responder.send(support.as_ref().map_err(|e| *e))
168 }
169 TelemetryRequest::GetIfaceStats { responder, .. } => {
170 let iface_stats_fut = sme.lock().iface_stats();
171 let iface_stats = iface_stats_fut
172 .await
173 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
174 .and_then(|stats| match stats {
175 fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
176 fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
177 });
178 responder.send(iface_stats.as_ref().map_err(|e| *e))
179 }
180 TelemetryRequest::GetHistogramStats { responder, .. } => {
181 let histogram_stats_fut = sme.lock().histogram_stats();
182 let histogram_stats = histogram_stats_fut
183 .await
184 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
185 .and_then(|stats| match stats {
186 fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
187 fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
188 });
189 responder.send(histogram_stats.as_ref().map_err(|e| *e))
190 }
191 TelemetryRequest::GetSignalReport { responder, .. } => {
192 let signal_report_fut = sme.lock().signal_report();
193 let signal_report = signal_report_fut
194 .await
195 .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
196 .and_then(|result| result);
197 responder.send(signal_report.as_ref().map_err(|e| *e))
198 }
199 TelemetryRequest::CloneInspectVmo { responder } => {
200 let inspect_vmo =
201 sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
202 responder.send(inspect_vmo)
203 }
204 }
205}
206
207async fn scan(
208 sme: &Mutex<Sme>,
209 request: fidl_sme::ScanRequest,
210 responder: impl FnOnce(
211 Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
212 ) -> Result<(), anyhow::Error>,
213) -> Result<(), anyhow::Error> {
214 let receiver = sme.lock().on_scan_command(request);
215 let receive_result = match receiver.await {
216 Ok(receive_result) => receive_result,
217 Err(e) => {
218 error!("Scan receiver error: {:?}", e);
219 responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
220 return Ok(());
221 }
222 };
223
224 match receive_result {
225 Ok(scan_results) => {
226 let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
227 responder(Ok(results))
228 }
229 Err(mlme_scan_result_code) => {
230 let scan_error_code = match mlme_scan_result_code {
231 fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
232 error!("Internal scan error: {:?}", mlme_scan_result_code);
233 fidl_sme::ScanErrorCode::InternalError
234 }
235 fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
236 fidl_mlme::ScanResultCode::InternalError => {
237 fidl_sme::ScanErrorCode::InternalMlmeError
238 }
239 fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
240 fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
241 fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
242 }
243 };
244 responder(Err(scan_error_code))
245 }
246 }?;
247 Ok(())
248}
249
250async fn connect(
251 sme: &Mutex<Sme>,
252 txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
253 req: fidl_sme::ConnectRequest,
254) -> Result<(), anyhow::Error> {
255 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
256 let handle = match txn {
257 None => None,
258 Some(txn) => Some(txn.into_stream().control_handle()),
259 };
260 let connect_txn_stream = sme.lock().on_connect_command(req);
261 serve_connect_txn_stream(handle, connect_txn_stream).await?;
262 Ok(())
263}
264
265async fn serve_connect_txn_stream(
266 handle: Option<fidl_sme::ConnectTransactionControlHandle>,
267 mut connect_txn_stream: ConnectTransactionStream,
268) -> Result<(), anyhow::Error> {
269 if let Some(handle) = handle {
270 loop {
271 match connect_txn_stream.next().await {
272 Some(event) => match event {
273 ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
274 let connect_result = convert_connect_result(&result, is_reconnect);
275 handle.send_on_connect_result(&connect_result)
276 }
277 ConnectTransactionEvent::OnRoamResult { result } => {
278 let roam_result = convert_roam_result(&result);
279 handle.send_on_roam_result(&roam_result)
280 }
281 ConnectTransactionEvent::OnDisconnect { info } => {
282 handle.send_on_disconnect(&info)
283 }
284 ConnectTransactionEvent::OnSignalReport { ind } => {
285 handle.send_on_signal_report(&ind)
286 }
287 ConnectTransactionEvent::OnChannelSwitched { info } => {
288 handle.send_on_channel_switched(&info)
289 }
290 }?,
291 None => return Ok(()),
293 }
294 }
295 }
296 Ok(())
297}
298
299fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
300 sme.lock().on_roam_command(req);
301}
302
303fn disconnect(
304 sme: &Mutex<Sme>,
305 policy_disconnect_reason: fidl_sme::UserDisconnectReason,
306 responder: fidl_sme::ClientSmeDisconnectResponder,
307) {
308 sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
309}
310
311fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
312 sme.lock().status().into()
313}
314
315async fn wmm_status(
316 sme: &Mutex<Sme>,
317 responder: fidl_sme::ClientSmeWmmStatusResponder,
318) -> Result<(), fidl::Error> {
319 let receiver = sme.lock().wmm_status();
320 let wmm_status = match receiver.await {
321 Ok(result) => result,
322 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
323 };
324 responder.send(wmm_status.as_ref().map_err(|e| *e))
325}
326
327async fn set_mac_address(
328 sme: &Mutex<Sme>,
329 mac_addr: [u8; 6],
330 responder: fidl_sme::ClientSmeSetMacAddressResponder,
331) -> Result<(), fidl::Error> {
332 let receiver = sme.lock().set_mac_address(mac_addr);
333 let resp = match receiver.await {
334 Ok(result) => result,
335 Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
336 };
337 responder.send(resp)
338}
339
340fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
341 let (code, is_credential_rejected) = match result {
342 ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
343 ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
344 ConnectResult::Failed(failure) => {
345 (failure.status_code(), failure.likely_due_to_credential_rejected())
346 }
347 };
348 fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
349}
350
351fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
352 match result {
353 RoamResult::Success(bss) => {
354 let bss_description = Some(Box::new(BssDescriptionFidl::from(*bss.clone())));
355 fidl_sme::RoamResult {
356 bssid: bss.bssid.to_array(),
357 status_code: fidl_ieee80211::StatusCode::Success,
358 original_association_maintained: false,
360 bss_description,
361 disconnect_info: None,
362 is_credential_rejected: false,
363 }
364 }
365 RoamResult::Failed(failure) => {
366 #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
367 fidl_sme::RoamResult {
368 bssid: failure.selected_bssid.to_array(),
369 status_code: failure.status_code,
370 original_association_maintained: false,
374 bss_description: match &failure.selected_bss {
375 Some(bss) => Some(Box::new(bss.clone().into())),
376 None => None,
377 },
378 disconnect_info: Some(Box::new(failure.disconnect_info)),
379 is_credential_rejected: failure.likely_due_to_credential_rejected(),
380 }
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
389 use crate::test_utils;
390 use assert_matches::assert_matches;
391 use fidl::endpoints::create_proxy_and_stream;
392 use fidl_fuchsia_wlan_mlme::ScanResultCode;
393 use fidl_fuchsia_wlan_sme::{self as fidl_sme};
394 use futures::stream::StreamFuture;
395 use futures::task::Poll;
396 use rand::Rng;
397 use rand::prelude::ThreadRng;
398 use std::pin::pin;
399 use test_case::test_case;
400 use wlan_common::random_bss_description;
401 use wlan_common::scan::{self, Incompatible};
402 use wlan_rsn::auth;
403 use {fidl_fuchsia_wlan_internal as fidl_internal, fuchsia_async as fasync};
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 .await
530 .expect("FIDL request failed")
531 }
532
533 let result_fut = request_and_collect_result(&client_sme_proxy);
534 let mut result_fut = pin!(result_fut);
535
536 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
537
538 let mut rng = rand::rng();
540 let scan_result_list = if randomize {
541 (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
542 } else {
543 vec![random_scan_result(&mut rng).into(); number_of_scan_results]
544 };
545 assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
546 Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
547 req: _, responder,
548 }))) => {
549 let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
550 responder.send(Ok(vmo)).expect("failed to send scan results");
551 }
552 );
553
554 assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
556 assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
557 })
558 }
559
560 #[test]
561 fn test_serve_connect_txn_stream() {
562 let mut exec = fasync::TestExecutor::new();
563
564 let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
565 let (fidl_client_proxy, fidl_connect_txn_stream) =
566 create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
567 let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
568 let mut fidl_client_fut = pin!(fidl_client_fut);
569 let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
570
571 let test_fut =
572 serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
573 let mut test_fut = pin!(test_fut);
574
575 sme_proxy
577 .unbounded_send(ConnectTransactionEvent::OnConnectResult {
578 result: ConnectResult::Success,
579 is_reconnect: true,
580 })
581 .expect("expect sending ConnectTransactionEvent to succeed");
582 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
583 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
584 assert_matches!(
585 event,
586 fidl_sme::ConnectTransactionEvent::OnConnectResult {
587 result: fidl_sme::ConnectResult {
588 code: fidl_ieee80211::StatusCode::Success,
589 is_credential_rejected: false,
590 is_reconnect: true,
591 }
592 }
593 );
594
595 let input_info = fidl_sme::DisconnectInfo {
597 is_sme_reconnecting: true,
598 disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
599 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
600 mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
601 }),
602 };
603 sme_proxy
604 .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
605 .expect("expect sending ConnectTransactionEvent to succeed");
606 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
607 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
608 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
609 assert_eq!(input_info, output_info);
610 });
611
612 let input_ind = fidl_internal::SignalReportIndication { rssi_dbm: -40, snr_db: 30 };
614 sme_proxy
615 .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind })
616 .expect("expect sending ConnectTransactionEvent to succeed");
617 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
618 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
619 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
620 assert_eq!(input_ind, ind);
621 });
622
623 let input_info = fidl_internal::ChannelSwitchInfo { new_channel: 8 };
625 sme_proxy
626 .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info })
627 .expect("expect sending ConnectTransactionEvent to succeed");
628 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
629 let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
630 assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
631 assert_eq!(input_info, info);
632 });
633
634 std::mem::drop(sme_proxy);
636 assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
637 }
638
639 fn poll_stream_fut<S: Stream + std::marker::Unpin>(
640 exec: &mut fasync::TestExecutor,
641 stream_fut: &mut StreamFuture<S>,
642 ) -> Poll<Option<S::Item>> {
643 exec.run_until_stalled(stream_fut).map(|(item, stream)| {
644 *stream_fut = stream.into_future();
645 item
646 })
647 }
648
649 fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
651 use wlan_common::security::SecurityDescriptor;
652
653 wlan_common::scan::ScanResult {
655 compatibility: match rng.random_range(0..4) {
656 0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
657 1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
658 2 => wlan_common::scan::Compatible::expect_ok([
659 SecurityDescriptor::WPA2_PERSONAL,
660 SecurityDescriptor::WPA3_PERSONAL,
661 ]),
662 _ => Incompatible::unknown(),
663 },
664 timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
665 bss_description: random_bss_description!(),
666 }
667 }
668
669 #[test]
670 fn test_handle_fidl_request_apf() {
671 let mut exec = fasync::TestExecutor::new();
672 let inspector = fuchsia_inspect::Inspector::default();
673 let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
674 client_sme::ClientConfig::default(),
675 test_utils::fake_device_info([0; 6].into()),
676 inspector.clone(),
677 inspector.root().create_child("sme"),
678 wlan_common::test_utils::fake_features::fake_security_support(),
679 wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
680 );
681 let sme = Mutex::new(sme);
682
683 let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
685 let program = vec![1, 2, 3];
686 let mut install_fut = proxy.install_apf_packet_filter(&program);
687 let mut stream = pin!(stream);
688
689 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
691 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
692 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
693
694 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::InstallApfPacketFilter(req, responder))) => {
696 assert_eq!(req.program, program);
697 responder.respond(Ok(()));
698 });
699
700 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
701 });
702 assert_matches!(exec.run_until_stalled(&mut install_fut), Poll::Ready(Ok(Ok(()))));
703
704 let mut read_fut = proxy.read_apf_packet_filter_data();
706 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
707 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
708 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
709
710 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::ReadApfPacketFilterData(responder))) => {
712 responder.respond(Ok(fidl_mlme::MlmeReadApfPacketFilterDataResponse {
713 memory: vec![4, 5, 6],
714 }));
715 });
716
717 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
718 });
719 assert_matches!(exec.run_until_stalled(&mut read_fut), Poll::Ready(Ok(Ok(data))) => {
720 assert_eq!(data, vec![4, 5, 6]);
721 });
722
723 let mut set_enabled_fut = proxy.set_apf_packet_filter_enabled(true);
725 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
726 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
727 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
728
729 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetApfPacketFilterEnabled(req, responder))) => {
731 assert!(req.enabled);
732 responder.respond(Ok(()));
733 });
734
735 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
736 });
737 assert_matches!(exec.run_until_stalled(&mut set_enabled_fut), Poll::Ready(Ok(Ok(()))));
738
739 let mut get_enabled_fut = proxy.get_apf_packet_filter_enabled();
741 assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
742 let mut handle_fut = pin!(handle_fidl_request(&sme, req));
743 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
744
745 assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::GetApfPacketFilterEnabled(responder))) => {
747 responder.respond(Ok(fidl_mlme::MlmeGetApfPacketFilterEnabledResponse {
748 enabled: true,
749 }));
750 });
751
752 assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
753 });
754 assert_matches!(exec.run_until_stalled(&mut get_enabled_fut), Poll::Ready(Ok(Ok(true))));
755 }
756}