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    HangingGetObserver, RequestType, ResponseType, UsagePublisher,
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 = HangingGetObserver<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, observer: SubscriberObject) -> bool {
62        let (usage_responder, responder) = observer.into_parts();
63        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
64        if let Err(e) = responder.send(&KeyboardSettings::from(*info)) {
65            log::warn!("Failed to respond to watch request: {e:?}");
66            return false;
67        }
68        true
69    }
70
71    pub fn handle_stream(&mut self, mut stream: KeyboardRequestStream) {
72        let request_handler = RequestHandler {
73            subscriber: self.hanging_get.new_subscriber(),
74            controller_tx: self.controller_tx.clone(),
75            usage_publisher: self.usage_publisher.clone(),
76        };
77        fasync::Task::local(async move {
78            while let Some(Ok(request)) = stream.next().await {
79                request_handler.handle_request(request).await;
80            }
81        })
82        .detach();
83    }
84}
85
86#[derive(Debug)]
87enum HandlerError {
88    AlreadySubscribed,
89    InvalidArgument(
90        // Error used by Debug impl for inspect logs.
91        #[allow(dead_code)] Error,
92    ),
93    ControllerStopped,
94    Controller(KeyboardError),
95}
96
97impl From<&HandlerError> for ResponseType {
98    fn from(error: &HandlerError) -> Self {
99        match error {
100            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
101            HandlerError::InvalidArgument(_) => ResponseType::InvalidArgument,
102            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
103            HandlerError::Controller(e) => ResponseType::from(e),
104        }
105    }
106}
107
108struct RequestHandler {
109    subscriber: Subscriber,
110    controller_tx: UnboundedSender<Request>,
111    usage_publisher: UsagePublisher<KeyboardInfo>,
112}
113
114impl RequestHandler {
115    async fn handle_request(&self, request: KeyboardRequest) {
116        match request {
117            KeyboardRequest::Watch { responder } => {
118                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
119                let observer = HangingGetObserver::new(usage_res, responder);
120                if let Err(observer) = self.subscriber.register2(observer) {
121                    let e = HandlerError::AlreadySubscribed;
122                    let (usage_res, responder) = observer.into_parts();
123                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
124                    drop(responder);
125                }
126            }
127            KeyboardRequest::Set { settings, responder } => {
128                let usage_res = self
129                    .usage_publisher
130                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
131                if let Err(e) = self.set(settings).await {
132                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
133                    let _ = responder.send(Err(SettingsError::Failed));
134                } else {
135                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
136                    let _ = responder.send(Ok(()));
137                }
138            }
139        }
140    }
141
142    async fn set(&self, settings: KeyboardSettings) -> Result<(), HandlerError> {
143        let (set_tx, set_rx) = oneshot::channel();
144        let info = to_request(settings).map_err(HandlerError::InvalidArgument)?;
145        self.controller_tx
146            .unbounded_send(Request::Set(info, set_tx))
147            .map_err(|_| HandlerError::ControllerStopped)?;
148        set_rx
149            .await
150            .map_err(|_| HandlerError::ControllerStopped)
151            .and_then(|res| res.map_err(HandlerError::Controller))
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[fuchsia::test]
160    fn test_request_from_settings_empty() {
161        let info = to_request(KeyboardSettings::default()).unwrap();
162        assert!(matches!(info, KeyboardInfo { keymap: None, autorepeat: None }));
163    }
164
165    #[fuchsia::test]
166    fn test_request_from_settings_error() {
167        let keyboard_settings = KeyboardSettings {
168            keymap: Some(fidl_fuchsia_input::KeymapId::unknown()),
169            ..Default::default()
170        };
171
172        assert!(
173            format!("{:?}", to_request(keyboard_settings).unwrap_err())
174                .contains("Received an invalid keymap id:")
175        );
176    }
177
178    #[fuchsia::test]
179    fn test_request_from_settings() {
180        use crate::types::Autorepeat;
181
182        const KEYMAP_ID: fidl_fuchsia_input::KeymapId = fidl_fuchsia_input::KeymapId::FrAzerty;
183        const DELAY: i64 = 1;
184        const PERIOD: i64 = 2;
185        const AUTOREPEAT: fidl_fuchsia_settings::Autorepeat =
186            fidl_fuchsia_settings::Autorepeat { delay: DELAY, period: PERIOD };
187
188        let keyboard_settings = KeyboardSettings {
189            keymap: Some(KEYMAP_ID),
190            autorepeat: Some(AUTOREPEAT),
191            ..Default::default()
192        };
193
194        let info = to_request(keyboard_settings).unwrap();
195        assert!(matches!(
196            info,
197            KeyboardInfo {
198                keymap: Some(KeymapId::FrAzerty),
199                autorepeat: Some(Autorepeat { delay: DELAY, period: PERIOD }),
200            },
201        ));
202    }
203}