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 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 #[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}