Skip to main content

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