wlancfg_lib/legacy/
deprecated_client.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use crate::legacy::IfaceRef;
5use futures::prelude::*;
6use log::{debug, error};
7use {fidl_fuchsia_wlan_product_deprecatedclient as deprecated, fidl_fuchsia_wlan_sme as fidl_sme};
8
9const MAX_CONCURRENT_WLAN_REQUESTS: usize = 1000;
10
11/// Takes in stream of deprecated client requests and handles each one.
12pub async fn serve_deprecated_client(
13    requests: deprecated::DeprecatedClientRequestStream,
14    iface: IfaceRef,
15) -> Result<(), fidl::Error> {
16    requests
17        .try_for_each_concurrent(MAX_CONCURRENT_WLAN_REQUESTS, |req| {
18            handle_request(iface.clone(), req)
19        })
20        .await
21}
22
23/// Handles an individual request from the deprecated client API.
24async fn handle_request(
25    iface: IfaceRef,
26    req: deprecated::DeprecatedClientRequest,
27) -> Result<(), fidl::Error> {
28    match req {
29        deprecated::DeprecatedClientRequest::Status { responder } => {
30            debug!("Deprecated WLAN client API used for status request");
31            let r = status(&iface).await;
32            responder.send(&r)
33        }
34    }
35}
36
37/// Produces a status representing the state where no client interface is present.
38fn no_client_status() -> deprecated::WlanStatus {
39    deprecated::WlanStatus { state: deprecated::State::NoClient, current_ap: None }
40}
41
42/// Manages the calling of client SME status and translation into a format that is compatible with
43/// the deprecated client API.
44async fn status(iface: &IfaceRef) -> deprecated::WlanStatus {
45    let iface = match iface.get() {
46        Ok(iface) => iface,
47        Err(_) => return no_client_status(),
48    };
49
50    let status = match iface.sme.status().await {
51        Ok(status) => status,
52        Err(e) => {
53            // An error here indicates that the SME channel is broken.
54            error!("Failed to query status: {}", e);
55            return no_client_status();
56        }
57    };
58
59    deprecated::WlanStatus {
60        state: convert_state(&status),
61        current_ap: extract_current_ap(&status),
62    }
63}
64
65/// Translates a client SME's status information into a deprecated client state.
66fn convert_state(status: &fidl_sme::ClientStatusResponse) -> deprecated::State {
67    match status {
68        fidl_sme::ClientStatusResponse::Connected(_) => deprecated::State::Associated,
69        fidl_sme::ClientStatusResponse::Connecting(_) => deprecated::State::Associating,
70        fidl_sme::ClientStatusResponse::Roaming(_) => deprecated::State::Associating,
71        fidl_sme::ClientStatusResponse::Idle(_) => deprecated::State::Disassociated,
72    }
73}
74
75/// Parses a Client SME's status and extracts AP SSID and RSSI if applicable.
76fn extract_current_ap(status: &fidl_sme::ClientStatusResponse) -> Option<Box<deprecated::Ap>> {
77    match status {
78        fidl_sme::ClientStatusResponse::Connected(serving_ap_info) => {
79            let ssid = String::from_utf8_lossy(&serving_ap_info.ssid).to_string();
80            let rssi_dbm = serving_ap_info.rssi_dbm;
81            Some(Box::new(deprecated::Ap { ssid, rssi_dbm }))
82        }
83        fidl_sme::ClientStatusResponse::Connecting(_)
84        | fidl_sme::ClientStatusResponse::Roaming(_)
85        | fidl_sme::ClientStatusResponse::Idle(_) => None,
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::legacy::Iface;
93    use assert_matches::assert_matches;
94    use fidl::endpoints::create_proxy;
95    use futures::task::Poll;
96    use std::pin::pin;
97    use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fuchsia_async as fasync};
98
99    struct TestValues {
100        iface: IfaceRef,
101        sme_stream: fidl_sme::ClientSmeRequestStream,
102    }
103
104    fn test_setup() -> TestValues {
105        let (sme, server) = create_proxy::<fidl_sme::ClientSmeMarker>();
106
107        let iface = Iface { sme, iface_id: 0 };
108        let iface_ref = IfaceRef::new();
109        iface_ref.set_if_empty(iface);
110
111        TestValues { iface: iface_ref, sme_stream: server.into_stream() }
112    }
113
114    #[fuchsia::test]
115    fn test_no_client() {
116        let mut exec = fasync::TestExecutor::new();
117        let iface = IfaceRef::new();
118        let status_fut = status(&iface);
119        let mut status_fut = pin!(status_fut);
120
121        // Expect that no client is reported and the AP status information is empty.
122        assert_matches!(
123            exec.run_until_stalled(&mut status_fut),
124            Poll::Ready(deprecated::WlanStatus {
125                state: deprecated::State::NoClient,
126                current_ap: None,
127            })
128        );
129    }
130
131    #[fuchsia::test]
132    fn test_broken_sme() {
133        let mut exec = fasync::TestExecutor::new();
134        let test_values = test_setup();
135
136        // Drop the SME request stream so that the client request will fail.
137        drop(test_values.sme_stream);
138
139        let status_fut = status(&test_values.iface);
140        let mut status_fut = pin!(status_fut);
141
142        // Expect that no client is reported and the AP status information is empty.
143        assert_matches!(
144            exec.run_until_stalled(&mut status_fut),
145            Poll::Ready(deprecated::WlanStatus {
146                state: deprecated::State::NoClient,
147                current_ap: None,
148            })
149        );
150    }
151
152    #[fuchsia::test]
153    fn test_disconnected_client() {
154        let mut exec = fasync::TestExecutor::new();
155        let mut test_values = test_setup();
156        let status_fut = status(&test_values.iface);
157        let mut status_fut = pin!(status_fut);
158
159        // Expect an SME status request and send back a response indicating that the SME is neither
160        // connected nor connecting.
161        assert_matches!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
162        assert_matches!(
163            exec.run_until_stalled(&mut test_values.sme_stream.next()),
164            Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
165                responder.send(&fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty{})).expect("could not send sme response")
166            }
167        );
168
169        // Expect a disconnected status.
170        assert_matches!(
171            exec.run_until_stalled(&mut status_fut),
172            Poll::Ready(deprecated::WlanStatus {
173                state: deprecated::State::Disassociated,
174                current_ap: None,
175            })
176        );
177    }
178
179    #[fuchsia::test]
180    fn test_connecting_client() {
181        let mut exec = fasync::TestExecutor::new();
182        let mut test_values = test_setup();
183        let status_fut = status(&test_values.iface);
184        let mut status_fut = pin!(status_fut);
185
186        // Expect an SME status request and send back a response indicating that the SME is
187        // connecting.
188        assert_matches!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
189        assert_matches!(
190                    exec.run_until_stalled(&mut test_values.sme_stream.next()),
191                    Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
192                        responder.send(&fidl_sme::ClientStatusResponse::Connecting("test_ssid".as_bytes().to_vec()))
193        .expect("could not send sme response")
194                    }
195                );
196
197        // Expect a connecting status.
198        assert_matches!(
199            exec.run_until_stalled(&mut status_fut),
200            Poll::Ready(deprecated::WlanStatus {
201                state: deprecated::State::Associating,
202                current_ap: None,
203            })
204        );
205    }
206
207    #[fuchsia::test]
208    fn test_connected_client() {
209        let mut exec = fasync::TestExecutor::new();
210        let mut test_values = test_setup();
211        let ssid = "test_ssid";
212        let rssi_dbm = -70;
213        let status_fut = status(&test_values.iface);
214        let mut status_fut = pin!(status_fut);
215
216        // Expect an SME status request and send back a response indicating that the SME is
217        // connected.
218        assert_matches!(exec.run_until_stalled(&mut status_fut), Poll::Pending);
219        assert_matches!(
220            exec.run_until_stalled(&mut test_values.sme_stream.next()),
221            Poll::Ready(Some(Ok(fidl_sme::ClientSmeRequest::Status { responder }))) => {
222                responder.send(&fidl_sme::ClientStatusResponse::Connected(
223                    fidl_sme::ServingApInfo{
224                        bssid: [0, 0, 0, 0, 0, 0],
225                        ssid: ssid.as_bytes().to_vec(),
226                        rssi_dbm,
227                        snr_db: 0,
228                        channel: fidl_ieee80211::WlanChannel {
229                            primary: 1,
230                            cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
231                            secondary80: 0,
232                        },
233                        protection: fidl_sme::Protection::Unknown,
234                    })).expect("could not send sme response")
235            }
236        );
237
238        // Expect a connected status.
239        let expected_current_ap =
240            Some(Box::new(deprecated::Ap { ssid: ssid.to_string(), rssi_dbm }));
241        assert_matches!(
242            exec.run_until_stalled(&mut status_fut),
243            Poll::Ready(deprecated::WlanStatus {
244                state: deprecated::State::Associated,
245                current_ap,
246            }) => {
247                assert_eq!(current_ap, expected_current_ap);
248            }
249        );
250    }
251}