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, 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    power_manager: Arc<dyn wlan_power_manager::SyncPowerManager>,
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        power_manager,
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::StartScheduledScan { req, txn, responder } => {
103            start_scheduled_scan(sme, req, txn, responder).await
104        }
105
106        ClientSmeRequest::GetScheduledScanEnabled { responder } => {
107            let receiver = sme.lock().on_get_scheduled_scan_enabled_command();
108            let resp = match receiver.await {
109                Ok(result) => result,
110                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
111            };
112            responder
113                .send(resp.as_ref().map(|r| !r.active_txn_ids.is_empty()).map_err(|e| *e))
114                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
115            Ok(())
116        }
117        ClientSmeRequest::ScanForController { req, responder } => {
118            Ok(scan(sme, req, |result| match result {
119                Ok(results) => responder.send(Ok(&results[..])).map_err(|e| e.into()),
120                Err(e) => responder.send(Err(e)).map_err(|e| e.into()),
121            })
122            .await
123            .unwrap_or_else(|e| error!("Error handling a test scan transaction: {:?}", e)))
124        }
125        ClientSmeRequest::SetMacAddress { mac_addr, responder } => {
126            Ok(set_mac_address(sme, mac_addr, responder).await?)
127        }
128        ClientSmeRequest::InstallApfPacketFilter { program, responder } => {
129            let receiver = sme.lock().install_apf_packet_filter(program);
130            let resp = match receiver.await {
131                Ok(result) => result,
132                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
133            };
134            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
135            Ok(())
136        }
137        ClientSmeRequest::ReadApfPacketFilterData { responder } => {
138            let receiver = sme.lock().read_apf_packet_filter_data();
139            let resp = match receiver.await {
140                Ok(result) => result.map(|r| r.memory),
141                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
142            };
143            responder
144                .send(resp.as_ref().map(|m| &m[..]).map_err(|e| *e))
145                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
146            Ok(())
147        }
148        ClientSmeRequest::SetApfPacketFilterEnabled { enabled, responder } => {
149            let receiver = sme.lock().set_apf_packet_filter_enabled(enabled);
150            let resp = match receiver.await {
151                Ok(result) => result,
152                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
153            };
154            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
155            Ok(())
156        }
157        ClientSmeRequest::GetApfPacketFilterEnabled { responder } => {
158            let receiver = sme.lock().get_apf_packet_filter_enabled();
159            let resp = match receiver.await {
160                Ok(result) => result.map(|r| r.enabled),
161                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
162            };
163            responder
164                .send(resp.as_ref().map(|e| *e).map_err(|e| *e))
165                .unwrap_or_else(|e| error!("Error sending response: {:?}", e));
166            Ok(())
167        }
168        ClientSmeRequest::StartRssiMonitor { min_rssi_dbm, max_rssi_dbm, responder } => {
169            let receiver = sme.lock().start_rssi_monitor(min_rssi_dbm, max_rssi_dbm);
170            let resp = match receiver.await {
171                Ok(result) => result,
172                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
173            };
174            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
175            Ok(())
176        }
177        ClientSmeRequest::StopRssiMonitor { responder } => {
178            let receiver = sme.lock().stop_rssi_monitor();
179            let resp = match receiver.await {
180                Ok(result) => result,
181                Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
182            };
183            responder.send(resp).unwrap_or_else(|e| error!("Error sending response: {:?}", e));
184            Ok(())
185        }
186    }
187}
188
189async fn handle_telemetry_fidl_request(
190    sme: &Mutex<Sme>,
191    request: TelemetryRequest,
192) -> Result<(), fidl::Error> {
193    match request {
194        TelemetryRequest::QueryTelemetrySupport { responder, .. } => {
195            let support_fut = sme.lock().query_telemetry_support();
196            let support = support_fut
197                .await
198                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
199                .and_then(|result| result);
200            responder.send(support.as_ref().map_err(|e| *e))
201        }
202        TelemetryRequest::GetIfaceStats { responder, .. } => {
203            let iface_stats_fut = sme.lock().iface_stats();
204            let iface_stats = iface_stats_fut
205                .await
206                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
207                .and_then(|stats| match stats {
208                    fidl_mlme::GetIfaceStatsResponse::Stats(stats) => Ok(stats),
209                    fidl_mlme::GetIfaceStatsResponse::ErrorStatus(err) => Err(err),
210                });
211            responder.send(iface_stats.as_ref().map_err(|e| *e))
212        }
213        TelemetryRequest::GetHistogramStats { responder, .. } => {
214            let histogram_stats_fut = sme.lock().histogram_stats();
215            let histogram_stats = histogram_stats_fut
216                .await
217                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
218                .and_then(|stats| match stats {
219                    fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => Ok(stats),
220                    fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(err) => Err(err),
221                });
222            responder.send(histogram_stats.as_ref().map_err(|e| *e))
223        }
224        TelemetryRequest::GetSignalReport { responder, .. } => {
225            let signal_report_fut = sme.lock().signal_report();
226            let signal_report = signal_report_fut
227                .await
228                .map_err(|_| zx::Status::CONNECTION_ABORTED.into_raw())
229                .and_then(|result| result);
230            if let Ok(ref report) = signal_report
231                && let Some(ref conn_report) = report.connection_signal_report
232            {
233                sme.lock().on_signal_report(conn_report);
234            }
235            responder.send(signal_report.as_ref().map_err(|e| *e))
236        }
237        TelemetryRequest::CloneInspectVmo { responder } => {
238            let inspect_vmo =
239                sme.lock().on_clone_inspect_vmo().ok_or_else(|| zx::Status::INTERNAL.into_raw());
240            responder.send(inspect_vmo)
241        }
242    }
243}
244
245async fn scan(
246    sme: &Mutex<Sme>,
247    request: fidl_sme::ScanRequest,
248    responder: impl FnOnce(
249        Result<Vec<fidl_sme::ScanResult>, fidl_sme::ScanErrorCode>,
250    ) -> Result<(), anyhow::Error>,
251) -> Result<(), anyhow::Error> {
252    let receiver = sme.lock().on_scan_command(request);
253    let receive_result = match receiver.await {
254        Ok(receive_result) => receive_result,
255        Err(e) => {
256            error!("Scan receiver error: {:?}", e);
257            responder(Err(fidl_sme::ScanErrorCode::InternalError))?;
258            return Ok(());
259        }
260    };
261
262    match receive_result {
263        Ok(scan_results) => {
264            let results = scan_results.into_iter().map(Into::into).collect::<Vec<_>>();
265            responder(Ok(results))
266        }
267        Err(mlme_scan_result_code) => {
268            let scan_error_code = match mlme_scan_result_code {
269                fidl_mlme::ScanResultCode::Success | fidl_mlme::ScanResultCode::InvalidArgs => {
270                    error!("Internal scan error: {:?}", mlme_scan_result_code);
271                    fidl_sme::ScanErrorCode::InternalError
272                }
273                fidl_mlme::ScanResultCode::NotSupported => fidl_sme::ScanErrorCode::NotSupported,
274                fidl_mlme::ScanResultCode::InternalError => {
275                    fidl_sme::ScanErrorCode::InternalMlmeError
276                }
277                fidl_mlme::ScanResultCode::ShouldWait => fidl_sme::ScanErrorCode::ShouldWait,
278                fidl_mlme::ScanResultCode::CanceledByDriverOrFirmware => {
279                    fidl_sme::ScanErrorCode::CanceledByDriverOrFirmware
280                }
281            };
282            responder(Err(scan_error_code))
283        }
284    }?;
285    Ok(())
286}
287
288async fn connect(
289    sme: &Mutex<Sme>,
290    txn: Option<ServerEnd<fidl_sme::ConnectTransactionMarker>>,
291    req: fidl_sme::ConnectRequest,
292) -> Result<(), anyhow::Error> {
293    #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
294    let handle = match txn {
295        None => None,
296        Some(txn) => Some(txn.into_stream().control_handle()),
297    };
298    let connect_txn_stream = sme.lock().on_connect_command(req);
299    serve_connect_txn_stream(handle, connect_txn_stream).await?;
300    Ok(())
301}
302
303/// Serves a scheduled scan session by processing a stream for events sent by MLME and processed in
304/// SME's scan scheduler, and a stream for client-initiated cancellations.
305async fn serve_sched_scan_session(
306    txn_handle: fidl_sme::ScheduledScanTransactionControlHandle,
307    txn_stream: fidl_sme::ScheduledScanTransactionRequestStream,
308    session: ScheduledScanReceiver,
309) -> Result<(), anyhow::Error> {
310    // Client will close the transaction channel to cancel session.
311    let mut cancellation_stream = txn_stream.fuse();
312    let mut session_stream = session.fuse();
313    loop {
314        futures::select! {
315            scan_results = session_stream.next() => {
316                if let Some(vmo) = scan_results {
317                    txn_handle.send_on_scheduled_scan_matches_available(vmo)?;
318                } else {
319                    // Stream closed naturally (SME dropped sender because firmware stopped it).
320                    txn_handle.shutdown_with_epitaph(Ok(()));
321                    break;
322                }
323            },
324            _ = cancellation_stream.next() => {
325                // Client dropped transaction stream. Cleanup is handled by ScheduledScanReceiver's
326                // Drop implementation.
327                break;
328            }
329        }
330    }
331    Ok(())
332}
333
334/// Creates a new scheduled scan session and starts serving it.
335async fn start_scheduled_scan(
336    sme: &Mutex<Sme>,
337    req: fidl_common::ScheduledScanRequest,
338    txn_server: ServerEnd<fidl_sme::ScheduledScanTransactionMarker>,
339    responder: fidl_sme::ClientSmeStartScheduledScanResponder,
340) -> Result<(), fidl::Error> {
341    let txn_receiver_stream = txn_server.into_stream();
342    let txn_sender_handle = txn_receiver_stream.control_handle();
343    let (receiver, session) = sme.lock().on_start_scheduled_scan_command(req);
344    match receiver.await.unwrap_or_else(|_| Err(zx::Status::CANCELED.into_raw())) {
345        Ok(()) => {
346            responder.send(Ok(()))?;
347            serve_sched_scan_session(txn_sender_handle, txn_receiver_stream, session)
348                .await
349                .unwrap_or_else(|e| {
350                    error!("Error serving sched scan txn stream: {:?}", e);
351                });
352        }
353        Err(status) => {
354            responder.send(Err(status))?;
355            txn_sender_handle.shutdown_with_epitaph(zx::Status::err_from_raw(status));
356        }
357    }
358    Ok(())
359}
360
361async fn serve_connect_txn_stream(
362    handle: Option<fidl_sme::ConnectTransactionControlHandle>,
363    mut connect_txn_stream: ConnectTransactionStream,
364) -> Result<(), anyhow::Error> {
365    if let Some(handle) = handle {
366        loop {
367            match connect_txn_stream.next().await {
368                Some(event) => match event {
369                    ConnectTransactionEvent::OnConnectResult { result, is_reconnect } => {
370                        let connect_result = convert_connect_result(&result, is_reconnect);
371                        handle.send_on_connect_result(&connect_result)
372                    }
373                    ConnectTransactionEvent::OnRoamResult { result } => {
374                        let roam_result = convert_roam_result(&result);
375                        handle.send_on_roam_result(&roam_result)
376                    }
377                    ConnectTransactionEvent::OnDisconnect { info } => {
378                        handle.send_on_disconnect(&info)
379                    }
380                    ConnectTransactionEvent::OnSignalReport { ind } => {
381                        handle.send_on_signal_report(&ind)
382                    }
383                    ConnectTransactionEvent::OnRssiThresholdBreached { cur_rssi_dbm } => {
384                        handle.send_on_rssi_threshold_breached(cur_rssi_dbm)
385                    }
386                    ConnectTransactionEvent::OnChannelSwitched { info } => {
387                        handle.send_on_channel_switched(&info)
388                    }
389                }?,
390                // SME has dropped the ConnectTransaction endpoint, likely due to a disconnect.
391                None => return Ok(()),
392            }
393        }
394    }
395    Ok(())
396}
397
398fn roam(sme: &Mutex<Sme>, req: fidl_sme::RoamRequest) {
399    sme.lock().on_roam_command(req);
400}
401
402fn disconnect(
403    sme: &Mutex<Sme>,
404    policy_disconnect_reason: fidl_sme::UserDisconnectReason,
405    responder: fidl_sme::ClientSmeDisconnectResponder,
406) {
407    sme.lock().on_disconnect_command(policy_disconnect_reason, responder);
408}
409
410fn status(sme: &Mutex<Sme>) -> fidl_sme::ClientStatusResponse {
411    sme.lock().status().into()
412}
413
414async fn wmm_status(
415    sme: &Mutex<Sme>,
416    responder: fidl_sme::ClientSmeWmmStatusResponder,
417) -> Result<(), fidl::Error> {
418    let receiver = sme.lock().wmm_status();
419    let wmm_status = match receiver.await {
420        Ok(result) => result,
421        Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
422    };
423    responder.send(wmm_status.as_ref().map_err(|e| *e))
424}
425
426async fn set_mac_address(
427    sme: &Mutex<Sme>,
428    mac_addr: [u8; 6],
429    responder: fidl_sme::ClientSmeSetMacAddressResponder,
430) -> Result<(), fidl::Error> {
431    let receiver = sme.lock().set_mac_address(mac_addr);
432    let resp = match receiver.await {
433        Ok(Ok(())) => {
434            sme.lock().update_mac_address(mac_addr);
435            Ok(())
436        }
437        Ok(result @ Err(_)) => result,
438        Err(_) => Err(zx::sys::ZX_ERR_CANCELED),
439    };
440    responder.send(resp)
441}
442
443fn convert_connect_result(result: &ConnectResult, is_reconnect: bool) -> fidl_sme::ConnectResult {
444    let (code, is_credential_rejected) = match result {
445        ConnectResult::Success => (fidl_ieee80211::StatusCode::Success, false),
446        ConnectResult::Canceled => (fidl_ieee80211::StatusCode::Canceled, false),
447        ConnectResult::Failed(failure) => {
448            (failure.status_code(), failure.likely_due_to_credential_rejected())
449        }
450    };
451    fidl_sme::ConnectResult { code, is_credential_rejected, is_reconnect }
452}
453
454fn convert_roam_result(result: &RoamResult) -> fidl_sme::RoamResult {
455    match result {
456        RoamResult::Success(bss) => {
457            let bss_description =
458                Some(Box::new(fidl_ieee80211::BssDescription::from(*bss.clone())));
459            fidl_sme::RoamResult {
460                bssid: bss.bssid.to_array(),
461                status_code: fidl_ieee80211::StatusCode::Success,
462                // Must always be false on roam success.
463                original_association_maintained: false,
464                bss_description,
465                disconnect_info: None,
466                is_credential_rejected: false,
467            }
468        }
469        RoamResult::Failed(failure) => {
470            #[allow(clippy::manual_map, reason = "mass allow for https://fxbug.dev/381896734")]
471            fidl_sme::RoamResult {
472                bssid: failure.selected_bssid.to_array(),
473                status_code: failure.status_code,
474                // Current implementation assumes that all roam attempts incur disassociation from the
475                // original BSS. When this changes (e.g. due to Fast BSS Transition support), this
476                // hard-coded field should be set from the RoamResult enum.
477                original_association_maintained: false,
478                bss_description: match &failure.selected_bss {
479                    Some(bss) => Some(Box::new(bss.clone().into())),
480                    None => None,
481                },
482                disconnect_info: Some(Box::new(failure.disconnect_info)),
483                is_credential_rejected: failure.likely_due_to_credential_rejected(),
484            }
485        }
486    }
487}
488
489#[cfg(test)]
490mod tests {
491    use super::*;
492    use crate::client::{ConnectFailure, EstablishRsnaFailure, EstablishRsnaFailureReason};
493    use crate::test_utils;
494    use assert_matches::assert_matches;
495    use fidl::endpoints::create_proxy_and_stream;
496    use fidl_fuchsia_wlan_internal as fidl_internal;
497    use fidl_fuchsia_wlan_mlme::ScanResultCode;
498    use fidl_fuchsia_wlan_sme::{self as fidl_sme};
499    use fidl_fuchsia_wlan_stats as fidl_stats;
500    use fuchsia_async as fasync;
501    use futures::stream::StreamFuture;
502    use futures::task::Poll;
503    use rand::RngExt as _;
504    use rand::prelude::ThreadRng;
505    use std::pin::pin;
506    use test_case::test_case;
507    use wlan_common::random_bss_description;
508    use wlan_common::scan::{self, Incompatible};
509    use wlan_rsn::auth;
510
511    #[test]
512    fn test_convert_connect_result() {
513        assert_eq!(
514            convert_connect_result(&ConnectResult::Success, false),
515            fidl_sme::ConnectResult {
516                code: fidl_ieee80211::StatusCode::Success,
517                is_credential_rejected: false,
518                is_reconnect: false,
519            }
520        );
521        assert_eq!(
522            convert_connect_result(&ConnectResult::Canceled, true),
523            fidl_sme::ConnectResult {
524                code: fidl_ieee80211::StatusCode::Canceled,
525                is_credential_rejected: false,
526                is_reconnect: true,
527            }
528        );
529        let connect_result =
530            ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::ShouldWait));
531        assert_eq!(
532            convert_connect_result(&connect_result, false),
533            fidl_sme::ConnectResult {
534                code: fidl_ieee80211::StatusCode::Canceled,
535                is_credential_rejected: false,
536                is_reconnect: false,
537            }
538        );
539
540        let connect_result =
541            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
542                auth_method: Some(auth::MethodName::Psk),
543                reason: EstablishRsnaFailureReason::InternalError,
544            }));
545        assert_eq!(
546            convert_connect_result(&connect_result, false),
547            fidl_sme::ConnectResult {
548                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
549                is_credential_rejected: false,
550                is_reconnect: false,
551            }
552        );
553
554        let connect_result =
555            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
556                auth_method: Some(auth::MethodName::Psk),
557                reason: EstablishRsnaFailureReason::RsnaResponseTimeout(
558                    wlan_rsn::Error::LikelyWrongCredential,
559                ),
560            }));
561        assert_eq!(
562            convert_connect_result(&connect_result, false),
563            fidl_sme::ConnectResult {
564                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
565                is_credential_rejected: true,
566                is_reconnect: false,
567            }
568        );
569
570        let connect_result =
571            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
572                auth_method: Some(auth::MethodName::Psk),
573                reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
574                    wlan_rsn::Error::LikelyWrongCredential,
575                ),
576            }));
577        assert_eq!(
578            convert_connect_result(&connect_result, false),
579            fidl_sme::ConnectResult {
580                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
581                is_credential_rejected: true,
582                is_reconnect: false,
583            }
584        );
585
586        let connect_result =
587            ConnectResult::Failed(ConnectFailure::EstablishRsnaFailure(EstablishRsnaFailure {
588                auth_method: Some(auth::MethodName::Psk),
589                reason: EstablishRsnaFailureReason::RsnaCompletionTimeout(
590                    wlan_rsn::Error::MissingGtkProvider,
591                ),
592            }));
593        assert_eq!(
594            convert_connect_result(&connect_result, false),
595            fidl_sme::ConnectResult {
596                code: fidl_ieee80211::StatusCode::EstablishRsnaFailure,
597                is_credential_rejected: false,
598                is_reconnect: false,
599            }
600        );
601
602        let connect_result =
603            ConnectResult::Failed(ConnectFailure::ScanFailure(ScanResultCode::InternalError));
604        assert_eq!(
605            convert_connect_result(&connect_result, false),
606            fidl_sme::ConnectResult {
607                code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
608                is_credential_rejected: false,
609                is_reconnect: false,
610            }
611        );
612    }
613
614    // TODO(https://fxbug.dev/42164611): There is no test coverage for consistency between MLME scan results
615    // and SME scan results produced by wlanstack. In particular, the timestamp_nanos field
616    // of fidl_mlme::ScanResult is dropped in SME, and no tests reveal this problem.
617
618    #[test_case(1, true; "with 1 result")]
619    #[test_case(2, true; "with 2 results")]
620    #[test_case(30, true; "with 30 results")]
621    #[test_case(4000, true; "with 4000 results")]
622    #[test_case(50000, false; "with 50000 results")]
623    #[test_case(100000, false; "with 100000 results")]
624    fn scan_results_are_effectively_unbounded(number_of_scan_results: usize, randomize: bool) {
625        let mut exec = fasync::TestExecutor::new();
626        let (client_sme_proxy, mut client_sme_stream) =
627            create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
628
629        // Request scan
630        async fn request_and_collect_result(
631            client_sme_proxy: &fidl_sme::ClientSmeProxy,
632        ) -> fidl_sme::ClientSmeScanResult {
633            client_sme_proxy
634                .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {
635                    channels: vec![],
636                }))
637                .await
638                .expect("FIDL request failed")
639        }
640
641        let result_fut = request_and_collect_result(&client_sme_proxy);
642        let mut result_fut = pin!(result_fut);
643
644        assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Pending);
645
646        // Generate and send scan results
647        let mut rng = rand::rng();
648        let scan_result_list = if randomize {
649            (0..number_of_scan_results).map(|_| random_scan_result(&mut rng).into()).collect()
650        } else {
651            vec![random_scan_result(&mut rng).into(); number_of_scan_results]
652        };
653        assert_matches!(exec.run_until_stalled(&mut client_sme_stream.next()),
654                        Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Scan {
655                            req: _, responder,
656                        }))) => {
657                            let vmo = write_vmo(scan_result_list.clone()).expect("failed to write VMO");
658                            responder.send(Ok(vmo)).expect("failed to send scan results");
659                        }
660        );
661
662        // Verify scan results
663        assert_matches!(exec.run_until_stalled(&mut result_fut), Poll::Ready(Ok(vmo)) => {
664            assert_eq!(scan_result_list, scan::read_vmo(vmo).expect("failed to read VMO"));
665        })
666    }
667
668    #[test]
669    fn test_serve_connect_txn_stream() {
670        let mut exec = fasync::TestExecutor::new();
671
672        let (sme_proxy, sme_connect_txn_stream) = mpsc::unbounded();
673        let (fidl_client_proxy, fidl_connect_txn_stream) =
674            create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
675        let fidl_client_fut = fidl_client_proxy.take_event_stream().into_future();
676        let mut fidl_client_fut = pin!(fidl_client_fut);
677        let fidl_connect_txn_handle = fidl_connect_txn_stream.control_handle();
678
679        let test_fut =
680            serve_connect_txn_stream(Some(fidl_connect_txn_handle), sme_connect_txn_stream);
681        let mut test_fut = pin!(test_fut);
682
683        // Test sending OnConnectResult
684        sme_proxy
685            .unbounded_send(ConnectTransactionEvent::OnConnectResult {
686                result: ConnectResult::Success,
687                is_reconnect: true,
688            })
689            .expect("expect sending ConnectTransactionEvent to succeed");
690        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
691        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
692        assert_matches!(
693            event,
694            fidl_sme::ConnectTransactionEvent::OnConnectResult {
695                result: fidl_sme::ConnectResult {
696                    code: fidl_ieee80211::StatusCode::Success,
697                    is_credential_rejected: false,
698                    is_reconnect: true,
699                }
700            }
701        );
702
703        // Test sending OnDisconnect
704        let input_info = fidl_sme::DisconnectInfo {
705            is_sme_reconnecting: true,
706            disconnect_source: fidl_sme::DisconnectSource::Mlme(fidl_sme::DisconnectCause {
707                reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
708                mlme_event_name: fidl_sme::DisconnectMlmeEventName::DeauthenticateIndication,
709            }),
710        };
711        sme_proxy
712            .unbounded_send(ConnectTransactionEvent::OnDisconnect { info: input_info })
713            .expect("expect sending ConnectTransactionEvent to succeed");
714        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
715        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
716        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnDisconnect { info: output_info } => {
717            assert_eq!(input_info, output_info);
718        });
719
720        // Test sending OnSignalReport
721        let input_ind = fidl_internal::SignalReportIndication {
722            rssi_dbm: Some(-40),
723            snr_db: Some(30),
724            tx_rate_500kbps: Some(0),
725            ..Default::default()
726        };
727        sme_proxy
728            .unbounded_send(ConnectTransactionEvent::OnSignalReport { ind: input_ind.clone() })
729            .expect("expect sending ConnectTransactionEvent to succeed");
730        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
731        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
732        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnSignalReport { ind } => {
733            assert_eq!(input_ind, ind);
734        });
735
736        // Test sending OnChannelSwitched
737        let input_info = fidl_internal::ChannelSwitchInfo {
738            new_primary_channel: fidl_ieee80211::ChannelNumber {
739                band: fidl_ieee80211::WlanBand::TwoGhz,
740                number: 8,
741            },
742            bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
743            vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
744                band: fidl_ieee80211::WlanBand::TwoGhz,
745                number: 0,
746            },
747        };
748        sme_proxy
749            .unbounded_send(ConnectTransactionEvent::OnChannelSwitched { info: input_info.clone() })
750            .expect("expect sending ConnectTransactionEvent to succeed");
751        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Pending);
752        let event = assert_matches!(poll_stream_fut(&mut exec, &mut fidl_client_fut), Poll::Ready(Some(Ok(event))) => event);
753        assert_matches!(event, fidl_sme::ConnectTransactionEvent::OnChannelSwitched { info } => {
754            assert_eq!(input_info, info);
755        });
756
757        // When SME proxy is dropped, the fut should terminate
758        std::mem::drop(sme_proxy);
759        assert_matches!(exec.run_until_stalled(&mut test_fut), Poll::Ready(Ok(())));
760    }
761
762    fn poll_stream_fut<S: Stream + std::marker::Unpin>(
763        exec: &mut fasync::TestExecutor,
764        stream_fut: &mut StreamFuture<S>,
765    ) -> Poll<Option<S::Item>> {
766        exec.run_until_stalled(stream_fut).map(|(item, stream)| {
767            *stream_fut = stream.into_future();
768            item
769        })
770    }
771
772    // Create roughly over 2k bytes ScanResult
773    fn random_scan_result(rng: &mut ThreadRng) -> wlan_common::scan::ScanResult {
774        use wlan_common::security::SecurityDescriptor;
775
776        // TODO(https://fxbug.dev/42164451): Merge this with a similar function in wlancfg.
777        wlan_common::scan::ScanResult {
778            compatibility: match rng.random_range(0..4) {
779                0 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::OPEN]),
780                1 => wlan_common::scan::Compatible::expect_ok([SecurityDescriptor::WPA2_PERSONAL]),
781                2 => wlan_common::scan::Compatible::expect_ok([
782                    SecurityDescriptor::WPA2_PERSONAL,
783                    SecurityDescriptor::WPA3_PERSONAL,
784                ]),
785                _ => Incompatible::unknown(),
786            },
787            timestamp: zx::MonotonicInstant::from_nanos(rng.random()),
788            bss_description: random_bss_description!(),
789        }
790    }
791
792    #[test]
793    fn test_handle_fidl_request_apf() {
794        let mut exec = fasync::TestExecutor::new();
795        let inspector = fuchsia_inspect::Inspector::default();
796        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
797            client_sme::ClientConfig::default(),
798            test_utils::fake_device_info([0; 6].into()),
799            inspector.clone(),
800            inspector.root().create_child("sme"),
801            wlan_common::test_utils::fake_features::fake_security_support(),
802            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
803            Arc::new(()),
804        );
805        let sme = Mutex::new(sme);
806
807        // Test InstallApfPacketFilter
808        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
809        let program = vec![1, 2, 3];
810        let mut install_fut = proxy.install_apf_packet_filter(&program);
811        let mut stream = pin!(stream);
812
813        // Run handle_fidl_request for the install request
814        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
815            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
816            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
817
818            // Forward from SME to MLME
819            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::InstallApfPacketFilter(req, responder))) => {
820                assert_eq!(req.program, program);
821                responder.respond(Ok(()));
822            });
823
824            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
825        });
826        assert_matches!(exec.run_until_stalled(&mut install_fut), Poll::Ready(Ok(Ok(()))));
827
828        // Test ReadApfPacketFilterData
829        let mut read_fut = proxy.read_apf_packet_filter_data();
830        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
831            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
832            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
833
834            // Forward from SME to MLME
835            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::ReadApfPacketFilterData(responder))) => {
836                responder.respond(Ok(fidl_mlme::MlmeReadApfPacketFilterDataResponse {
837                    memory: vec![4, 5, 6],
838                }));
839            });
840
841            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
842        });
843        assert_matches!(exec.run_until_stalled(&mut read_fut), Poll::Ready(Ok(Ok(data))) => {
844            assert_eq!(data, vec![4, 5, 6]);
845        });
846
847        // Test SetApfPacketFilterEnabled
848        let mut set_enabled_fut = proxy.set_apf_packet_filter_enabled(true);
849        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
850            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
851            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
852
853            // Forward from SME to MLME
854            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetApfPacketFilterEnabled(req, responder))) => {
855                assert!(req.enabled);
856                responder.respond(Ok(()));
857            });
858
859            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
860        });
861        assert_matches!(exec.run_until_stalled(&mut set_enabled_fut), Poll::Ready(Ok(Ok(()))));
862
863        // Test GetApfPacketFilterEnabled
864        let mut get_enabled_fut = proxy.get_apf_packet_filter_enabled();
865        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
866            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
867            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
868
869            // Forward from SME to MLME
870            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::GetApfPacketFilterEnabled(responder))) => {
871                responder.respond(Ok(fidl_mlme::MlmeGetApfPacketFilterEnabledResponse {
872                    enabled: true,
873                }));
874            });
875
876            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
877        });
878        assert_matches!(exec.run_until_stalled(&mut get_enabled_fut), Poll::Ready(Ok(Ok(true))));
879    }
880
881    #[test_case(Ok(()); "ok")]
882    #[test_case(Err(123); "err")]
883    fn test_handle_fidl_request_set_mac_address(set_mac_address_result: Result<(), i32>) {
884        let mut exec = fasync::TestExecutor::new();
885        let inspector = fuchsia_inspect::Inspector::default();
886        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
887            client_sme::ClientConfig::default(),
888            test_utils::fake_device_info([0; 6].into()),
889            inspector.clone(),
890            inspector.root().create_child("sme"),
891            wlan_common::test_utils::fake_features::fake_security_support(),
892            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
893            Arc::new(()),
894        );
895        let sme = Mutex::new(sme);
896
897        // Send SetMacAddress request
898        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
899        let new_mac = [1, 2, 3, 4, 5, 6];
900        let mut set_mac_fut = proxy.set_mac_address(&new_mac);
901        let mut stream = pin!(stream);
902
903        // Handle SetMacAddress request
904        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
905            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
906            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
907
908            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(mac, responder))) => {
909                assert_eq!(mac, new_mac);
910                responder.respond(set_mac_address_result);
911            });
912
913            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
914        });
915
916        if set_mac_address_result.is_ok() {
917            assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Ok(()))));
918            // Verify the device_info in client_sme is updated
919            assert_eq!(sme.lock().device_info().sta_addr, new_mac);
920        } else {
921            assert_matches!(exec.run_until_stalled(&mut set_mac_fut), Poll::Ready(Ok(Err(123))));
922            assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
923        }
924    }
925
926    #[test]
927    fn test_handle_fidl_request_set_mac_address_cancelled() {
928        let mut exec = fasync::TestExecutor::new();
929        let inspector = fuchsia_inspect::Inspector::default();
930        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
931            client_sme::ClientConfig::default(),
932            test_utils::fake_device_info([0; 6].into()),
933            inspector.clone(),
934            inspector.root().create_child("sme"),
935            wlan_common::test_utils::fake_features::fake_security_support(),
936            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
937            Arc::new(()),
938        );
939        let sme = Mutex::new(sme);
940
941        // Send SetMacAddress request
942        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
943        let new_mac = [1, 2, 3, 4, 5, 6];
944        let mut set_mac_fut = proxy.set_mac_address(&new_mac);
945        let mut stream = pin!(stream);
946
947        // Handle SetMacAddress request
948        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
949            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
950            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
951
952            assert_matches!(exec.run_until_stalled(&mut mlme_stream.next()), Poll::Ready(Some(crate::MlmeRequest::SetMacAddress(_mac, _responder))) => {
953                // Drop the responder to simulate an error
954            });
955
956            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
957        });
958
959        assert_matches!(
960            exec.run_until_stalled(&mut set_mac_fut),
961            Poll::Ready(Ok(Err(zx::sys::ZX_ERR_CANCELED)))
962        );
963        assert_eq!(sme.lock().device_info().sta_addr, [0; 6]);
964    }
965
966    #[test]
967    fn test_handle_fidl_request_start_and_stop_rssi_monitor() {
968        let mut exec = fasync::TestExecutor::new();
969        let inspector = fuchsia_inspect::Inspector::default();
970        let (sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
971            client_sme::ClientConfig::default(),
972            test_utils::fake_device_info([0; 6].into()),
973            inspector.clone(),
974            inspector.root().create_child("sme"),
975            wlan_common::test_utils::fake_features::fake_security_support(),
976            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
977            Arc::new(()),
978        );
979        let sme = Mutex::new(sme);
980
981        let (proxy, stream) = create_proxy_and_stream::<fidl_sme::ClientSmeMarker>();
982        let mut stream = pin!(stream);
983
984        let mut start_fut = proxy.start_rssi_monitor(-80, -50);
985        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
986            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
987            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
988
989            let (req, responder) = assert_matches!(
990                exec.run_until_stalled(&mut mlme_stream.next()),
991                Poll::Ready(Some(crate::MlmeRequest::StartRssiMonitor(req, responder))) => (req, responder)
992            );
993            assert_eq!(req.min_rssi_dbm, -80);
994            assert_eq!(req.max_rssi_dbm, -50);
995            responder.respond(Ok(()));
996
997            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
998        });
999        assert_matches!(exec.run_until_stalled(&mut start_fut), Poll::Ready(Ok(Ok(()))));
1000
1001        let mut stop_fut = proxy.stop_rssi_monitor();
1002        assert_matches!(exec.run_until_stalled(&mut stream.next()), Poll::Ready(Some(Ok(req))) => {
1003            let mut handle_fut = pin!(handle_fidl_request(&sme, req));
1004            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Pending);
1005
1006            let responder = assert_matches!(
1007                exec.run_until_stalled(&mut mlme_stream.next()),
1008                Poll::Ready(Some(crate::MlmeRequest::StopRssiMonitor(responder))) => responder
1009            );
1010            responder.respond(Ok(()));
1011
1012            assert_matches!(exec.run_until_stalled(&mut handle_fut), Poll::Ready(Ok(())));
1013        });
1014        assert_matches!(exec.run_until_stalled(&mut stop_fut), Poll::Ready(Ok(Ok(()))));
1015    }
1016
1017    #[test]
1018    fn test_serve_connect_txn_stream_on_rssi_threshold_breached() {
1019        let mut exec = fasync::TestExecutor::new();
1020        let (txn_proxy, txn_stream) =
1021            create_proxy_and_stream::<fidl_sme::ConnectTransactionMarker>();
1022        let handle = txn_stream.control_handle();
1023        let (mut sink, stream) = client_sme::ConnectTransactionSink::new_unbounded();
1024        let mut serve_fut = pin!(serve_connect_txn_stream(Some(handle), stream));
1025        let mut event_stream = txn_proxy.take_event_stream();
1026
1027        sink.send(ConnectTransactionEvent::OnRssiThresholdBreached { cur_rssi_dbm: -75 });
1028        assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1029        assert_matches!(
1030            exec.run_until_stalled(&mut event_stream.next()),
1031            Poll::Ready(Some(Ok(fidl_sme::ConnectTransactionEvent::OnRssiThresholdBreached {
1032                cur_rssi_dbm: -75
1033            })))
1034        );
1035    }
1036
1037    #[test]
1038    fn test_handle_telemetry_fidl_request_get_signal_report_updates_status() {
1039        use crate::Station;
1040        use wlan_common::fake_fidl_bss_description;
1041
1042        let mut exec = fasync::TestExecutor::new();
1043        let inspector = fuchsia_inspect::Inspector::default();
1044        let (mut sme, _mlme_sink, mut mlme_stream, _time_stream) = client_sme::ClientSme::new(
1045            client_sme::ClientConfig::default(),
1046            test_utils::fake_device_info([0; 6].into()),
1047            inspector.clone(),
1048            inspector.root().create_child("sme"),
1049            wlan_common::test_utils::fake_features::fake_security_support(),
1050            wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty(),
1051            Arc::new(()),
1052        );
1053
1054        let ssid = ieee80211::Ssid::try_from("foo").unwrap();
1055        let bssid = [1, 2, 3, 4, 5, 6];
1056        let _txn = sme.on_connect_command(fidl_sme::ConnectRequest {
1057            ssid: ssid.to_vec(),
1058            bss_description: fake_fidl_bss_description!(
1059                Open,
1060                ssid: ssid,
1061                bssid: bssid,
1062                rssi_dbm: -60,
1063                snr_db: 20,
1064            ),
1065            multiple_bss_candidates: false,
1066            authentication: fidl_internal::Authentication {
1067                protocol: fidl_internal::Protocol::Open,
1068                credentials: None,
1069            },
1070            deprecated_scan_type: fidl_common::ScanType::Passive,
1071        });
1072        assert_matches!(
1073            exec.run_until_stalled(&mut mlme_stream.next()),
1074            Poll::Ready(Some(crate::MlmeRequest::Connect(_)))
1075        );
1076        sme.on_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
1077            resp: fidl_mlme::ConnectConfirm {
1078                peer_sta_address: bssid,
1079                result_code: fidl_ieee80211::StatusCode::Success,
1080                association_id: 1,
1081                association_ies: vec![],
1082            },
1083        });
1084
1085        let sme = Mutex::new(sme);
1086        let (telemetry_proxy, telemetry_server) =
1087            fidl::endpoints::create_endpoints::<fidl_sme::TelemetryMarker>();
1088        let telemetry_proxy = telemetry_proxy.into_proxy();
1089        let mut serve_fut = pin!(super::super::serve_fidl_endpoint(
1090            &sme,
1091            telemetry_server,
1092            handle_telemetry_fidl_request,
1093        ));
1094
1095        let mut get_signal_fut = telemetry_proxy.get_signal_report();
1096        assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1097        let responder = assert_matches!(
1098            exec.run_until_stalled(&mut mlme_stream.next()),
1099            Poll::Ready(Some(crate::MlmeRequest::GetSignalReport(responder))) => responder
1100        );
1101        let expected_report = fidl_stats::SignalReport {
1102            connection_signal_report: Some(fidl_stats::ConnectionSignalReport {
1103                rssi_dbm: Some(-45),
1104                snr_db: Some(35),
1105                ..Default::default()
1106            }),
1107            ..Default::default()
1108        };
1109        responder.respond(Ok(expected_report.clone()));
1110        assert_matches!(exec.run_until_stalled(&mut serve_fut), Poll::Pending);
1111        assert_matches!(
1112            exec.run_until_stalled(&mut get_signal_fut),
1113            Poll::Ready(Ok(Ok(report))) if report == expected_report
1114        );
1115        assert_matches!(sme.lock().status(), client_sme::ClientSmeStatus::Connected(ap_info) => {
1116            assert_eq!((ap_info.rssi_dbm, ap_info.snr_db), (-45, 35));
1117        });
1118    }
1119}