Skip to main content

sl4f_lib/wlan_policy/
facade.rs

1// Copyright 2020 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::common_utils::common::macros::with_line;
6use crate::wlan_policy::types::{ClientStateSummary, NetworkConfig};
7use anyhow::{Error, format_err};
8use fidl::endpoints::Proxy as _;
9use fidl_fuchsia_wlan_policy as fidl_policy;
10use fuchsia_async::{self as fasync, DurationExt as _};
11use fuchsia_component::client::connect_to_protocol;
12use fuchsia_sync::RwLock;
13use futures::TryStreamExt;
14use log::*;
15use std::cell::Cell;
16use std::collections::HashSet;
17use std::fmt::{self, Debug};
18
19pub struct WlanPolicyFacade {
20    controller: RwLock<InnerController>,
21    update_listener: Cell<Option<fidl_policy::ClientStateUpdatesRequestStream>>,
22}
23
24#[derive(Debug)]
25pub struct InnerController {
26    inner: Option<fidl_policy::ClientControllerProxy>,
27}
28
29impl Debug for WlanPolicyFacade {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        let listener = self.update_listener.take();
32        let update_listener =
33            if listener.is_some() { "Some(ClientStateUpdatesRequestStream)" } else { "None" }
34                .to_string();
35        self.update_listener.set(listener);
36
37        f.debug_struct("InnerWlanPolicyFacade")
38            .field("controller", &self.controller)
39            .field("update_listener", &update_listener)
40            .finish()
41    }
42}
43
44impl WlanPolicyFacade {
45    pub fn new() -> Result<WlanPolicyFacade, Error> {
46        Ok(Self {
47            controller: RwLock::new(InnerController { inner: None }),
48            update_listener: Cell::new(None),
49        })
50    }
51
52    /// Create a client controller and listen for client state updates. If the facade already has
53    /// a client controller, recreate it and start listening for client state updates again.
54    /// See [`WlanPolicyFacade::get_update()`] for details about listening for updates.
55    ///
56    /// A client controller is necessary to access the fuchsia.wlan.policy.ClientController API.
57    /// Only one caller can have the control channel open at a time.
58    pub async fn create_client_controller(&self) -> Result<(), Error> {
59        let tag = "WlanPolicyFacade::create_client_controller";
60
61        {
62            let mut controller_guard = self.controller.write();
63            controller_guard.inner = None;
64        }
65
66        let (controller, update_stream) = Self::init_client_controller().await.map_err(|e| {
67            info!(tag = &with_line!(tag); "Error getting client controller: {}", e);
68            format_err!("Error getting client controller: {}", e)
69        })?;
70
71        {
72            let mut controller_guard = self.controller.write();
73            controller_guard.inner = Some(controller);
74        }
75
76        self.update_listener.set(Some(update_stream));
77
78        Ok(())
79    }
80
81    /// Creates and returns a client controller. This also returns the stream for listener updates
82    /// that is created in the process of creating the client controller.
83    async fn init_client_controller() -> Result<
84        (fidl_policy::ClientControllerProxy, fidl_policy::ClientStateUpdatesRequestStream),
85        Error,
86    > {
87        let provider = connect_to_protocol::<fidl_policy::ClientProviderMarker>()?;
88        let (controller, req) =
89            fidl::endpoints::create_proxy::<fidl_policy::ClientControllerMarker>();
90        let (update_sink, update_stream) =
91            fidl::endpoints::create_request_stream::<fidl_policy::ClientStateUpdatesMarker>();
92        provider.get_controller(req, update_sink)?;
93
94        // Sleep very briefly to introduce a yield point (with the await) so that in case the other
95        // end of the channel is closed, its status is correctly propagated by the kernel and we can
96        // accurately check it using `is_closed()`.
97        let sleep_duration = zx::MonotonicDuration::from_millis(10);
98        fasync::Timer::new(sleep_duration.after_now()).await;
99        if controller.is_closed() {
100            return Err(format_err!(
101                "Policy layer closed channel, client controller is likely already in use."
102            ));
103        }
104
105        Ok((controller, update_stream))
106    }
107
108    /// Drop the facade's client controller so that something else can get a controller.
109    pub fn drop_client_controller(&self) {
110        let mut controller_guard = self.controller.write();
111        controller_guard.inner = None;
112    }
113
114    /// Creates a listener update stream for getting status updates.
115    fn init_listener() -> Result<fidl_policy::ClientStateUpdatesRequestStream, Error> {
116        let listener = connect_to_protocol::<fidl_policy::ClientListenerMarker>()?;
117        let (client_end, server_end) =
118            fidl::endpoints::create_endpoints::<fidl_policy::ClientStateUpdatesMarker>();
119        listener.get_listener(client_end)?;
120        Ok(server_end.into_stream())
121    }
122
123    /// This function will set a new listener even if there is one because new listeners will get
124    /// the most recent update immediately without waiting. This might be used to set the facade's
125    /// listener update stream if it wasn't set by creating the client controller or to set a clean
126    /// state for a new test.
127    pub fn set_new_listener(&self) -> Result<(), Error> {
128        self.update_listener.set(Some(Self::init_listener()?));
129        Ok(())
130    }
131
132    /// Request a scan and return the list of network names found, or an error if one occurs.
133    pub async fn scan_for_networks(&self) -> Result<Vec<String>, Error> {
134        let controller = self
135            .controller
136            .read()
137            .inner
138            .clone()
139            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
140
141        // Policy will send results back through this iterator
142        let (iter, server) =
143            fidl::endpoints::create_proxy::<fidl_policy::ScanResultIteratorMarker>();
144        // Request a scan from policy
145        controller.scan_for_networks(server)?;
146
147        // Get results and check for scan error. Get the next chunk of results until we get an
148        // error or empty list, which indicates the end of results.
149        let mut scan_results = HashSet::new();
150        loop {
151            let results = iter.get_next().await?.map_err(|e| format_err!("{:?}", e))?;
152            if results.is_empty() {
153                break;
154            }
155
156            // For now, just return the names of the scanned networks.
157            let results = Self::stringify_scan_results(results);
158            scan_results.extend(results);
159        }
160        Ok(scan_results.into_iter().collect())
161    }
162
163    /// Connect to a network through the policy layer. The network must have been saved first.
164    /// Returns an error if the connect command was not received, otherwise returns the response
165    /// to the connect request as a string. A connection should be triggered if the response is
166    /// "Acknowledged".
167    /// # Arguments:
168    /// * `target_ssid': The SSID (network name) that we want to connect to.
169    /// * `type`: Security type should be a string of the security type, either "none", "wep",
170    ///           "wpa", "wpa2" or "wpa3", matching the policy API's defined security types, case
171    ///           doesn't matter.
172    pub async fn connect(
173        &self,
174        target_ssid: Vec<u8>,
175        type_: fidl_policy::SecurityType,
176    ) -> Result<String, Error> {
177        let controller = self
178            .controller
179            .read()
180            .inner
181            .clone()
182            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
183
184        let network_id = fidl_policy::NetworkIdentifier { ssid: target_ssid, type_ };
185        let response = controller
186            .connect(&network_id)
187            .await
188            .map_err(|e| format_err!("Connect: failed to connect: {}", e))?;
189        Ok(Self::request_status_as_string(response))
190    }
191
192    fn request_status_as_string(response: fidl_policy::RequestStatus) -> String {
193        match response {
194            fidl_policy::RequestStatus::Acknowledged => "Acknowledged",
195            fidl_policy::RequestStatus::RejectedNotSupported => "RejectedNotSupported",
196            fidl_policy::RequestStatus::RejectedIncompatibleMode => "RejectedIncompatibleMode",
197            fidl_policy::RequestStatus::RejectedAlreadyInUse => "RejectedAlreadyInUse",
198            fidl_policy::RequestStatus::RejectedDuplicateRequest => "RejectedDuplicateRequest",
199        }
200        .to_string()
201    }
202
203    /// Forget the specified saved network. Doesn't do anything if network not saved.
204    /// # Arguments:
205    /// * `target_ssid`:  The SSID (network name) that we want to forget.
206    /// * `type`: the security type of the network. It should be a string, either "none", "wep",
207    ///           "wpa", "wpa2" or "wpa3", matching the policy API's defined security types. Target
208    ///           password can be password, PSK, or none, represented by empty string.
209    /// * `credential`: the password or other credential of the network we want to forget.
210    pub async fn remove_network(
211        &self,
212        target_ssid: Vec<u8>,
213        type_: fidl_policy::SecurityType,
214    ) -> Result<(), Error> {
215        let controller = self
216            .controller
217            .read()
218            .inner
219            .clone()
220            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
221        info!(
222            tag = &with_line!("WlanPolicyFacade::remove_network");
223            "Removing network: ({}{:?})",
224            String::from_utf8_lossy(&target_ssid),
225            type_
226        );
227
228        let id = fidl_policy::NetworkIdentifier { ssid: target_ssid, type_ };
229        controller
230            .forget_network(&id)
231            .await
232            .map_err(|err| format_err!("{:?}", err))? // FIDL error
233            .map_err(|err| format_err!("{:?}", err)) // network config change error
234    }
235
236    /// Remove all of the client's saved networks.
237    pub async fn remove_all_networks(&self) -> Result<(), Error> {
238        let controller = self
239            .controller
240            .read()
241            .inner
242            .clone()
243            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
244
245        // Remove each saved network individually.
246        let saved_networks = self.get_saved_networks().await?;
247        for network_config in saved_networks {
248            let id = network_config.id.as_ref().ok_or_else(|| format_err!("missing network ID"))?;
249            controller
250                .forget_network(id)
251                .await
252                .map_err(|err| format_err!("{:?}", err))? // FIDL error
253                .map_err(|err| format_err!("{:?}", err))?; // network config change error
254        }
255        Ok(())
256    }
257
258    /// Send the request to the policy layer to start making client connections.
259    pub async fn start_client_connections(&self) -> Result<(), Error> {
260        let controller = self
261            .controller
262            .read()
263            .inner
264            .clone()
265            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
266
267        let req_status = controller.start_client_connections().await?;
268        if fidl_policy::RequestStatus::Acknowledged == req_status {
269            Ok(())
270        } else {
271            bail!("{:?}", req_status);
272        }
273    }
274
275    /// Wait for and return a client update. If this is the first update gotten from the facade
276    /// since the client controller or a new update listener has been created, it will get an
277    /// immediate status. After that, it will wait for a change and return a status when there has
278    /// been a change since the last call to get_update. This call will hang if there are no
279    /// updates.
280    /// This function is not thread safe, so there should not be multiple get_update calls at the
281    /// same time unless a new listener is set between them. There is no lock around the
282    /// update_listener field of the facade in order to prevent a hanging get_update from blocking
283    /// all future get_updates.
284    pub async fn get_update(&self) -> Result<ClientStateSummary, Error> {
285        // Initialize the update listener if it has not been initialized.
286        let listener = self.update_listener.take();
287        let mut update_listener = if listener.is_none() {
288            Self::init_listener()
289        } else {
290            listener.ok_or_else(|| format_err!("failed to set update listener of facade"))
291        }?;
292
293        if let Some(update_request) = update_listener.try_next().await? {
294            let update = update_request.into_on_client_state_update();
295            let (update, responder) = match update {
296                Some((update, responder)) => (update, responder),
297                None => return Err(format_err!("Client provider produced invalid update.")),
298            };
299            // Ack the update.
300            responder.send().map_err(|e| format_err!("failed to ack update: {}", e))?;
301            // Put the update listener back in the facade
302            self.update_listener.set(Some(update_listener));
303            Ok(update.into())
304        } else {
305            self.update_listener.set(Some(update_listener));
306            Err(format_err!("update listener's next update is None"))
307        }
308    }
309
310    /// Send the request to the policy layer to stop making client connections.
311    pub async fn stop_client_connections(&self) -> Result<(), Error> {
312        let controller = self
313            .controller
314            .read()
315            .inner
316            .clone()
317            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
318
319        let req_status = controller.stop_client_connections().await?;
320        if fidl_policy::RequestStatus::Acknowledged == req_status {
321            Ok(())
322        } else {
323            bail!("{:?}", req_status);
324        }
325    }
326
327    /// Save the specified network.
328    /// # Arguments:
329    /// * `target_ssid`:  The SSID (network name) that we want to save.
330    /// * `type`: the security type of the network. It should be a string, either "none", "wep",
331    ///           "wpa", "wpa2" or "wpa3", matching the policy API's defined security types. Target
332    ///           password can be password, PSK, or none, represented by empty string
333    /// * `credential`: the password or other credential of the network we want to remember.
334    pub async fn save_network(
335        &self,
336        target_ssid: Vec<u8>,
337        type_: fidl_policy::SecurityType,
338        credential: fidl_policy::Credential,
339    ) -> Result<(), Error> {
340        let controller = self
341            .controller
342            .read()
343            .inner
344            .clone()
345            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
346
347        let network_id = fidl_policy::NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ };
348
349        controller
350            .save_network(&fidl_policy::NetworkConfig {
351                id: Some(network_id),
352                credential: Some(credential),
353                ..Default::default()
354            })
355            .await?
356            .map_err(|e| format_err!("{:?}", e))
357    }
358
359    pub async fn get_saved_networks_json(&self) -> Result<Vec<NetworkConfig>, Error> {
360        let saved_networks = self.get_saved_networks().await?;
361        // Convert FIDL network configs to JSON values that can be passed through SL4F
362        Ok(saved_networks.into_iter().map(|cfg| cfg.into()).collect::<Vec<_>>())
363    }
364
365    /// Get a list of the saved networks. Returns FIDL values to be used directly or converted to
366    /// serializable values that can be passed through SL4F
367    async fn get_saved_networks(&self) -> Result<Vec<fidl_policy::NetworkConfig>, Error> {
368        let controller = self
369            .controller
370            .read()
371            .inner
372            .clone()
373            .ok_or_else(|| format_err!("client controller has not been initialized"))?;
374
375        // Policy will send configs back through this iterator
376        let (iter, server) =
377            fidl::endpoints::create_proxy::<fidl_policy::NetworkConfigIteratorMarker>();
378        controller
379            .get_saved_networks(server)
380            .map_err(|e| format_err!("Get saved networks: fidl error {:?}", e))?;
381
382        // get each config from the stream as they become available
383        let mut networks = vec![];
384        loop {
385            let cfgs = iter.get_next().await?;
386            if cfgs.is_empty() {
387                break;
388            }
389            networks.extend(cfgs);
390        }
391        Ok(networks)
392    }
393
394    fn stringify_scan_results(results: Vec<fidl_policy::ScanResult>) -> Vec<String> {
395        results
396            .into_iter()
397            .filter_map(|result| result.id)
398            .map(|id| String::from_utf8_lossy(&id.ssid).into_owned())
399            .collect()
400    }
401}