settings/setup/
setup_fidl_handler.rs

1// Copyright 2019 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 super::setup_controller::{Request, SetupController, SetupError};
6use crate::setup::types::{ConfigurationInterfaceFlags, SetupInfo};
7use async_utils::hanging_get::server;
8use fidl_fuchsia_settings::{
9    Error as SettingsError, SetupRequest, SetupRequestStream, SetupSettings, SetupWatchResponder,
10};
11use fuchsia_async as fasync;
12use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
13use futures::channel::oneshot;
14use futures::StreamExt;
15use settings_common::inspect::event::{
16    RequestType, ResponseType, UsagePublisher, UsageResponsePublisher,
17};
18
19impl From<fidl_fuchsia_settings::ConfigurationInterfaces> for ConfigurationInterfaceFlags {
20    fn from(interfaces: fidl_fuchsia_settings::ConfigurationInterfaces) -> Self {
21        let mut flags = ConfigurationInterfaceFlags::empty();
22
23        if interfaces.intersects(fidl_fuchsia_settings::ConfigurationInterfaces::ETHERNET) {
24            flags |= ConfigurationInterfaceFlags::ETHERNET;
25        }
26
27        if interfaces.intersects(fidl_fuchsia_settings::ConfigurationInterfaces::WIFI) {
28            flags |= ConfigurationInterfaceFlags::WIFI;
29        }
30
31        flags
32    }
33}
34
35impl From<ConfigurationInterfaceFlags> for fidl_fuchsia_settings::ConfigurationInterfaces {
36    fn from(flags: ConfigurationInterfaceFlags) -> Self {
37        let mut interfaces = fidl_fuchsia_settings::ConfigurationInterfaces::empty();
38
39        if flags.intersects(ConfigurationInterfaceFlags::ETHERNET) {
40            interfaces |= fidl_fuchsia_settings::ConfigurationInterfaces::ETHERNET;
41        }
42
43        if flags.intersects(ConfigurationInterfaceFlags::WIFI) {
44            interfaces |= fidl_fuchsia_settings::ConfigurationInterfaces::WIFI;
45        }
46
47        interfaces
48    }
49}
50
51impl From<SetupInfo> for SetupSettings {
52    fn from(info: SetupInfo) -> Self {
53        let mut settings = SetupSettings::default();
54        let interfaces =
55            fidl_fuchsia_settings::ConfigurationInterfaces::from(info.configuration_interfaces);
56
57        if !interfaces.is_empty() {
58            settings.enabled_configuration_interfaces = Some(interfaces);
59        }
60
61        settings
62    }
63}
64
65pub(super) type SubscriberObject = (UsageResponsePublisher<SetupInfo>, SetupWatchResponder);
66type InfoHangingFn = fn(&SetupInfo, SubscriberObject) -> bool;
67pub(super) type InfoHangingGet = server::HangingGet<SetupInfo, SubscriberObject, InfoHangingFn>;
68pub(super) type InfoPublisher = server::Publisher<SetupInfo, SubscriberObject, InfoHangingFn>;
69pub(super) type InfoSubscriber = server::Subscriber<SetupInfo, SubscriberObject, InfoHangingFn>;
70
71pub struct SetupFidlHandler {
72    info_hanging_get: InfoHangingGet,
73    controller_tx: UnboundedSender<Request>,
74    usage_publisher: UsagePublisher<SetupInfo>,
75}
76
77impl SetupFidlHandler {
78    pub(super) fn new(
79        setup_controller: &mut SetupController,
80        usage_publisher: UsagePublisher<SetupInfo>,
81        initial_value: SetupInfo,
82    ) -> (Self, UnboundedReceiver<Request>) {
83        let info_hanging_get = InfoHangingGet::new(initial_value, Self::hanging_get);
84        setup_controller.register_publisher(info_hanging_get.new_publisher());
85        let (controller_tx, controller_rx) = mpsc::unbounded();
86        (Self { info_hanging_get, controller_tx, usage_publisher }, controller_rx)
87    }
88
89    fn hanging_get(info: &SetupInfo, (usage_responder, responder): SubscriberObject) -> bool {
90        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
91        if let Err(e) = responder.send(&SetupSettings::from(*info)) {
92            log::warn!("Failed to respond to watch request: {e:?}");
93            return false;
94        }
95        true
96    }
97
98    pub fn handle_stream(&mut self, mut stream: SetupRequestStream) {
99        let request_handler = RequestHandler {
100            subscriber: self.info_hanging_get.new_subscriber(),
101            controller_tx: self.controller_tx.clone(),
102            usage_publisher: self.usage_publisher.clone(),
103        };
104        fasync::Task::local(async move {
105            while let Some(Ok(request)) = stream.next().await {
106                request_handler.handle_request(request).await;
107            }
108        })
109        .detach();
110    }
111}
112
113#[derive(Debug)]
114enum HandlerError {
115    AlreadySubscribed,
116    MissingConfig,
117    ControllerStopped,
118    Controller(SetupError),
119}
120
121impl From<&HandlerError> for ResponseType {
122    fn from(error: &HandlerError) -> Self {
123        match error {
124            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
125            HandlerError::MissingConfig => ResponseType::InvalidArgument,
126            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
127            HandlerError::Controller(e) => ResponseType::from(e),
128        }
129    }
130}
131
132struct RequestHandler {
133    subscriber: InfoSubscriber,
134    controller_tx: UnboundedSender<Request>,
135    usage_publisher: UsagePublisher<SetupInfo>,
136}
137
138impl RequestHandler {
139    async fn handle_request(&self, request: SetupRequest) {
140        match request {
141            SetupRequest::Watch { responder } => {
142                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
143                if let Err((usage_res, responder)) =
144                    self.subscriber.register2((usage_res, responder))
145                {
146                    let e = HandlerError::AlreadySubscribed;
147                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
148                    drop(responder);
149                }
150            }
151            SetupRequest::Set { settings, reboot_device, responder } => {
152                let usage_res = self.usage_publisher.request(
153                    format!("Set{{settings:{settings:?},reboot_device:{reboot_device:?}}}"),
154                    RequestType::Set,
155                );
156                if let Err(e) = self.set(settings, reboot_device).await {
157                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
158                    let _ = responder.send(Err(SettingsError::Failed));
159                } else {
160                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
161                    let _ = responder.send(Ok(()));
162                }
163            }
164        }
165    }
166
167    async fn set(&self, settings: SetupSettings, reboot_device: bool) -> Result<(), HandlerError> {
168        let Some(enabled_config_interfaces) = settings.enabled_configuration_interfaces else {
169            return Err(HandlerError::MissingConfig);
170        };
171        let (set_tx, set_rx) = oneshot::channel();
172        self.controller_tx
173            .unbounded_send(Request::Set(
174                ConfigurationInterfaceFlags::from(enabled_config_interfaces),
175                reboot_device,
176                set_tx,
177            ))
178            .map_err(|_| HandlerError::ControllerStopped)?;
179        set_rx
180            .await
181            .map_err(|_| HandlerError::ControllerStopped)
182            .and_then(|res| res.map_err(HandlerError::Controller))
183    }
184}