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