Skip to main content

wlan_sme/serve/
client.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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                // SME has dropped the ConnectTransaction endpoint, likely due to a disconnect.
291                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                // Must always be false on roam success.
358                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                // Current implementation assumes that all roam attempts incur disassociation from the
370                // original BSS. When this changes (e.g. due to Fast BSS Transition support), this
371                // hard-coded field should be set from the RoamResult enum.
372                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    // TODO(https://fxbug.dev/42164611): There is no test coverage for consistency between MLME scan results
509    // and SME scan results produced by wlanstack. In particular, the timestamp_nanos field
510    // of fidl_mlme::ScanResult is dropped in SME, and no tests reveal this problem.
511
512    #[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        // Request scan
524        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        // Generate and send scan results
541        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        // Verify scan results
557        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        // Test sending OnConnectResult
578        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        // Test sending OnDisconnect
598        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        // Test sending OnSignalReport
615        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        // Test sending OnChannelSwitched
626        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        // When SME proxy is dropped, the fut should terminate
637        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    // Create roughly over 2k bytes ScanResult
652    fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
653        use wlan_common::security::SecurityDescriptor;
654
655        // TODO(https://fxbug.dev/42164451): Merge this with a similar function in wlancfg.
656        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        // Test InstallApfPacketFilter
686        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        // Run handle_fidl_request for the install request
692        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            // Forward from SME to MLME
697            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        // Test ReadApfPacketFilterData
707        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            // Forward from SME to MLME
713            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        // Test SetApfPacketFilterEnabled
726        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            // Forward from SME to MLME
732            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        // Test GetApfPacketFilterEnabled
742        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            // Forward from SME to MLME
748            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}