Skip to main content

settings_keyboard/
keyboard_fidl_handler.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.
4
5use crate::keyboard_controller::{KeyboardController, KeyboardError, Request};
6use crate::types::{Autorepeat, KeyboardInfo, KeymapId};
7use anyhow::Error;
8use async_utils::hanging_get::server;
9use fidl_fuchsia_settings::{
10    Error as SettingsError, KeyboardRequest, KeyboardRequestStream, KeyboardSettings,
11    KeyboardWatchResponder,
12};
13use fuchsia_async as fasync;
14use futures::StreamExt;
15use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
16use futures::channel::oneshot;
17use settings_common::inspect::event::{
18    RequestType, ResponseType, UsagePublisher, UsageResponsePublisher,
19};
20
21impl From<KeyboardInfo> for KeyboardSettings {
22    fn from(info: KeyboardInfo) -> Self {
23        KeyboardSettings {
24            keymap: info.keymap.map(KeymapId::into),
25            autorepeat: info.autorepeat.map(Autorepeat::into),
26            ..Default::default()
27        }
28    }
29}
30
31fn to_request(settings: KeyboardSettings) -> Result<KeyboardInfo, Error> {
32    let autorepeat: Option<Autorepeat> = settings.autorepeat.map(|src| src.into());
33    let keymap = settings.keymap.map(KeymapId::try_from).transpose()?;
34    Ok(KeyboardInfo { keymap, autorepeat })
35}
36
37pub(super) type SubscriberObject = (UsageResponsePublisher<KeyboardInfo>, KeyboardWatchResponder);
38type HangingGetFn = fn(&KeyboardInfo, SubscriberObject) -> bool;
39pub(super) type HangingGet = server::HangingGet<KeyboardInfo, SubscriberObject, HangingGetFn>;
40pub(super) type Publisher = server::Publisher<KeyboardInfo, SubscriberObject, HangingGetFn>;
41pub(super) type Subscriber = server::Subscriber<KeyboardInfo, SubscriberObject, HangingGetFn>;
42
43pub struct KeyboardFidlHandler {
44    hanging_get: HangingGet,
45    controller_tx: UnboundedSender<Request>,
46    usage_publisher: UsagePublisher<KeyboardInfo>,
47}
48
49impl KeyboardFidlHandler {
50    pub(crate) fn new(
51        keyboard_controller: &mut KeyboardController,
52        usage_publisher: UsagePublisher<KeyboardInfo>,
53        initial_value: KeyboardInfo,
54    ) -> (Self, UnboundedReceiver<Request>) {
55        let hanging_get = HangingGet::new(initial_value, Self::hanging_get);
56        keyboard_controller.register_publisher(hanging_get.new_publisher());
57        let (controller_tx, controller_rx) = mpsc::unbounded();
58        (Self { hanging_get, controller_tx, usage_publisher }, controller_rx)
59    }
60
61    fn hanging_get(info: &KeyboardInfo, (usage_responder, responder): SubscriberObject) -> bool {
62        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
63        if let Err(e) = responder.send(&KeyboardSettings::from(*info)) {
64            log::warn!("Failed to respond to watch request: {e:?}");
65            return false;
66        }
67        true
68    }
69
70    pub fn handle_stream(&mut self, mut stream: KeyboardRequestStream) {
71        let request_handler = RequestHandler {
72            subscriber: self.hanging_get.new_subscriber(),
73            controller_tx: self.controller_tx.clone(),
74            usage_publisher: self.usage_publisher.clone(),
75        };
76        fasync::Task::local(async move {
77            while let Some(Ok(request)) = stream.next().await {
78                request_handler.handle_request(request).await;
79            }
80        })
81        .detach();
82    }
83}
84
85#[derive(Debug)]
86enum HandlerError {
87    AlreadySubscribed,
88    InvalidArgument(
89        // Error used by Debug impl for inspect logs.
90        #[allow(dead_code)] Error,
91    ),
92    ControllerStopped,
93    Controller(KeyboardError),
94}
95
96impl From<&HandlerError> for ResponseType {
97    fn from(error: &HandlerError) -> Self {
98        match error {
99            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
100            HandlerError::InvalidArgument(_) => ResponseType::InvalidArgument,
101            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
102            HandlerError::Controller(e) => ResponseType::from(e),
103        }
104    }
105}
106
107struct RequestHandler {
108    subscriber: Subscriber,
109    controller_tx: UnboundedSender<Request>,
110    usage_publisher: UsagePublisher<KeyboardInfo>,
111}
112
113impl RequestHandler {
114    async fn handle_request(&self, request: KeyboardRequest) {
115        match request {
116            KeyboardRequest::Watch { responder } => {
117                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
118                if let Err((usage_res, responder)) =
119                    self.subscriber.register2((usage_res, responder))
120                {
121                    let e = HandlerError::AlreadySubscribed;
122                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
123                    drop(responder);
124                }
125            }
126            KeyboardRequest::Set { settings, responder } => {
127                let usage_res = self
128                    .usage_publisher
129                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
130                if let Err(e) = self.set(settings).await {
131                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
132                    let _ = responder.send(Err(SettingsError::Failed));
133                } else {
134                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
135                    let _ = responder.send(Ok(()));
136                }
137            }
138        }
139    }
140
141    async fn set(&self, settings: KeyboardSettings) -> Result<(), HandlerError> {
142        let (set_tx, set_rx) = oneshot::channel();
143        let info = to_request(settings).map_err(HandlerError::InvalidArgument)?;
144        self.controller_tx
145            .unbounded_send(Request::Set(info, set_tx))
146            .map_err(|_| HandlerError::ControllerStopped)?;
147        set_rx
148            .await
149            .map_err(|_| HandlerError::ControllerStopped)
150            .and_then(|res| res.map_err(HandlerError::Controller))
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[fuchsia::test]
159    fn test_request_from_settings_empty() {
160        let info = to_request(KeyboardSettings::default()).unwrap();
161        assert!(matches!(info, KeyboardInfo { keymap: None, autorepeat: None }));
162    }
163
164    #[fuchsia::test]
165    fn test_request_from_settings_error() {
166        let keyboard_settings = KeyboardSettings {
167            keymap: Some(fidl_fuchsia_input::KeymapId::unknown()),
168            ..Default::default()
169        };
170
171        assert!(
172            format!("{:?}", to_request(keyboard_settings).unwrap_err())
173                .contains("Received an invalid keymap id:")
174        );
175    }
176
177    #[fuchsia::test]
178    fn test_request_from_settings() {
179        use crate::types::Autorepeat;
180
181        const KEYMAP_ID: fidl_fuchsia_input::KeymapId = fidl_fuchsia_input::KeymapId::FrAzerty;
182        const DELAY: i64 = 1;
183        const PERIOD: i64 = 2;
184        const AUTOREPEAT: fidl_fuchsia_settings::Autorepeat =
185            fidl_fuchsia_settings::Autorepeat { delay: DELAY, period: PERIOD };
186
187        let keyboard_settings = KeyboardSettings {
188            keymap: Some(KEYMAP_ID),
189            autorepeat: Some(AUTOREPEAT),
190            ..Default::default()
191        };
192
193        let info = to_request(keyboard_settings).unwrap();
194        assert!(matches!(
195            info,
196            KeyboardInfo {
197                keymap: Some(KeymapId::FrAzerty),
198                autorepeat: Some(Autorepeat { delay: DELAY, period: PERIOD }),
199            },
200        ));
201    }
202}