| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::wlan_policy::types::AccessPointState; |
| use anyhow::Error; |
| use fidl::endpoints::{create_endpoints, create_proxy}; |
| use fidl_fuchsia_wlan_common::RequestStatus; |
| use fidl_fuchsia_wlan_policy::{ |
| AccessPointControllerMarker, AccessPointControllerProxy, AccessPointListenerMarker, |
| AccessPointProviderMarker, AccessPointStateUpdatesMarker, AccessPointStateUpdatesRequestStream, |
| ConnectivityMode, Credential, NetworkConfig, NetworkIdentifier, OperatingBand, OperatingState, |
| SecurityType, |
| }; |
| use fuchsia_component::client::connect_to_protocol; |
| use futures::TryStreamExt; |
| use std::cell::Cell; |
| use std::fmt::{self, Debug}; |
| |
| pub struct WlanApPolicyFacade { |
| ap_controller: AccessPointControllerProxy, |
| update_listener: Cell<Option<AccessPointStateUpdatesRequestStream>>, |
| } |
| |
| impl Debug for WlanApPolicyFacade { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let listener = self.update_listener.take(); |
| let update_listener = |
| if listener.is_some() { "Some(AccessPointStateUpdatesRequestStream)" } else { "None" } |
| .to_string(); |
| self.update_listener.set(listener); |
| |
| f.debug_struct("WlanApPolicyFacade") |
| .field("controller", &self.ap_controller) |
| .field("update_listener", &update_listener) |
| .finish() |
| } |
| } |
| |
| impl WlanApPolicyFacade { |
| pub fn new() -> Result<WlanApPolicyFacade, Error> { |
| let policy_provider = connect_to_protocol::<AccessPointProviderMarker>()?; |
| let (ap_controller, server_end) = create_proxy::<AccessPointControllerMarker>().unwrap(); |
| |
| let (update_client_end, update_listener) = |
| create_endpoints::<AccessPointStateUpdatesMarker>(); |
| let () = policy_provider.get_controller(server_end, update_client_end)?; |
| let update_stream = update_listener.into_stream()?; |
| Ok(WlanApPolicyFacade { ap_controller, update_listener: Cell::new(Some(update_stream)) }) |
| } |
| |
| pub async fn start_access_point( |
| &self, |
| target_ssid: Vec<u8>, |
| type_: SecurityType, |
| credential: Credential, |
| mode: ConnectivityMode, |
| band: OperatingBand, |
| ) -> Result<(), Error> { |
| let listener = connect_to_protocol::<AccessPointListenerMarker>()?; |
| let (client_end, server_end) = create_endpoints::<AccessPointStateUpdatesMarker>(); |
| listener.get_listener(client_end)?; |
| let mut server_stream = server_end.into_stream()?; |
| |
| match server_stream.try_next().await? { |
| Some(update) => { |
| let update = update.into_on_access_point_state_update(); |
| let (_, responder) = match update { |
| Some((update, responder)) => (update, responder), |
| None => return Err(format_err!("AP provider produced invalid update.")), |
| }; |
| let _ = responder.send(); |
| } |
| None => return Err(format_err!("initial steam already busted")), |
| } |
| |
| let network_id = NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ }; |
| |
| match self |
| .ap_controller |
| .start_access_point( |
| &NetworkConfig { |
| id: Some(network_id), |
| credential: Some(credential), |
| ..Default::default() |
| }, |
| mode, |
| band, |
| ) |
| .await? |
| { |
| RequestStatus::Acknowledged => {} |
| RequestStatus::RejectedNotSupported => { |
| return Err(format_err!("failed to start AP (not supported)")) |
| } |
| RequestStatus::RejectedIncompatibleMode => { |
| return Err(format_err!("failed to start AP (incompatible mode)")) |
| } |
| RequestStatus::RejectedAlreadyInUse => { |
| return Err(format_err!("failed to start AP (already in use)")) |
| } |
| RequestStatus::RejectedDuplicateRequest => { |
| return Err(format_err!("failed to start AP (duplicate request)")) |
| } |
| } |
| |
| while let Some(update_request) = server_stream.try_next().await.unwrap() { |
| let update = update_request.into_on_access_point_state_update(); |
| let (updates, responder) = match update { |
| Some((update, responder)) => (update, responder), |
| None => return Err(format_err!("AP provider produced invalid update.")), |
| }; |
| let _ = responder.send(); |
| |
| for update in updates { |
| match update.state { |
| Some(state) => match state { |
| OperatingState::Failed => { |
| return Err(format_err!("Failed to start AP.")); |
| } |
| OperatingState::Starting => { |
| continue; |
| } |
| OperatingState::Active => return Ok(()), |
| }, |
| None => continue, |
| } |
| } |
| } |
| return Err(format_err!("AP update stream failed unexpectedly")); |
| } |
| |
| pub async fn stop_access_point( |
| &self, |
| target_ssid: Vec<u8>, |
| type_: SecurityType, |
| credential: Credential, |
| ) -> Result<(), Error> { |
| let network_id = NetworkIdentifier { ssid: target_ssid.clone(), type_: type_ }; |
| match self |
| .ap_controller |
| .stop_access_point(&NetworkConfig { |
| id: Some(network_id), |
| credential: Some(credential), |
| ..Default::default() |
| }) |
| .await? |
| { |
| RequestStatus::Acknowledged => Ok(()), |
| RequestStatus::RejectedNotSupported => { |
| Err(format_err!("Failed to stop AP (not supported)")) |
| } |
| RequestStatus::RejectedIncompatibleMode => { |
| Err(format_err!("Failed to stop AP (incompatible mode)")) |
| } |
| RequestStatus::RejectedAlreadyInUse => { |
| Err(format_err!("Failed to stop AP (already in use)")) |
| } |
| RequestStatus::RejectedDuplicateRequest => { |
| Err(format_err!("Failed to stop AP (duplicate request)")) |
| } |
| } |
| } |
| |
| pub async fn stop_all_access_points(&self) -> Result<(), Error> { |
| self.ap_controller.stop_all_access_points()?; |
| Ok(()) |
| } |
| |
| /// Creates a listener update stream for getting status updates. |
| fn init_listener() -> Result<AccessPointStateUpdatesRequestStream, Error> { |
| let listener = connect_to_protocol::<AccessPointListenerMarker>()?; |
| let (client_end, server_end) = |
| fidl::endpoints::create_endpoints::<AccessPointStateUpdatesMarker>(); |
| listener.get_listener(client_end)?; |
| Ok(server_end.into_stream()?) |
| } |
| |
| /// This function will set a new listener even if there is one because new listeners will get |
| /// the most recent update immediately without waiting. |
| pub fn set_new_listener(&self) -> Result<(), Error> { |
| self.update_listener.set(Some(Self::init_listener()?)); |
| Ok(()) |
| } |
| |
| /// Wait for and return an AP update. If this is the first update gotten from the facade |
| /// since the AP controller or a new update listener has been created, it will get an |
| /// immediate status. After that, it will wait for a change and return a status when there has |
| /// been a change since the last call to get_update. This call will hang if there are no |
| /// updates. |
| /// This function is not thread safe, so there should not be multiple get_update calls at the |
| /// same time unless a new listener is set between them. There is no lock around the |
| /// update_listener field of the facade in order to prevent a hanging get_update from blocking |
| /// all future get_updates. |
| pub async fn get_update(&self) -> Result<Vec<AccessPointState>, Error> { |
| // Initialize the update listener if it has not been initialized. |
| let listener = self.update_listener.take(); |
| let mut update_listener = if listener.is_none() { |
| Self::init_listener() |
| } else { |
| listener.ok_or(format_err!("failed to set update listener of facade")) |
| }?; |
| |
| if let Some(update_request) = update_listener.try_next().await? { |
| let update = update_request.into_on_access_point_state_update(); |
| let (update, responder) = match update { |
| Some((update, responder)) => (update, responder), |
| None => return Err(format_err!("Client provider produced invalid update.")).into(), |
| }; |
| // Ack the update. |
| responder.send().map_err(|e| format_err!("failed to ack update: {}", e))?; |
| // Put the update listener back in the facade |
| self.update_listener.set(Some(update_listener)); |
| |
| let update = update.into_iter().map(|update| AccessPointState::from(update)).collect(); |
| Ok(update) |
| } else { |
| self.update_listener.set(Some(update_listener)); |
| Err(format_err!("update listener's next update is None")) |
| } |
| } |
| } |