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