settings_keyboard/
keyboard_fidl_handler.rs1use 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 #[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}