wlan_fullmac_mlme/
mlme_main_loop.rs

1// Copyright 2024 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::convert::{fullmac_to_mlme, mlme_to_fullmac};
6use crate::device::DeviceOps;
7use crate::wlan_fullmac_impl_ifc_request_handler::serve_wlan_fullmac_impl_ifc_request_handler;
8use crate::{DriverState, FullmacDriverEvent, FullmacDriverEventSink};
9use anyhow::{bail, Context};
10use futures::channel::{mpsc, oneshot};
11use futures::{select, Future, StreamExt};
12use log::{error, info};
13use std::pin::Pin;
14use {
15    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_fullmac as fidl_fullmac,
16    fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme,
17    fuchsia_async as fasync,
18};
19
20/// Creates a future that implements the MLME main loop.
21///
22/// The MLME main loop is responsible for:
23/// - Converting and sending MLME requests from |mlme_request_stream| to the vendor driver via
24///   |device|.
25/// - Receiving requests from the vendor driver through |fullmac_ifc_request_stream|, and
26///   pushing them onto |driver_event_stream|.
27pub(crate) fn create_mlme_main_loop<D: DeviceOps>(
28    device: D,
29    mlme_request_stream: wlan_sme::MlmeStream,
30    mlme_event_sink: wlan_sme::MlmeEventSink,
31    driver_event_stream: mpsc::UnboundedReceiver<FullmacDriverEvent>,
32    driver_event_sink: FullmacDriverEventSink,
33    fullmac_ifc_request_stream: fidl_fullmac::WlanFullmacImplIfcRequestStream,
34) -> Pin<Box<impl Future<Output = anyhow::Result<()>>>> {
35    let main_loop = MlmeMainLoop {
36        device,
37        mlme_request_stream,
38        mlme_event_sink,
39        driver_event_stream,
40        is_bss_protected: false,
41        device_link_state: fidl_mlme::ControlledPortState::Closed,
42    };
43
44    Box::pin(main_loop.serve(fullmac_ifc_request_stream, driver_event_sink))
45}
46
47struct MlmeMainLoop<D: DeviceOps> {
48    device: D,
49    mlme_request_stream: wlan_sme::MlmeStream,
50    mlme_event_sink: wlan_sme::MlmeEventSink,
51    driver_event_stream: mpsc::UnboundedReceiver<FullmacDriverEvent>,
52    is_bss_protected: bool,
53    device_link_state: fidl_mlme::ControlledPortState,
54}
55
56impl<D: DeviceOps> MlmeMainLoop<D> {
57    /// Serves the MLME main loop.
58    ///
59    /// This:
60    /// - Spawns the background task that implements the WlanFullmacImplIfc server.
61    /// - Handles SME -> Vendor Driver requests by servicing |self.mlme_request_stream|.
62    /// - Handles Vendor Driver -> SME requests by servicing |self.driver_event_stream|.
63    ///
64    /// |self.driver_event_stream| is populated by the WlanFullmacImplIfc server task, except for
65    /// the `Stop` event which is sent by `FullmacMlmeHandle::stop`.
66    ///
67    /// Returns success if it receives the `Stop` event on |self.driver_event_stream|, and returns
68    /// an error in all other cases.
69    async fn serve(
70        mut self,
71        fullmac_ifc_request_stream: fidl_fullmac::WlanFullmacImplIfcRequestStream,
72        driver_event_sink: FullmacDriverEventSink,
73    ) -> anyhow::Result<()> {
74        let mac_role = self
75            .device
76            .query_device_info()?
77            .role
78            .context("Vendor driver query response missing MAC role")?;
79
80        // The WlanFullmacImplIfc server is a background task so that a blocking call into the
81        // vendor driver does not prevent MLME from handling incoming WlanFullmacImplIfc requests.
82        let (ifc_server_stop_sender, mut ifc_server_stop_receiver) = oneshot::channel::<()>();
83        let _fullmac_ifc_server_task = fasync::Task::spawn(async move {
84            serve_wlan_fullmac_impl_ifc_request_handler(
85                fullmac_ifc_request_stream,
86                driver_event_sink,
87            )
88            .await;
89            info!("WlanFullmacImplIfc server stopped");
90
91            // This signals that the fullmac_ifc_server_task exited before the main loop. It's fine
92            // if the main loop exits and drops the receiver before this task exits, so we ignore
93            // the result of this send.
94            let _ = ifc_server_stop_sender.send(());
95        });
96
97        loop {
98            select! {
99                mlme_request = self.mlme_request_stream.next() => match mlme_request {
100                    Some(req) => {
101                        if let Err(e) = self.handle_mlme_request(req) {
102                            error!("Failed to handle MLME req: {}", e);
103                        }
104                    },
105                    None => bail!("MLME request stream terminated unexpectedly."),
106                },
107                driver_event = self.driver_event_stream.next() => match driver_event {
108                    Some(event) => {
109                        match self.handle_driver_event(event, &mac_role) {
110                            Ok(DriverState::Running) => {},
111                            Ok(DriverState::Stopping) => return Ok(()),
112                            Err(e) => error!("Failed to handle driver event: {}", e),
113                        }
114                    },
115                    None => bail!("Driver event stream terminated unexpectedly."),
116                },
117                ifc_stop = ifc_server_stop_receiver => match ifc_stop {
118                    Ok(()) => bail!("WlanFullmacImplIfc request stream terminated unexpectedly."),
119                    Err(e) => bail!("WlanFullmacImplIfc server task dropped stop sender unexpectedly. {}", e),
120                },
121            }
122        }
123    }
124
125    fn handle_mlme_request(&mut self, req: wlan_sme::MlmeRequest) -> anyhow::Result<()> {
126        use wlan_sme::MlmeRequest::*;
127        match req {
128            Scan(req) => self.handle_mlme_scan_request(req)?,
129            Connect(req) => {
130                self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
131                self.is_bss_protected = !req.security_ie.is_empty();
132                self.device.connect(mlme_to_fullmac::convert_connect_request(req))?;
133            }
134            Reconnect(req) => {
135                self.device.reconnect(mlme_to_fullmac::convert_reconnect_request(req))?;
136            }
137            Roam(req) => {
138                self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
139                self.device.roam(mlme_to_fullmac::convert_roam_request(req))?;
140            }
141            AuthResponse(resp) => {
142                self.device.auth_resp(mlme_to_fullmac::convert_authenticate_response(resp))?;
143            }
144            Deauthenticate(req) => {
145                self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
146                self.device.deauth(mlme_to_fullmac::convert_deauthenticate_request(req))?;
147            }
148            AssocResponse(resp) => {
149                self.device.assoc_resp(mlme_to_fullmac::convert_associate_response(resp))?;
150            }
151            Disassociate(req) => {
152                self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
153                self.device.disassoc(mlme_to_fullmac::convert_disassociate_request(req))?;
154            }
155            Start(req) => {
156                self.device.start_bss(mlme_to_fullmac::convert_start_bss_request(req)?)?
157            }
158            Stop(req) => self.device.stop_bss(mlme_to_fullmac::convert_stop_bss_request(req)?)?,
159            SetKeys(req) => self.handle_mlme_set_keys_request(req)?,
160            Eapol(req) => self.device.eapol_tx(mlme_to_fullmac::convert_eapol_request(req))?,
161            SetCtrlPort(req) => self.set_link_state(req.state)?,
162            QueryDeviceInfo(responder) => {
163                let device_info =
164                    fullmac_to_mlme::convert_device_info(self.device.query_device_info()?)?;
165                responder.respond(device_info);
166            }
167            QueryMacSublayerSupport(_) => info!("QueryMacSublayerSupport is unsupported"),
168            QuerySecuritySupport(responder) => {
169                responder.respond(self.device.query_security_support()?)
170            }
171            QuerySpectrumManagementSupport(responder) => {
172                responder.respond(self.device.query_spectrum_management_support()?)
173            }
174            QueryTelemetrySupport(responder) => {
175                responder.respond(self.device.query_telemetry_support()?)
176            }
177            GetIfaceStats(responder) => responder.respond(self.device.get_iface_stats()?),
178            GetIfaceHistogramStats(responder) => {
179                responder.respond(self.device.get_iface_histogram_stats()?)
180            }
181            GetMinstrelStats(_, _) => info!("GetMinstrelStats is unsupported"),
182            ListMinstrelPeers(_) => info!("ListMinstrelPeers is unsupported"),
183            SaeHandshakeResp(resp) => self
184                .device
185                .sae_handshake_resp(mlme_to_fullmac::convert_sae_handshake_response(resp))?,
186            SaeFrameTx(frame) => {
187                self.device.sae_frame_tx(mlme_to_fullmac::convert_sae_frame(frame))?
188            }
189            WmmStatusReq => self.device.wmm_status_req()?,
190            FinalizeAssociation(..) => info!("FinalizeAssociation is unsupported"),
191        };
192        Ok(())
193    }
194
195    fn handle_mlme_scan_request(&self, req: fidl_mlme::ScanRequest) -> anyhow::Result<()> {
196        if req.channel_list.is_empty() {
197            let end = fidl_mlme::ScanEnd {
198                txn_id: req.txn_id,
199                code: fidl_mlme::ScanResultCode::InvalidArgs,
200            };
201            self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnScanEnd { end });
202        } else {
203            self.device.start_scan(mlme_to_fullmac::convert_scan_request(req)?)?;
204        }
205        Ok(())
206    }
207
208    fn handle_mlme_set_keys_request(&self, req: fidl_mlme::SetKeysRequest) -> anyhow::Result<()> {
209        let fullmac_req = mlme_to_fullmac::convert_set_keys_request(&req)?;
210        let fullmac_resp = self.device.set_keys(fullmac_req)?;
211        let mlme_resp = fullmac_to_mlme::convert_set_keys_resp(fullmac_resp, &req)?;
212        self.mlme_event_sink.send(fidl_mlme::MlmeEvent::SetKeysConf { conf: mlme_resp });
213        Ok(())
214    }
215
216    fn handle_driver_event(
217        &mut self,
218        event: FullmacDriverEvent,
219        mac_role: &fidl_common::WlanMacRole,
220    ) -> anyhow::Result<DriverState> {
221        match event {
222            FullmacDriverEvent::Stop => return Ok(DriverState::Stopping),
223            FullmacDriverEvent::OnScanResult { result } => {
224                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnScanResult { result });
225            }
226            FullmacDriverEvent::OnScanEnd { end } => {
227                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnScanEnd { end });
228            }
229            FullmacDriverEvent::ConnectConf { resp } => {
230                // IEEE Std 802.11-2016, 9.4.2.57
231                // If BSS is protected, we do not open the controlled port yet until
232                // RSN association is established and the keys are installed, at which
233                // point we would receive a request to open the controlled port from SME.
234                if !self.is_bss_protected && resp.result_code == fidl_ieee80211::StatusCode::Success
235                {
236                    self.set_link_state(fidl_mlme::ControlledPortState::Open)?;
237                }
238                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::ConnectConf { resp });
239            }
240            FullmacDriverEvent::RoamConf { conf } => {
241                if *mac_role == fidl_common::WlanMacRole::Client {
242                    // Like for connect, SME will open the controlled port for a protected BSS.
243                    if !self.is_bss_protected
244                        && conf.status_code == fidl_ieee80211::StatusCode::Success
245                    {
246                        self.set_link_state(fidl_mlme::ControlledPortState::Open)?;
247                    }
248                }
249                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::RoamConf { conf });
250            }
251            FullmacDriverEvent::RoamStartInd { ind } => {
252                if *mac_role == fidl_common::WlanMacRole::Client {
253                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
254                }
255                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::RoamStartInd { ind });
256            }
257            FullmacDriverEvent::RoamResultInd { ind } => {
258                if *mac_role == fidl_common::WlanMacRole::Client {
259                    // Like for connect, SME will open the controlled port for a protected BSS.
260                    if !self.is_bss_protected
261                        && ind.status_code == fidl_ieee80211::StatusCode::Success
262                    {
263                        self.set_link_state(fidl_mlme::ControlledPortState::Open)?;
264                    }
265                }
266                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::RoamResultInd { ind });
267            }
268            FullmacDriverEvent::AuthInd { ind } => {
269                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::AuthenticateInd { ind });
270            }
271            FullmacDriverEvent::DeauthConf { resp } => {
272                if *mac_role == fidl_common::WlanMacRole::Client {
273                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
274                }
275                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::DeauthenticateConf { resp });
276            }
277            FullmacDriverEvent::DeauthInd { ind } => {
278                if *mac_role == fidl_common::WlanMacRole::Client {
279                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
280                }
281                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::DeauthenticateInd { ind });
282            }
283            FullmacDriverEvent::AssocInd { ind } => {
284                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::AssociateInd { ind });
285            }
286            FullmacDriverEvent::DisassocConf { resp } => {
287                if *mac_role == fidl_common::WlanMacRole::Client {
288                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
289                }
290                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::DisassociateConf { resp });
291            }
292            FullmacDriverEvent::DisassocInd { ind } => {
293                if *mac_role == fidl_common::WlanMacRole::Client {
294                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
295                }
296                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::DisassociateInd { ind });
297            }
298            FullmacDriverEvent::StartConf { resp } => {
299                if resp.result_code == fidl_mlme::StartResultCode::Success {
300                    self.set_link_state(fidl_mlme::ControlledPortState::Open)?;
301                }
302                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::StartConf { resp });
303            }
304            FullmacDriverEvent::StopConf { resp } => {
305                if resp.result_code == fidl_mlme::StopResultCode::Success {
306                    self.set_link_state(fidl_mlme::ControlledPortState::Closed)?;
307                }
308                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::StopConf { resp });
309            }
310            FullmacDriverEvent::EapolConf { resp } => {
311                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::EapolConf { resp });
312            }
313            FullmacDriverEvent::OnChannelSwitch { resp } => {
314                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnChannelSwitched { info: resp });
315            }
316            FullmacDriverEvent::SignalReport { ind } => {
317                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::SignalReport { ind });
318            }
319            FullmacDriverEvent::EapolInd { ind } => {
320                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::EapolInd { ind });
321            }
322            FullmacDriverEvent::OnPmkAvailable { info } => {
323                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnPmkAvailable { info });
324            }
325            FullmacDriverEvent::SaeHandshakeInd { ind } => {
326                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnSaeHandshakeInd { ind });
327            }
328            FullmacDriverEvent::SaeFrameRx { frame } => {
329                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnSaeFrameRx { frame });
330            }
331            FullmacDriverEvent::OnWmmStatusResp { status, resp } => {
332                self.mlme_event_sink.send(fidl_mlme::MlmeEvent::OnWmmStatusResp { status, resp });
333            }
334        }
335        Ok(DriverState::Running)
336    }
337
338    fn set_link_state(
339        &mut self,
340        new_link_state: fidl_mlme::ControlledPortState,
341    ) -> anyhow::Result<()> {
342        // TODO(https://fxbug.dev/42128153): Let SME handle these changes.
343        if new_link_state == self.device_link_state {
344            return Ok(());
345        }
346
347        let req = fidl_fullmac::WlanFullmacImplOnLinkStateChangedRequest {
348            online: Some(new_link_state == fidl_mlme::ControlledPortState::Open),
349            ..Default::default()
350        };
351
352        self.device.on_link_state_changed(req)?;
353        self.device_link_state = new_link_state;
354        Ok(())
355    }
356}
357
358#[cfg(test)]
359mod handle_mlme_request_tests {
360    use super::*;
361    use crate::device::test_utils::{DriverCall, FakeFullmacDevice, FakeFullmacDeviceMocks};
362    use std::sync::{Arc, Mutex};
363    use test_case::test_case;
364    use wlan_common::assert_variant;
365    use wlan_common::sink::UnboundedSink;
366    use {fidl_fuchsia_wlan_fullmac as fidl_fullmac, fidl_fuchsia_wlan_stats as fidl_stats};
367
368    #[test]
369    fn test_scan_request() {
370        let mut h = TestHelper::set_up();
371        let fidl_req = wlan_sme::MlmeRequest::Scan(fidl_mlme::ScanRequest {
372            txn_id: 1,
373            scan_type: fidl_mlme::ScanTypes::Passive,
374            channel_list: vec![2],
375            ssid_list: vec![vec![3u8; 4]],
376            probe_delay: 5,
377            min_channel_time: 6,
378            max_channel_time: 7,
379        });
380
381        h.mlme.handle_mlme_request(fidl_req).unwrap();
382
383        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::StartScan { req })) => req);
384        assert_eq!(driver_req.txn_id, Some(1));
385        assert_eq!(driver_req.scan_type, Some(fidl_fullmac::WlanScanType::Passive));
386        assert_eq!(driver_req.channels, Some(vec![2]));
387        assert_eq!(driver_req.min_channel_time, Some(6));
388        assert_eq!(driver_req.max_channel_time, Some(7));
389        assert_eq!(driver_req.ssids, Some(vec![vec![3u8; 4]]));
390    }
391
392    #[test]
393    fn test_scan_request_empty_ssid_list() {
394        let mut h = TestHelper::set_up();
395        let fidl_req = wlan_sme::MlmeRequest::Scan(fidl_mlme::ScanRequest {
396            txn_id: 1,
397            scan_type: fidl_mlme::ScanTypes::Active,
398            channel_list: vec![2],
399            ssid_list: vec![],
400            probe_delay: 5,
401            min_channel_time: 6,
402            max_channel_time: 7,
403        });
404
405        h.mlme.handle_mlme_request(fidl_req).unwrap();
406
407        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::StartScan { req })) => req);
408        assert_eq!(driver_req.scan_type, Some(fidl_fullmac::WlanScanType::Active));
409        assert!(driver_req.ssids.as_ref().unwrap().is_empty());
410    }
411
412    #[test]
413    fn test_scan_request_empty_channel_list() {
414        let mut h = TestHelper::set_up();
415        let fidl_req = wlan_sme::MlmeRequest::Scan(fidl_mlme::ScanRequest {
416            txn_id: 1,
417            scan_type: fidl_mlme::ScanTypes::Passive,
418            channel_list: vec![],
419            ssid_list: vec![vec![3u8; 4]],
420            probe_delay: 5,
421            min_channel_time: 6,
422            max_channel_time: 7,
423        });
424
425        h.mlme.handle_mlme_request(fidl_req).unwrap();
426
427        assert_variant!(h.driver_calls.try_next(), Err(_));
428        let scan_end = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(fidl_mlme::MlmeEvent::OnScanEnd { end })) => end);
429        assert_eq!(
430            scan_end,
431            fidl_mlme::ScanEnd { txn_id: 1, code: fidl_mlme::ScanResultCode::InvalidArgs }
432        );
433    }
434
435    #[test]
436    fn test_connect_request() {
437        let mut h = TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
438        let fidl_req = wlan_sme::MlmeRequest::Connect(fidl_mlme::ConnectRequest {
439            selected_bss: fidl_common::BssDescription {
440                bssid: [100u8; 6],
441                bss_type: fidl_common::BssType::Infrastructure,
442                beacon_period: 101,
443                capability_info: 102,
444                ies: vec![103u8, 104, 105],
445                channel: fidl_common::WlanChannel {
446                    primary: 106,
447                    cbw: fidl_common::ChannelBandwidth::Cbw40,
448                    secondary80: 0,
449                },
450                rssi_dbm: 107,
451                snr_db: 108,
452            },
453            connect_failure_timeout: 1u32,
454            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
455            sae_password: vec![2u8, 3, 4],
456            wep_key: Some(Box::new(fidl_mlme::SetKeyDescriptor {
457                key: vec![5u8, 6],
458                key_id: 7,
459                key_type: fidl_mlme::KeyType::Group,
460                address: [8u8; 6],
461                rsc: 9,
462                cipher_suite_oui: [10u8; 3],
463                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
464                    11,
465                ),
466            })),
467            security_ie: vec![12u8, 13],
468        });
469
470        h.mlme.handle_mlme_request(fidl_req).unwrap();
471
472        assert!(h.mlme.is_bss_protected);
473
474        assert_variant!(
475            h.driver_calls.try_next(),
476            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
477              assert_eq!(req.online, Some(false));
478          }
479        );
480        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::ConnectReq { req })) => {
481            let selected_bss = req.selected_bss.clone().unwrap();
482            assert_eq!(selected_bss.bssid, [100u8; 6]);
483            assert_eq!(selected_bss.bss_type, fidl_common::BssType::Infrastructure);
484            assert_eq!(selected_bss.beacon_period, 101);
485            assert_eq!(selected_bss.capability_info, 102);
486            assert_eq!(selected_bss.ies, vec![103u8, 104, 105]);
487            assert_eq!(
488                selected_bss.channel,
489                fidl_common::WlanChannel {
490                    primary: 106,
491                    cbw: fidl_common::ChannelBandwidth::Cbw40,
492                    secondary80: 0,
493                }
494            );
495            assert_eq!(selected_bss.rssi_dbm, 107);
496            assert_eq!(selected_bss.snr_db, 108);
497
498            assert_eq!(req.connect_failure_timeout, Some(1u32));
499            assert_eq!(req.auth_type, Some(fidl_fullmac::WlanAuthType::OpenSystem));
500            assert_eq!(req.sae_password, Some(vec![2u8, 3, 4]));
501
502            let wep_key_desc = req.wep_key_desc.clone().unwrap();
503            assert_eq!(wep_key_desc.key, Some(vec![5u8, 6]));
504            assert_eq!(wep_key_desc.key_id, Some(7));
505            assert_eq!(wep_key_desc.key_type, Some(fidl_ieee80211::KeyType::Group));
506            assert_eq!(wep_key_desc.peer_addr, Some([8u8; 6]));
507            assert_eq!(wep_key_desc.rsc, Some(9));
508            assert_eq!(wep_key_desc.cipher_oui, Some([10u8; 3]));
509            assert_eq!(wep_key_desc.cipher_type, fidl_ieee80211::CipherSuiteType::from_primitive(11));
510
511            assert_eq!(req.security_ie, Some(vec![12u8, 13]));
512        });
513    }
514
515    #[test]
516    fn test_reconnect_request() {
517        let mut h = TestHelper::set_up();
518        let fidl_req = wlan_sme::MlmeRequest::Reconnect(fidl_mlme::ReconnectRequest {
519            peer_sta_address: [1u8; 6],
520        });
521
522        h.mlme.handle_mlme_request(fidl_req).unwrap();
523
524        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::ReconnectReq { req })) => req);
525        assert_eq!(
526            driver_req,
527            fidl_fullmac::WlanFullmacImplReconnectRequest {
528                peer_sta_address: Some([1u8; 6]),
529                ..Default::default()
530            }
531        );
532    }
533
534    #[test]
535    fn test_authenticate_response() {
536        let mut h = TestHelper::set_up();
537        let fidl_req = wlan_sme::MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
538            peer_sta_address: [1u8; 6],
539            result_code: fidl_mlme::AuthenticateResultCode::Success,
540        });
541
542        h.mlme.handle_mlme_request(fidl_req).unwrap();
543
544        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::AuthResp { resp })) => resp);
545        assert_eq!(
546            driver_req,
547            fidl_fullmac::WlanFullmacImplAuthRespRequest {
548                peer_sta_address: Some([1u8; 6]),
549                result_code: Some(fidl_fullmac::WlanAuthResult::Success),
550                ..Default::default()
551            }
552        );
553    }
554
555    #[test]
556    fn test_deauthenticate_request() {
557        let mut h = TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
558        let fidl_req = wlan_sme::MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
559            peer_sta_address: [1u8; 6],
560            reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
561        });
562
563        h.mlme.handle_mlme_request(fidl_req).unwrap();
564
565        assert_variant!(
566            h.driver_calls.try_next(),
567            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
568              assert_eq!(req.online, Some(false));
569          }
570        );
571        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::DeauthReq { req })) => req);
572        assert_eq!(driver_req.peer_sta_address, Some([1u8; 6]));
573        assert_eq!(driver_req.reason_code, Some(fidl_ieee80211::ReasonCode::LeavingNetworkDeauth));
574    }
575
576    #[test]
577    fn test_associate_response() {
578        let mut h = TestHelper::set_up();
579        let fidl_req = wlan_sme::MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
580            peer_sta_address: [1u8; 6],
581            result_code: fidl_mlme::AssociateResultCode::Success,
582            association_id: 2,
583            capability_info: 3,
584            rates: vec![4, 5],
585        });
586
587        h.mlme.handle_mlme_request(fidl_req).unwrap();
588
589        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::AssocResp { resp })) => resp);
590        assert_eq!(driver_req.peer_sta_address, Some([1u8; 6]));
591        assert_eq!(driver_req.result_code, Some(fidl_fullmac::WlanAssocResult::Success));
592        assert_eq!(driver_req.association_id, Some(2));
593    }
594
595    #[test]
596    fn test_disassociate_request() {
597        let mut h = TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
598        let fidl_req = wlan_sme::MlmeRequest::Disassociate(fidl_mlme::DisassociateRequest {
599            peer_sta_address: [1u8; 6],
600            reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
601        });
602
603        h.mlme.handle_mlme_request(fidl_req).unwrap();
604
605        assert_variant!(
606            h.driver_calls.try_next(),
607            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
608              assert_eq!(req.online, Some(false));
609          }
610        );
611
612        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::Disassoc{ req })) => req);
613        assert_eq!(driver_req.peer_sta_address, Some([1u8; 6]));
614        assert_eq!(
615            driver_req.reason_code,
616            Some(fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc)
617        );
618    }
619
620    #[test]
621    fn test_start_request() {
622        let mut h = TestHelper::set_up();
623        const SSID_LEN: usize = 2;
624        const RSNE_LEN: usize = 15;
625        let fidl_req = wlan_sme::MlmeRequest::Start(fidl_mlme::StartRequest {
626            ssid: vec![1u8; SSID_LEN],
627            bss_type: fidl_common::BssType::Infrastructure,
628            beacon_period: 3,
629            dtim_period: 4,
630            channel: 5,
631            capability_info: 6,
632            rates: vec![7, 8, 9],
633            country: fidl_mlme::Country { alpha2: [10, 11], suffix: 12 },
634            mesh_id: vec![13],
635            rsne: Some(vec![14; RSNE_LEN]),
636            phy: fidl_common::WlanPhyType::Vht,
637            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw80,
638        });
639
640        h.mlme.handle_mlme_request(fidl_req).unwrap();
641
642        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::StartBss { req })) => req);
643
644        assert_eq!(driver_req.ssid, Some(vec![1u8; SSID_LEN]));
645        assert_eq!(driver_req.bss_type, Some(fidl_common::BssType::Infrastructure));
646        assert_eq!(driver_req.beacon_period, Some(3));
647        assert_eq!(driver_req.dtim_period, Some(4));
648        assert_eq!(driver_req.channel, Some(5));
649        assert_ne!(driver_req.rsne, Some(vec![14 as u8, RSNE_LEN as u8]));
650        assert_eq!(driver_req.vendor_ie, Some(vec![]));
651    }
652
653    #[test]
654    fn test_stop_request() {
655        let mut h = TestHelper::set_up();
656        const SSID_LEN: usize = 2;
657        let fidl_req =
658            wlan_sme::MlmeRequest::Stop(fidl_mlme::StopRequest { ssid: vec![1u8; SSID_LEN] });
659
660        h.mlme.handle_mlme_request(fidl_req).unwrap();
661
662        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::StopBss { req })) => req);
663        assert_eq!(driver_req.ssid, Some(vec![1u8; SSID_LEN]));
664    }
665
666    #[test]
667    fn test_set_keys_request() {
668        let mut h = TestHelper::set_up();
669        let fidl_req = wlan_sme::MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest {
670            keylist: vec![fidl_mlme::SetKeyDescriptor {
671                key: vec![5u8, 6],
672                key_id: 7,
673                key_type: fidl_mlme::KeyType::Group,
674                address: [8u8; 6],
675                rsc: 9,
676                cipher_suite_oui: [10u8; 3],
677                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
678                    11,
679                ),
680            }],
681        });
682
683        h.mlme.handle_mlme_request(fidl_req).unwrap();
684
685        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::SetKeys { req })) => req);
686        assert_eq!(driver_req.key_descriptors.as_ref().unwrap().len(), 1 as usize);
687        let key_descriptors = driver_req.key_descriptors.as_ref().unwrap();
688        assert_eq!(key_descriptors[0].key_id, Some(7));
689        assert_eq!(key_descriptors[0].key_type, Some(fidl_ieee80211::KeyType::Group));
690        assert_eq!(key_descriptors[0].peer_addr, Some([8u8; 6]));
691        assert_eq!(key_descriptors[0].rsc, Some(9));
692        assert_eq!(key_descriptors[0].cipher_oui, Some([10u8; 3]));
693        assert_eq!(
694            key_descriptors[0].cipher_type,
695            Some(fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(11))
696        );
697
698        let conf = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(fidl_mlme::MlmeEvent::SetKeysConf { conf })) => conf);
699        assert_eq!(
700            conf,
701            fidl_mlme::SetKeysConfirm {
702                results: vec![fidl_mlme::SetKeyResult { key_id: 7, status: 0 }]
703            }
704        );
705    }
706
707    #[test]
708    fn test_set_keys_request_partial_failure() {
709        let mut h = TestHelper::set_up();
710        const NUM_KEYS: usize = 3;
711        h.fake_device.lock().unwrap().set_keys_resp_mock =
712            Some(fidl_fullmac::WlanFullmacSetKeysResp { statuslist: [0i32, 1, 0].to_vec() });
713        let mut keylist = vec![];
714        let key = fidl_mlme::SetKeyDescriptor {
715            key: vec![5u8, 6],
716            key_id: 7,
717            key_type: fidl_mlme::KeyType::Group,
718            address: [8u8; 6],
719            rsc: 9,
720            cipher_suite_oui: [10u8; 3],
721            cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(11),
722        };
723        for i in 0..NUM_KEYS {
724            keylist.push(fidl_mlme::SetKeyDescriptor { key_id: i as u16, ..key.clone() });
725        }
726        let fidl_req = wlan_sme::MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest { keylist });
727
728        h.mlme.handle_mlme_request(fidl_req).unwrap();
729
730        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::SetKeys { req })) => req);
731        assert_eq!(driver_req.key_descriptors.as_ref().unwrap().len(), NUM_KEYS as usize);
732        let key_descriptors = driver_req.key_descriptors.unwrap();
733        for i in 0..NUM_KEYS {
734            assert_eq!(key_descriptors[i].key_id, Some(i as u16));
735        }
736
737        let conf = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(fidl_mlme::MlmeEvent::SetKeysConf { conf })) => conf);
738        assert_eq!(
739            conf,
740            fidl_mlme::SetKeysConfirm {
741                results: vec![
742                    fidl_mlme::SetKeyResult { key_id: 0, status: 0 },
743                    fidl_mlme::SetKeyResult { key_id: 1, status: 1 },
744                    fidl_mlme::SetKeyResult { key_id: 2, status: 0 },
745                ]
746            }
747        );
748    }
749
750    #[test]
751    fn test_set_keys_request_too_many_keys() {
752        let mut h = TestHelper::set_up();
753        let key = fidl_mlme::SetKeyDescriptor {
754            key: vec![5u8, 6],
755            key_id: 7,
756            key_type: fidl_mlme::KeyType::Group,
757            address: [8u8; 6],
758            rsc: 9,
759            cipher_suite_oui: [10u8; 3],
760            cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(11),
761        };
762        let fidl_req = wlan_sme::MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest {
763            keylist: vec![key.clone(); 5],
764        });
765
766        assert!(h.mlme.handle_mlme_request(fidl_req).is_err());
767
768        // No SetKeys and SetKeysResp
769        assert_variant!(h.driver_calls.try_next(), Err(_));
770        assert_variant!(h.mlme_event_receiver.try_next(), Err(_));
771    }
772
773    #[test]
774    fn test_set_keys_request_when_resp_has_different_num_keys() {
775        let mut h = TestHelper::set_up();
776        h.fake_device.lock().unwrap().set_keys_resp_mock =
777            Some(fidl_fullmac::WlanFullmacSetKeysResp { statuslist: [0i32; 2].to_vec() });
778        let fidl_req = wlan_sme::MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest {
779            keylist: vec![fidl_mlme::SetKeyDescriptor {
780                key: vec![5u8, 6],
781                key_id: 7,
782                key_type: fidl_mlme::KeyType::Group,
783                address: [8u8; 6],
784                rsc: 9,
785                cipher_suite_oui: [10u8; 3],
786                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
787                    11,
788                ),
789            }],
790        });
791
792        // An error is expected when converting the response
793        assert!(h.mlme.handle_mlme_request(fidl_req).is_err());
794
795        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::SetKeys { .. })));
796        // No SetKeysConf MLME event because the SetKeysResp from driver has different number of
797        // keys.
798        assert_variant!(h.mlme_event_receiver.try_next(), Err(_));
799    }
800
801    #[test]
802    fn test_eapol_request() {
803        let mut h = TestHelper::set_up();
804        let fidl_req = wlan_sme::MlmeRequest::Eapol(fidl_mlme::EapolRequest {
805            src_addr: [1u8; 6],
806            dst_addr: [2u8; 6],
807            data: vec![3u8; 4],
808        });
809
810        h.mlme.handle_mlme_request(fidl_req).unwrap();
811
812        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::EapolTx { req })) => req);
813        assert_eq!(driver_req.src_addr, Some([1u8; 6]));
814        assert_eq!(driver_req.dst_addr, Some([2u8; 6]));
815        assert_eq!(driver_req.data, Some(vec![3u8; 4]));
816    }
817
818    #[test_case(fidl_mlme::ControlledPortState::Open, true; "online")]
819    #[test_case(fidl_mlme::ControlledPortState::Closed, false; "offline")]
820    #[fuchsia::test(add_test_attr = false)]
821    fn test_set_ctrl_port(
822        controlled_port_state: fidl_mlme::ControlledPortState,
823        expected_link_state: bool,
824    ) {
825        let mut h = match controlled_port_state {
826            fidl_mlme::ControlledPortState::Open => TestHelper::set_up(),
827            fidl_mlme::ControlledPortState::Closed => {
828                TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open)
829            }
830        };
831        let fidl_req = wlan_sme::MlmeRequest::SetCtrlPort(fidl_mlme::SetControlledPortRequest {
832            peer_sta_address: [1u8; 6],
833            state: controlled_port_state,
834        });
835
836        h.mlme.handle_mlme_request(fidl_req).unwrap();
837
838        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
839            assert_eq!(req.online, Some(expected_link_state));
840        });
841    }
842
843    #[test]
844    fn test_query_telemetry_support() {
845        let mut h = TestHelper::set_up();
846        let mocked_support = Ok(fidl_stats::TelemetrySupport {
847            inspect_counter_configs: Some(vec![fidl_stats::InspectCounterConfig {
848                counter_id: Some(1),
849                counter_name: Some("foo_counter".to_string()),
850                ..Default::default()
851            }]),
852            ..Default::default()
853        });
854        h.fake_device.lock().unwrap().query_telemetry_support_mock.replace(mocked_support.clone());
855        let (support_responder, mut support_receiver) = wlan_sme::responder::Responder::new();
856        let fidl_req = wlan_sme::MlmeRequest::QueryTelemetrySupport(support_responder);
857
858        h.mlme.handle_mlme_request(fidl_req).unwrap();
859
860        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::QueryTelemetrySupport)));
861        let support = assert_variant!(support_receiver.try_recv(), Ok(Some(support)) => support);
862        assert_eq!(support, mocked_support);
863    }
864
865    #[test]
866    fn test_get_iface_stats() {
867        let mut h = TestHelper::set_up();
868        let mocked_stats = fidl_stats::IfaceStats {
869            connection_stats: Some(fidl_stats::ConnectionStats {
870                connection_id: Some(1),
871                rx_unicast_drop: Some(11),
872                rx_unicast_total: Some(22),
873                rx_multicast: Some(33),
874                tx_total: Some(44),
875                tx_drop: Some(55),
876                ..Default::default()
877            }),
878            ..Default::default()
879        };
880        h.fake_device
881            .lock()
882            .unwrap()
883            .get_iface_stats_mock
884            .replace(fidl_mlme::GetIfaceStatsResponse::Stats(mocked_stats));
885        let (stats_responder, mut stats_receiver) = wlan_sme::responder::Responder::new();
886        let fidl_req = wlan_sme::MlmeRequest::GetIfaceStats(stats_responder);
887
888        h.mlme.handle_mlme_request(fidl_req).unwrap();
889
890        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::GetIfaceStats)));
891        let stats = assert_variant!(stats_receiver.try_recv(), Ok(Some(stats)) => stats);
892        let stats = assert_variant!(stats, fidl_mlme::GetIfaceStatsResponse::Stats(stats) => stats);
893        assert_eq!(
894            stats,
895            fidl_stats::IfaceStats {
896                connection_stats: Some(fidl_stats::ConnectionStats {
897                    connection_id: Some(1),
898                    rx_unicast_drop: Some(11),
899                    rx_unicast_total: Some(22),
900                    rx_multicast: Some(33),
901                    tx_total: Some(44),
902                    tx_drop: Some(55),
903                    ..Default::default()
904                }),
905                ..Default::default()
906            }
907        );
908    }
909
910    #[test]
911    fn test_get_iface_histogram_stats() {
912        let mut h = TestHelper::set_up();
913
914        let mocked_stats = fidl_stats::IfaceHistogramStats {
915            noise_floor_histograms: Some(vec![fidl_stats::NoiseFloorHistogram {
916                hist_scope: fidl_stats::HistScope::Station,
917                antenna_id: None,
918                noise_floor_samples: vec![fidl_stats::HistBucket {
919                    bucket_index: 2,
920                    num_samples: 3,
921                }],
922                invalid_samples: 4,
923            }]),
924            rssi_histograms: Some(vec![fidl_stats::RssiHistogram {
925                hist_scope: fidl_stats::HistScope::PerAntenna,
926                antenna_id: Some(Box::new(fidl_stats::AntennaId {
927                    freq: fidl_stats::AntennaFreq::Antenna5G,
928                    index: 5,
929                })),
930                rssi_samples: vec![fidl_stats::HistBucket { bucket_index: 6, num_samples: 7 }],
931                invalid_samples: 8,
932            }]),
933            rx_rate_index_histograms: Some(vec![fidl_stats::RxRateIndexHistogram {
934                hist_scope: fidl_stats::HistScope::Station,
935                antenna_id: None,
936                rx_rate_index_samples: vec![fidl_stats::HistBucket {
937                    bucket_index: 10,
938                    num_samples: 11,
939                }],
940                invalid_samples: 12,
941            }]),
942            snr_histograms: Some(vec![fidl_stats::SnrHistogram {
943                hist_scope: fidl_stats::HistScope::PerAntenna,
944                antenna_id: Some(Box::new(fidl_stats::AntennaId {
945                    freq: fidl_stats::AntennaFreq::Antenna2G,
946                    index: 13,
947                })),
948                snr_samples: vec![fidl_stats::HistBucket { bucket_index: 14, num_samples: 15 }],
949                invalid_samples: 16,
950            }]),
951            ..Default::default()
952        };
953
954        h.fake_device
955            .lock()
956            .unwrap()
957            .get_iface_histogram_stats_mock
958            .replace(fidl_mlme::GetIfaceHistogramStatsResponse::Stats(mocked_stats.clone()));
959        let (stats_responder, mut stats_receiver) = wlan_sme::responder::Responder::new();
960        let fidl_req = wlan_sme::MlmeRequest::GetIfaceHistogramStats(stats_responder);
961
962        h.mlme.handle_mlme_request(fidl_req).unwrap();
963
964        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::GetIfaceHistogramStats)));
965        let stats = assert_variant!(stats_receiver.try_recv(), Ok(Some(stats)) => stats);
966        let stats = assert_variant!(stats, fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats) => stats);
967        assert_eq!(stats, mocked_stats);
968    }
969
970    #[test]
971    fn test_sae_handshake_resp() {
972        let mut h = TestHelper::set_up();
973        let fidl_req = wlan_sme::MlmeRequest::SaeHandshakeResp(fidl_mlme::SaeHandshakeResponse {
974            peer_sta_address: [1u8; 6],
975            status_code: fidl_ieee80211::StatusCode::AntiCloggingTokenRequired,
976        });
977
978        h.mlme.handle_mlme_request(fidl_req).unwrap();
979
980        let driver_req = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::SaeHandshakeResp { resp })) => resp);
981        assert_eq!(driver_req.peer_sta_address.unwrap(), [1u8; 6]);
982        assert_eq!(
983            driver_req.status_code.unwrap(),
984            fidl_ieee80211::StatusCode::AntiCloggingTokenRequired
985        );
986    }
987
988    #[test]
989    fn test_convert_sae_frame() {
990        let mut h = TestHelper::set_up();
991        let fidl_req = wlan_sme::MlmeRequest::SaeFrameTx(fidl_mlme::SaeFrame {
992            peer_sta_address: [1u8; 6],
993            status_code: fidl_ieee80211::StatusCode::Success,
994            seq_num: 2,
995            sae_fields: vec![3u8; 4],
996        });
997
998        h.mlme.handle_mlme_request(fidl_req).unwrap();
999
1000        let driver_frame = assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::SaeFrameTx { frame })) => frame);
1001        assert_eq!(driver_frame.peer_sta_address.unwrap(), [1u8; 6]);
1002        assert_eq!(driver_frame.status_code.unwrap(), fidl_ieee80211::StatusCode::Success);
1003        assert_eq!(driver_frame.seq_num.unwrap(), 2);
1004        assert_eq!(driver_frame.sae_fields.unwrap(), vec![3u8; 4]);
1005    }
1006
1007    #[test]
1008    fn test_wmm_status_req() {
1009        let mut h = TestHelper::set_up();
1010        let fidl_req = wlan_sme::MlmeRequest::WmmStatusReq;
1011
1012        h.mlme.handle_mlme_request(fidl_req).unwrap();
1013
1014        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::WmmStatusReq)));
1015    }
1016
1017    pub struct TestHelper {
1018        fake_device: Arc<Mutex<FakeFullmacDeviceMocks>>,
1019        mlme: MlmeMainLoop<FakeFullmacDevice>,
1020        mlme_event_receiver: mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>,
1021        driver_calls: mpsc::UnboundedReceiver<DriverCall>,
1022        _mlme_request_sender: mpsc::UnboundedSender<wlan_sme::MlmeRequest>,
1023        _driver_event_sender: mpsc::UnboundedSender<FullmacDriverEvent>,
1024    }
1025
1026    impl TestHelper {
1027        pub fn set_up_with_link_state(device_link_state: fidl_mlme::ControlledPortState) -> Self {
1028            let (fake_device, driver_calls) = FakeFullmacDevice::new();
1029            let (mlme_request_sender, mlme_request_stream) = mpsc::unbounded();
1030            let (mlme_event_sender, mlme_event_receiver) = mpsc::unbounded();
1031            let mlme_event_sink = UnboundedSink::new(mlme_event_sender);
1032
1033            let (driver_event_sender, driver_event_stream) = mpsc::unbounded();
1034            let mocks = fake_device.mocks.clone();
1035
1036            let mlme = MlmeMainLoop {
1037                device: fake_device,
1038                mlme_request_stream,
1039                mlme_event_sink,
1040                driver_event_stream,
1041                is_bss_protected: false,
1042                device_link_state,
1043            };
1044            Self {
1045                fake_device: mocks,
1046                mlme,
1047                mlme_event_receiver,
1048                driver_calls,
1049                _mlme_request_sender: mlme_request_sender,
1050                _driver_event_sender: driver_event_sender,
1051            }
1052        }
1053
1054        // By default, link state starts off closed
1055        pub fn set_up() -> Self {
1056            Self::set_up_with_link_state(fidl_mlme::ControlledPortState::Closed)
1057        }
1058    }
1059}
1060#[cfg(test)]
1061mod handle_driver_event_tests {
1062    use super::*;
1063    use crate::device::test_utils::{DriverCall, FakeFullmacDevice, FakeFullmacDeviceMocks};
1064    use futures::channel::mpsc;
1065    use futures::task::Poll;
1066    use futures::Future;
1067    use std::pin::Pin;
1068    use std::sync::{Arc, Mutex};
1069    use test_case::test_case;
1070    use wlan_common::sink::UnboundedSink;
1071    use wlan_common::{assert_variant, fake_fidl_bss_description};
1072    use {
1073        fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_internal as fidl_internal,
1074        fidl_fuchsia_wlan_mlme as fidl_mlme, fuchsia_async as fasync,
1075    };
1076
1077    fn create_bss_descriptions() -> fidl_common::BssDescription {
1078        fidl_common::BssDescription {
1079            bssid: [9u8; 6],
1080            bss_type: fidl_common::BssType::Infrastructure,
1081            beacon_period: 1,
1082            capability_info: 2,
1083            ies: vec![3, 4, 5],
1084            channel: fidl_common::WlanChannel {
1085                primary: 6,
1086                cbw: fidl_common::ChannelBandwidth::Cbw20,
1087                secondary80: 0,
1088            },
1089            rssi_dbm: 7,
1090            snr_db: 8,
1091        }
1092    }
1093
1094    fn create_connect_request(security_ie: Vec<u8>) -> wlan_sme::MlmeRequest {
1095        wlan_sme::MlmeRequest::Connect(fidl_mlme::ConnectRequest {
1096            selected_bss: fake_fidl_bss_description!(Wpa2),
1097            connect_failure_timeout: 1u32,
1098            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1099            sae_password: vec![2u8, 3, 4],
1100            wep_key: None,
1101            security_ie,
1102        })
1103    }
1104
1105    #[test]
1106    fn test_on_scan_result() {
1107        let (mut h, mut test_fut) = TestHelper::set_up();
1108        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1109
1110        let bss = create_bss_descriptions();
1111        let scan_result = fidl_fullmac::WlanFullmacImplIfcOnScanResultRequest {
1112            txn_id: Some(42u64),
1113            timestamp_nanos: Some(1337i64),
1114            bss: Some(bss.clone()),
1115            ..Default::default()
1116        };
1117        assert_variant!(
1118            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.on_scan_result(&scan_result)),
1119            Poll::Ready(Ok(())),
1120        );
1121        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1122
1123        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1124        let result =
1125            assert_variant!(event, fidl_mlme::MlmeEvent::OnScanResult { result } => result);
1126        assert_eq!(result, fidl_mlme::ScanResult { txn_id: 42u64, timestamp_nanos: 1337i64, bss });
1127    }
1128
1129    #[test]
1130    fn test_on_scan_end() {
1131        let (mut h, mut test_fut) = TestHelper::set_up();
1132        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1133
1134        let scan_end = fidl_fullmac::WlanFullmacImplIfcOnScanEndRequest {
1135            txn_id: Some(42u64),
1136            code: Some(fidl_fullmac::WlanScanResult::Success),
1137            ..Default::default()
1138        };
1139        assert_variant!(
1140            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.on_scan_end(&scan_end)),
1141            Poll::Ready(Ok(())),
1142        );
1143        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1144
1145        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1146        let end = assert_variant!(event, fidl_mlme::MlmeEvent::OnScanEnd { end } => end);
1147        assert_eq!(
1148            end,
1149            fidl_mlme::ScanEnd { txn_id: 42u64, code: fidl_mlme::ScanResultCode::Success }
1150        );
1151    }
1152
1153    #[test]
1154    fn test_connect_conf() {
1155        let (mut h, mut test_fut) = TestHelper::set_up();
1156        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1157
1158        let connect_conf = fidl_fullmac::WlanFullmacImplIfcConnectConfRequest {
1159            peer_sta_address: Some([1u8; 6]),
1160            result_code: Some(fidl_ieee80211::StatusCode::Success),
1161            association_id: Some(2),
1162            association_ies: Some(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]),
1163            ..Default::default()
1164        };
1165        assert_variant!(
1166            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.connect_conf(&connect_conf)),
1167            Poll::Ready(Ok(())),
1168        );
1169        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1170
1171        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1172        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::ConnectConf { resp } => resp);
1173        assert_eq!(
1174            conf,
1175            fidl_mlme::ConnectConfirm {
1176                peer_sta_address: [1u8; 6],
1177                result_code: fidl_ieee80211::StatusCode::Success,
1178                association_id: 2,
1179                association_ies: vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
1180            }
1181        );
1182    }
1183
1184    #[test_case(true, fidl_ieee80211::StatusCode::Success, false; "secure connect with success status is not online yet")]
1185    #[test_case(false, fidl_ieee80211::StatusCode::Success, true; "insecure connect with success status is online right away")]
1186    #[test_case(true, fidl_ieee80211::StatusCode::RefusedReasonUnspecified, false; "secure connect with failed status is not online")]
1187    #[test_case(false, fidl_ieee80211::StatusCode::RefusedReasonUnspecified, false; "insecure connect with failed status in not online")]
1188    #[fuchsia::test(add_test_attr = false)]
1189    fn test_connect_req_connect_conf_link_state(
1190        secure_connect: bool,
1191        connect_result_code: fidl_ieee80211::StatusCode,
1192        expected_online: bool,
1193    ) {
1194        let (mut h, mut test_fut) =
1195            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1196        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1197
1198        let connect_req =
1199            create_connect_request(if secure_connect { vec![7u8, 8] } else { vec![] });
1200        h.mlme_request_sender
1201            .unbounded_send(connect_req)
1202            .expect("sending ConnectReq should succeed");
1203        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1204
1205        let connect_conf = fidl_fullmac::WlanFullmacImplIfcConnectConfRequest {
1206            peer_sta_address: Some([1u8; 6]),
1207            result_code: Some(connect_result_code),
1208            association_id: Some(2),
1209            association_ies: Some(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]),
1210            ..Default::default()
1211        };
1212
1213        assert_variant!(
1214            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.connect_conf(&connect_conf)),
1215            Poll::Ready(Ok(()))
1216        );
1217        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1218
1219        assert_variant!(
1220            h.driver_calls.try_next(),
1221            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
1222              assert_eq!(req.online, Some(false));
1223          }
1224        );
1225        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::ConnectReq { .. })));
1226        if expected_online {
1227            assert_variant!(
1228                h.driver_calls.try_next(),
1229                Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
1230                  assert_eq!(req.online, Some(true));
1231              }
1232            );
1233        } else {
1234            assert_variant!(h.driver_calls.try_next(), Err(_));
1235        }
1236    }
1237
1238    #[test]
1239    fn test_auth_ind() {
1240        let (mut h, mut test_fut) = TestHelper::set_up();
1241        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1242
1243        let auth_ind = fidl_fullmac::WlanFullmacImplIfcAuthIndRequest {
1244            peer_sta_address: Some([1u8; 6]),
1245            auth_type: Some(fidl_fullmac::WlanAuthType::OpenSystem),
1246            ..Default::default()
1247        };
1248        assert_variant!(
1249            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.auth_ind(&auth_ind)),
1250            Poll::Ready(Ok(()))
1251        );
1252        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1253
1254        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1255        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::AuthenticateInd { ind } => ind);
1256        assert_eq!(
1257            ind,
1258            fidl_mlme::AuthenticateIndication {
1259                peer_sta_address: [1u8; 6],
1260                auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1261            }
1262        );
1263    }
1264
1265    #[test_case(fidl_common::WlanMacRole::Client; "client")]
1266    #[test_case(fidl_common::WlanMacRole::Ap; "ap")]
1267    #[fuchsia::test(add_test_attr = false)]
1268    fn test_deauth_conf(mac_role: fidl_common::WlanMacRole) {
1269        let (mut h, mut test_fut) =
1270            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1271        h.fake_device.lock().unwrap().query_device_info_mock.as_mut().unwrap().role =
1272            Some(mac_role);
1273        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1274
1275        let deauth_conf = fidl_fullmac::WlanFullmacImplIfcDeauthConfRequest {
1276            peer_sta_address: Some([1u8; 6]),
1277            ..Default::default()
1278        };
1279        assert_variant!(
1280            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.deauth_conf(&deauth_conf)),
1281            Poll::Ready(Ok(()))
1282        );
1283        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1284
1285        if mac_role == fidl_common::WlanMacRole::Client {
1286            assert_variant!(
1287                h.driver_calls.try_next(),
1288                Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
1289                  assert_eq!(req.online, Some(false));
1290              }
1291            );
1292        } else {
1293            assert_variant!(h.driver_calls.try_next(), Err(_));
1294        }
1295
1296        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1297        let conf =
1298            assert_variant!(event, fidl_mlme::MlmeEvent::DeauthenticateConf { resp } => resp);
1299        assert_eq!(conf, fidl_mlme::DeauthenticateConfirm { peer_sta_address: [1u8; 6] });
1300    }
1301
1302    #[test_case(fidl_common::WlanMacRole::Client; "client")]
1303    #[test_case(fidl_common::WlanMacRole::Ap; "ap")]
1304    #[fuchsia::test(add_test_attr = false)]
1305    fn test_deauth_ind(mac_role: fidl_common::WlanMacRole) {
1306        let (mut h, mut test_fut) =
1307            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1308        h.fake_device.lock().unwrap().query_device_info_mock.as_mut().unwrap().role =
1309            Some(mac_role);
1310        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1311
1312        let deauth_ind = fidl_fullmac::WlanFullmacImplIfcDeauthIndRequest {
1313            peer_sta_address: Some([1u8; 6]),
1314            reason_code: Some(fidl_ieee80211::ReasonCode::LeavingNetworkDeauth),
1315            locally_initiated: Some(true),
1316            ..Default::default()
1317        };
1318        assert_variant!(
1319            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.deauth_ind(&deauth_ind)),
1320            Poll::Ready(Ok(())),
1321        );
1322        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1323
1324        if mac_role == fidl_common::WlanMacRole::Client {
1325            assert_variant!(
1326                h.driver_calls.try_next(),
1327                Ok(Some(DriverCall::OnLinkStateChanged { req })) =>{
1328                  assert_eq!(req.online, Some(false));
1329              }
1330            );
1331        } else {
1332            assert_variant!(h.driver_calls.try_next(), Err(_));
1333        }
1334
1335        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1336        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::DeauthenticateInd { ind } => ind);
1337        assert_eq!(
1338            ind,
1339            fidl_mlme::DeauthenticateIndication {
1340                peer_sta_address: [1u8; 6],
1341                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1342                locally_initiated: true,
1343            }
1344        );
1345    }
1346
1347    #[test]
1348    fn test_assoc_ind() {
1349        let (mut h, mut test_fut) = TestHelper::set_up();
1350        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1351
1352        let assoc_ind = fidl_fullmac::WlanFullmacImplIfcAssocIndRequest {
1353            peer_sta_address: Some([1u8; 6]),
1354            listen_interval: Some(2),
1355            ssid: vec![3u8; 4].into(),
1356            rsne: Some(vec![5u8; 6]),
1357            vendor_ie: Some(vec![7u8; 8]),
1358            ..Default::default()
1359        };
1360        assert_variant!(
1361            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.assoc_ind(&assoc_ind)),
1362            Poll::Ready(Ok(()))
1363        );
1364        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1365
1366        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1367        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::AssociateInd { ind } => ind);
1368        assert_eq!(
1369            ind,
1370            fidl_mlme::AssociateIndication {
1371                peer_sta_address: [1u8; 6],
1372                capability_info: 0,
1373                listen_interval: 2,
1374                ssid: Some(vec![3u8; 4]),
1375                rates: vec![],
1376                rsne: Some(vec![5u8; 6]),
1377            }
1378        );
1379    }
1380
1381    #[test_case(fidl_common::WlanMacRole::Client; "client")]
1382    #[test_case(fidl_common::WlanMacRole::Ap; "ap")]
1383    #[fuchsia::test(add_test_attr = false)]
1384    fn test_disassoc_conf(mac_role: fidl_common::WlanMacRole) {
1385        let (mut h, mut test_fut) =
1386            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1387        h.fake_device.lock().unwrap().query_device_info_mock.as_mut().unwrap().role =
1388            Some(mac_role);
1389        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1390
1391        let disassoc_conf = fidl_fullmac::WlanFullmacImplIfcDisassocConfRequest {
1392            status: Some(1),
1393            ..Default::default()
1394        };
1395        assert_variant!(
1396            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.disassoc_conf(&disassoc_conf)),
1397            Poll::Ready(Ok(()))
1398        );
1399        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1400
1401        if mac_role == fidl_common::WlanMacRole::Client {
1402            assert_variant!(
1403                h.driver_calls.try_next(),
1404                Ok(Some(DriverCall::OnLinkStateChanged { req })) =>{
1405                  assert_eq!(req.online, Some(false));
1406              }
1407            );
1408        } else {
1409            assert_variant!(h.driver_calls.try_next(), Err(_));
1410        }
1411
1412        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1413        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::DisassociateConf { resp } => resp);
1414        assert_eq!(conf, fidl_mlme::DisassociateConfirm { status: 1 });
1415    }
1416
1417    #[test_case(fidl_common::WlanMacRole::Client; "client")]
1418    #[test_case(fidl_common::WlanMacRole::Ap; "ap")]
1419    #[fuchsia::test(add_test_attr = false)]
1420    fn test_disassoc_ind(mac_role: fidl_common::WlanMacRole) {
1421        let (mut h, mut test_fut) =
1422            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1423        h.fake_device.lock().unwrap().query_device_info_mock.as_mut().unwrap().role =
1424            Some(mac_role);
1425        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1426
1427        let disassoc_ind = fidl_fullmac::WlanFullmacImplIfcDisassocIndRequest {
1428            peer_sta_address: Some([1u8; 6]),
1429            reason_code: Some(fidl_ieee80211::ReasonCode::LeavingNetworkDeauth),
1430            locally_initiated: Some(true),
1431            ..Default::default()
1432        };
1433        assert_variant!(
1434            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.disassoc_ind(&disassoc_ind)),
1435            Poll::Ready(Ok(()))
1436        );
1437        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1438
1439        if mac_role == fidl_common::WlanMacRole::Client {
1440            assert_variant!(
1441                h.driver_calls.try_next(),
1442                Ok(Some(DriverCall::OnLinkStateChanged { req })) =>{
1443                  assert_eq!(req.online, Some(false));
1444              }
1445            );
1446        } else {
1447            assert_variant!(h.driver_calls.try_next(), Err(_));
1448        }
1449
1450        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1451        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::DisassociateInd { ind } => ind);
1452        assert_eq!(
1453            ind,
1454            fidl_mlme::DisassociateIndication {
1455                peer_sta_address: [1u8; 6],
1456                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1457                locally_initiated: true,
1458            }
1459        );
1460    }
1461
1462    #[test_case(fidl_fullmac::StartResult::Success, true, fidl_mlme::StartResultCode::Success; "success start result")]
1463    #[test_case(fidl_fullmac::StartResult::BssAlreadyStartedOrJoined, false, fidl_mlme::StartResultCode::BssAlreadyStartedOrJoined; "other start result")]
1464    #[fuchsia::test(add_test_attr = false)]
1465    fn test_start_conf(
1466        start_result: fidl_fullmac::StartResult,
1467        expected_link_state_changed: bool,
1468        expected_fidl_result_code: fidl_mlme::StartResultCode,
1469    ) {
1470        let (mut h, mut test_fut) = TestHelper::set_up();
1471        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1472
1473        let start_conf = fidl_fullmac::WlanFullmacImplIfcStartConfRequest {
1474            result_code: Some(start_result),
1475            ..Default::default()
1476        };
1477        assert_variant!(
1478            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.start_conf(&start_conf)),
1479            Poll::Ready(Ok(()))
1480        );
1481        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1482
1483        if expected_link_state_changed {
1484            assert_variant!(
1485                h.driver_calls.try_next(),
1486                Ok(Some(DriverCall::OnLinkStateChanged { req }))=>{
1487                  assert_eq!(req.online, Some(true));
1488              }
1489            );
1490        } else {
1491            assert_variant!(h.driver_calls.try_next(), Err(_));
1492        }
1493
1494        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1495        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::StartConf { resp } => resp);
1496        assert_eq!(conf, fidl_mlme::StartConfirm { result_code: expected_fidl_result_code });
1497    }
1498
1499    #[test]
1500    fn test_stop_conf() {
1501        let (mut h, mut test_fut) = TestHelper::set_up();
1502        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1503
1504        let stop_conf = fidl_fullmac::WlanFullmacImplIfcStopConfRequest {
1505            result_code: Some(fidl_fullmac::StopResult::Success),
1506            ..Default::default()
1507        };
1508        assert_variant!(
1509            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.stop_conf(&stop_conf)),
1510            Poll::Ready(Ok(()))
1511        );
1512        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1513
1514        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1515        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::StopConf { resp } => resp);
1516        assert_eq!(
1517            conf,
1518            fidl_mlme::StopConfirm { result_code: fidl_mlme::StopResultCode::Success }
1519        );
1520    }
1521
1522    #[test]
1523    fn test_eapol_conf() {
1524        let (mut h, mut test_fut) = TestHelper::set_up();
1525        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1526
1527        let eapol_conf = fidl_fullmac::WlanFullmacImplIfcEapolConfRequest {
1528            result_code: Some(fidl_fullmac::EapolTxResult::Success),
1529            dst_addr: Some([1u8; 6]),
1530            ..Default::default()
1531        };
1532        assert_variant!(
1533            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.eapol_conf(&eapol_conf)),
1534            Poll::Ready(Ok(()))
1535        );
1536        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1537
1538        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1539        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::EapolConf { resp } => resp);
1540        assert_eq!(
1541            conf,
1542            fidl_mlme::EapolConfirm {
1543                result_code: fidl_mlme::EapolResultCode::Success,
1544                dst_addr: [1u8; 6],
1545            }
1546        );
1547    }
1548
1549    #[test]
1550    fn test_on_channel_switch() {
1551        let (mut h, mut test_fut) = TestHelper::set_up();
1552        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1553
1554        let channel_switch_info = fidl_fullmac::WlanFullmacChannelSwitchInfo { new_channel: 9 };
1555        assert_variant!(
1556            h.exec.run_until_stalled(
1557                &mut h.fullmac_ifc_proxy.on_channel_switch(&channel_switch_info)
1558            ),
1559            Poll::Ready(Ok(()))
1560        );
1561        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1562
1563        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1564        let info = assert_variant!(event, fidl_mlme::MlmeEvent::OnChannelSwitched { info } => info);
1565        assert_eq!(info, fidl_internal::ChannelSwitchInfo { new_channel: 9 });
1566    }
1567
1568    #[test]
1569    fn test_roam_start_ind() {
1570        let (mut h, mut test_fut) =
1571            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1572        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1573
1574        let selected_bss = create_bss_descriptions();
1575        let roam_start_ind = fidl_fullmac::WlanFullmacImplIfcRoamStartIndRequest {
1576            selected_bssid: Some(selected_bss.bssid.clone()),
1577            selected_bss: Some(selected_bss.clone()),
1578            original_association_maintained: Some(false),
1579            ..Default::default()
1580        };
1581        assert_variant!(
1582            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.roam_start_ind(&roam_start_ind)),
1583            Poll::Ready(Ok(()))
1584        );
1585        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1586
1587        // Receipt of a roam start causes MLME to close the controlled port.
1588        assert_variant!(
1589            h.driver_calls.try_next(),
1590            Ok(Some(DriverCall::OnLinkStateChanged { req }))=>{
1591              assert_eq!(req.online, Some(false));
1592          }
1593        );
1594
1595        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1596        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::RoamStartInd { ind } => ind);
1597
1598        // SME is notified of the roam start.
1599        assert_eq!(
1600            ind,
1601            fidl_mlme::RoamStartIndication {
1602                selected_bssid: selected_bss.bssid,
1603                selected_bss,
1604                original_association_maintained: false,
1605            }
1606        );
1607    }
1608
1609    #[test]
1610    fn test_roam_req() {
1611        let (mut h, mut test_fut) =
1612            TestHelper::set_up_with_link_state(fidl_mlme::ControlledPortState::Open);
1613
1614        let selected_bss = create_bss_descriptions();
1615        let roam_req = wlan_sme::MlmeRequest::Roam(fidl_mlme::RoamRequest {
1616            selected_bss: selected_bss.clone(),
1617        });
1618
1619        h.mlme_request_sender.unbounded_send(roam_req).expect("sending RoamReq should succeed");
1620        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1621
1622        // Receipt of a roam request causes MLME to close the controlled port.
1623        assert_variant!(
1624            h.driver_calls.try_next(),
1625            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
1626                assert_eq!(req.online, Some(false));
1627            }
1628        );
1629        assert_variant!(h.driver_calls.try_next(), Ok(Some(DriverCall::RoamReq { req })) => {
1630            assert_eq!(selected_bss, req.selected_bss.clone().unwrap());
1631        });
1632    }
1633
1634    #[test]
1635    fn test_roam_result_ind() {
1636        let (mut h, mut test_fut) = TestHelper::set_up();
1637        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1638
1639        let selected_bss = create_bss_descriptions();
1640        let original_association_maintained = false;
1641        let target_bss_authenticated = true;
1642        let association_id = 42;
1643        let association_ies = Vec::new();
1644
1645        let roam_result_ind = fidl_fullmac::WlanFullmacImplIfcRoamResultIndRequest {
1646            selected_bssid: Some(selected_bss.bssid.clone()),
1647            status_code: Some(fidl_ieee80211::StatusCode::Success),
1648            original_association_maintained: Some(original_association_maintained),
1649            target_bss_authenticated: Some(target_bss_authenticated),
1650            association_id: Some(association_id),
1651            association_ies: Some(association_ies.clone()),
1652            ..Default::default()
1653        };
1654
1655        assert_variant!(
1656            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.roam_result_ind(&roam_result_ind)),
1657            Poll::Ready(Ok(()))
1658        );
1659        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1660
1661        // Receipt of a roam result success causes MLME to open the controlled port on an open network.
1662        assert_variant!(
1663            h.driver_calls.try_next(),
1664            Ok(Some(DriverCall::OnLinkStateChanged { req }))=>{
1665              assert_eq!(req.online, Some(true));
1666          }
1667        );
1668
1669        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1670        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::RoamResultInd { ind } => ind);
1671
1672        // SME is notified of the roam result.
1673        assert_eq!(
1674            ind,
1675            fidl_mlme::RoamResultIndication {
1676                selected_bssid: selected_bss.bssid,
1677                status_code: fidl_ieee80211::StatusCode::Success,
1678                original_association_maintained,
1679                target_bss_authenticated,
1680                association_id,
1681                association_ies,
1682            }
1683        );
1684    }
1685
1686    #[test]
1687    fn test_roam_conf() {
1688        let (mut h, mut test_fut) = TestHelper::set_up();
1689        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1690
1691        let selected_bss = create_bss_descriptions();
1692        let original_association_maintained = false;
1693        let target_bss_authenticated = true;
1694        let association_id = 42;
1695        let association_ies = Vec::new();
1696
1697        let roam_conf = fidl_fullmac::WlanFullmacImplIfcRoamConfRequest {
1698            selected_bssid: Some(selected_bss.bssid.clone()),
1699            status_code: Some(fidl_ieee80211::StatusCode::Success),
1700            original_association_maintained: Some(original_association_maintained),
1701            target_bss_authenticated: Some(target_bss_authenticated),
1702            association_id: Some(association_id),
1703            association_ies: Some(association_ies.clone()),
1704            ..Default::default()
1705        };
1706
1707        assert_variant!(
1708            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.roam_conf(&roam_conf)),
1709            Poll::Ready(Ok(()))
1710        );
1711        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1712
1713        // Receipt of a roam result success causes MLME to open the controlled port on an open network.
1714        assert_variant!(
1715            h.driver_calls.try_next(),
1716            Ok(Some(DriverCall::OnLinkStateChanged { req })) => {
1717                assert_eq!(req.online, Some(true));
1718            }
1719        );
1720
1721        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1722        let conf = assert_variant!(event, fidl_mlme::MlmeEvent::RoamConf { conf } => conf);
1723
1724        // SME is notified of the roam result.
1725        assert_eq!(
1726            conf,
1727            fidl_mlme::RoamConfirm {
1728                selected_bssid: selected_bss.bssid,
1729                status_code: fidl_ieee80211::StatusCode::Success,
1730                original_association_maintained,
1731                target_bss_authenticated,
1732                association_id,
1733                association_ies,
1734            }
1735        );
1736    }
1737
1738    #[test]
1739    fn test_signal_report() {
1740        let (mut h, mut test_fut) = TestHelper::set_up();
1741        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1742
1743        let signal_report_ind =
1744            fidl_fullmac::WlanFullmacSignalReportIndication { rssi_dbm: 1, snr_db: 2 };
1745        assert_variant!(
1746            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.signal_report(&signal_report_ind)),
1747            Poll::Ready(Ok(()))
1748        );
1749        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1750
1751        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1752        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::SignalReport { ind } => ind);
1753        assert_eq!(ind, fidl_internal::SignalReportIndication { rssi_dbm: 1, snr_db: 2 });
1754    }
1755
1756    #[test]
1757    fn test_eapol_ind() {
1758        let (mut h, mut test_fut) = TestHelper::set_up();
1759        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1760
1761        let eapol_ind = fidl_fullmac::WlanFullmacImplIfcEapolIndRequest {
1762            src_addr: Some([1u8; 6]),
1763            dst_addr: Some([2u8; 6]),
1764            data: Some(vec![3u8; 4]),
1765            ..Default::default()
1766        };
1767        assert_variant!(
1768            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.eapol_ind(&eapol_ind)),
1769            Poll::Ready(Ok(()))
1770        );
1771        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1772
1773        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1774        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::EapolInd { ind } => ind);
1775        assert_eq!(
1776            ind,
1777            fidl_mlme::EapolIndication {
1778                src_addr: [1u8; 6],
1779                dst_addr: [2u8; 6],
1780                data: vec![3u8; 4],
1781            }
1782        );
1783    }
1784
1785    #[test]
1786    fn test_on_pmk_available() {
1787        let (mut h, mut test_fut) = TestHelper::set_up();
1788        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1789
1790        let pmk_info = fidl_fullmac::WlanFullmacImplIfcOnPmkAvailableRequest {
1791            pmk: Some(vec![1u8; 2]),
1792            pmkid: Some(vec![3u8; 4]),
1793            ..Default::default()
1794        };
1795        assert_variant!(
1796            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.on_pmk_available(&pmk_info)),
1797            Poll::Ready(Ok(()))
1798        );
1799        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1800
1801        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1802        let info = assert_variant!(event, fidl_mlme::MlmeEvent::OnPmkAvailable { info } => info);
1803        assert_eq!(info, fidl_mlme::PmkInfo { pmk: vec![1u8; 2], pmkid: vec![3u8; 4] });
1804    }
1805
1806    #[test]
1807    fn test_sae_handshake_ind() {
1808        let (mut h, mut test_fut) = TestHelper::set_up();
1809        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1810
1811        let sae_handshake_ind = fidl_fullmac::WlanFullmacImplIfcSaeHandshakeIndRequest {
1812            peer_sta_address: Some([1u8; 6]),
1813            ..Default::default()
1814        };
1815        assert_variant!(
1816            h.exec
1817                .run_until_stalled(&mut h.fullmac_ifc_proxy.sae_handshake_ind(&sae_handshake_ind)),
1818            Poll::Ready(Ok(()))
1819        );
1820        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1821
1822        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1823        let ind = assert_variant!(event, fidl_mlme::MlmeEvent::OnSaeHandshakeInd { ind } => ind);
1824        assert_eq!(ind, fidl_mlme::SaeHandshakeIndication { peer_sta_address: [1u8; 6] });
1825    }
1826
1827    #[test]
1828    fn test_sae_frame_rx() {
1829        let (mut h, mut test_fut) = TestHelper::set_up();
1830        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1831
1832        let sae_frame = fidl_fullmac::SaeFrame {
1833            peer_sta_address: Some([1u8; 6]),
1834            status_code: Some(fidl_ieee80211::StatusCode::Success),
1835            seq_num: Some(2),
1836            sae_fields: Some(vec![3u8; 4]),
1837            ..Default::default()
1838        };
1839        assert_variant!(
1840            h.exec.run_until_stalled(&mut h.fullmac_ifc_proxy.sae_frame_rx(&sae_frame)),
1841            Poll::Ready(Ok(()))
1842        );
1843        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1844
1845        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1846        let frame = assert_variant!(event, fidl_mlme::MlmeEvent::OnSaeFrameRx { frame } => frame);
1847        assert_eq!(
1848            frame,
1849            fidl_mlme::SaeFrame {
1850                peer_sta_address: [1u8; 6],
1851                status_code: fidl_ieee80211::StatusCode::Success,
1852                seq_num: 2,
1853                sae_fields: vec![3u8; 4],
1854            }
1855        );
1856    }
1857
1858    #[test]
1859    fn test_on_wmm_status_resp() {
1860        let (mut h, mut test_fut) = TestHelper::set_up();
1861        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1862
1863        let status = zx::sys::ZX_OK;
1864        let wmm_params = fidl_common::WlanWmmParameters {
1865            apsd: true,
1866            ac_be_params: fidl_common::WlanWmmAccessCategoryParameters {
1867                ecw_min: 1,
1868                ecw_max: 2,
1869                aifsn: 3,
1870                txop_limit: 4,
1871                acm: true,
1872            },
1873            ac_bk_params: fidl_common::WlanWmmAccessCategoryParameters {
1874                ecw_min: 5,
1875                ecw_max: 6,
1876                aifsn: 7,
1877                txop_limit: 8,
1878                acm: false,
1879            },
1880            ac_vi_params: fidl_common::WlanWmmAccessCategoryParameters {
1881                ecw_min: 9,
1882                ecw_max: 10,
1883                aifsn: 11,
1884                txop_limit: 12,
1885                acm: true,
1886            },
1887            ac_vo_params: fidl_common::WlanWmmAccessCategoryParameters {
1888                ecw_min: 13,
1889                ecw_max: 14,
1890                aifsn: 15,
1891                txop_limit: 16,
1892                acm: false,
1893            },
1894        };
1895        assert_variant!(
1896            h.exec.run_until_stalled(
1897                &mut h.fullmac_ifc_proxy.on_wmm_status_resp(status, &wmm_params)
1898            ),
1899            Poll::Ready(Ok(()))
1900        );
1901        assert_variant!(h.exec.run_until_stalled(&mut test_fut), Poll::Pending);
1902
1903        let event = assert_variant!(h.mlme_event_receiver.try_next(), Ok(Some(ev)) => ev);
1904        let (status, resp) = assert_variant!(event, fidl_mlme::MlmeEvent::OnWmmStatusResp { status, resp } => (status, resp));
1905        assert_eq!(status, zx::sys::ZX_OK);
1906        assert_eq!(
1907            resp,
1908            fidl_internal::WmmStatusResponse {
1909                apsd: true,
1910                ac_be_params: fidl_internal::WmmAcParams {
1911                    ecw_min: 1,
1912                    ecw_max: 2,
1913                    aifsn: 3,
1914                    txop_limit: 4,
1915                    acm: true,
1916                },
1917                ac_bk_params: fidl_internal::WmmAcParams {
1918                    ecw_min: 5,
1919                    ecw_max: 6,
1920                    aifsn: 7,
1921                    txop_limit: 8,
1922                    acm: false,
1923                },
1924                ac_vi_params: fidl_internal::WmmAcParams {
1925                    ecw_min: 9,
1926                    ecw_max: 10,
1927                    aifsn: 11,
1928                    txop_limit: 12,
1929                    acm: true,
1930                },
1931                ac_vo_params: fidl_internal::WmmAcParams {
1932                    ecw_min: 13,
1933                    ecw_max: 14,
1934                    aifsn: 15,
1935                    txop_limit: 16,
1936                    acm: false,
1937                },
1938            }
1939        );
1940    }
1941
1942    struct TestHelper {
1943        fake_device: Arc<Mutex<FakeFullmacDeviceMocks>>,
1944        mlme_request_sender: mpsc::UnboundedSender<wlan_sme::MlmeRequest>,
1945        fullmac_ifc_proxy: fidl_fullmac::WlanFullmacImplIfcProxy,
1946        mlme_event_receiver: mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>,
1947        driver_calls: mpsc::UnboundedReceiver<DriverCall>,
1948        exec: fasync::TestExecutor,
1949    }
1950
1951    impl TestHelper {
1952        // By default, MLME is set up closed
1953        pub fn set_up() -> (Self, Pin<Box<impl Future<Output = Result<(), anyhow::Error>>>>) {
1954            Self::set_up_with_link_state(fidl_mlme::ControlledPortState::Closed)
1955        }
1956
1957        /// For tests that need to observe all OnLinkStateChanged calls, they can choose what link
1958        /// state the device starts with to ensure that none get filtered out by MLME.
1959        pub fn set_up_with_link_state(
1960            device_link_state: fidl_mlme::ControlledPortState,
1961        ) -> (Self, Pin<Box<impl Future<Output = Result<(), anyhow::Error>>>>) {
1962            let exec = fasync::TestExecutor::new();
1963
1964            let (fake_device, driver_call_receiver) = FakeFullmacDevice::new();
1965            let (mlme_request_sender, mlme_request_stream) = mpsc::unbounded();
1966            let (mlme_event_sender, mlme_event_receiver) = mpsc::unbounded();
1967            let mlme_event_sink = UnboundedSink::new(mlme_event_sender);
1968
1969            let (driver_event_sender, driver_event_stream) = mpsc::unbounded();
1970            let driver_event_sink = FullmacDriverEventSink(UnboundedSink::new(driver_event_sender));
1971
1972            let (fullmac_ifc_proxy, fullmac_ifc_request_stream) =
1973                fidl::endpoints::create_proxy_and_stream::<fidl_fullmac::WlanFullmacImplIfcMarker>(
1974                );
1975
1976            let mocks = fake_device.mocks.clone();
1977            let main_loop = MlmeMainLoop {
1978                device: fake_device,
1979                mlme_request_stream,
1980                mlme_event_sink,
1981                driver_event_stream,
1982                is_bss_protected: false,
1983                device_link_state,
1984            };
1985
1986            let test_fut = Box::pin(main_loop.serve(fullmac_ifc_request_stream, driver_event_sink));
1987
1988            let test_helper = TestHelper {
1989                fake_device: mocks,
1990                mlme_request_sender,
1991                fullmac_ifc_proxy,
1992                mlme_event_receiver,
1993                driver_calls: driver_call_receiver,
1994                exec,
1995            };
1996            (test_helper, test_fut)
1997        }
1998    }
1999}