focus_chain_provider/
lib.rs
1mod instance_counter;
14
15use crate::instance_counter::InstanceCounter;
16use async_utils::hanging_get::server as hanging_get;
17use fidl_fuchsia_ui_focus::{
18 self as focus, FocusChainProviderWatchFocusKoidChainResponder, FocusKoidChain,
19};
20use fidl_fuchsia_ui_focus_ext::FocusChainExt;
21use fuchsia_async as fasync;
22use futures::lock::Mutex;
23use futures::stream::TryStreamExt;
24use futures::TryFutureExt;
25use log::error;
26use std::sync::Arc;
27
28type HangingGetNotifyFn =
30 Box<dyn Fn(&FocusKoidChain, FocusChainProviderWatchFocusKoidChainResponder) -> bool + Send>;
31type HangingGetBroker = hanging_get::HangingGet<
32 FocusKoidChain,
33 FocusChainProviderWatchFocusKoidChainResponder,
34 HangingGetNotifyFn,
35>;
36type HangingGetPublisher = hanging_get::Publisher<
37 FocusKoidChain,
38 FocusChainProviderWatchFocusKoidChainResponder,
39 HangingGetNotifyFn,
40>;
41
42pub fn make_publisher_and_stream_handler(
45) -> (FocusChainProviderPublisher, FocusChainProviderRequestStreamHandler) {
46 let notify_fn: HangingGetNotifyFn =
47 Box::new(|focus_koid_chain, responder| match responder.send(&focus_koid_chain) {
48 Ok(()) => true,
49 Err(e) => {
50 error!("Failed to send focus chain to client: {e:?}");
51 false
52 }
53 });
54
55 let broker = hanging_get::HangingGet::new(FocusKoidChain::default(), notify_fn);
56 let publisher = broker.new_publisher();
57 let subscriber_counter = InstanceCounter::new();
58
59 (
60 FocusChainProviderPublisher { publisher },
61 FocusChainProviderRequestStreamHandler {
62 broker: Arc::new(Mutex::new(broker)),
63 subscriber_counter,
64 },
65 )
66}
67
68#[derive(Clone)]
73pub struct FocusChainProviderPublisher {
74 publisher: HangingGetPublisher,
75}
76
77impl FocusChainProviderPublisher {
78 pub fn set_state_and_notify_if_changed<C: FocusChainExt>(
83 &self,
84 new_state: &C,
85 ) -> Result<(), zx::Status> {
86 let new_state = new_state.to_focus_koid_chain()?;
87 let publisher = self.publisher.clone();
88 publisher.update(|old_state| match old_state.as_ref().unwrap().equivalent(&new_state) {
89 Ok(true) => false,
90 Ok(false) => {
91 *old_state = Some(new_state);
92 true
93 }
94 Err(e) => unreachable!("Unexpected state {e:?}"),
95 });
96 Ok(())
97 }
98
99 pub fn set_state_and_notify_always<C: FocusChainExt>(
103 &self,
104 new_state: &C,
105 ) -> Result<(), zx::Status> {
106 let publisher = self.publisher.clone();
107 publisher.set(new_state.to_focus_koid_chain()?);
108 Ok(())
109 }
110}
111
112#[derive(Clone)]
117pub struct FocusChainProviderRequestStreamHandler {
118 broker: Arc<Mutex<HangingGetBroker>>,
119 subscriber_counter: InstanceCounter,
120}
121
122impl FocusChainProviderRequestStreamHandler {
123 #[must_use = "The Task must be retained or `.detach()`ed."]
126 pub fn handle_request_stream(
127 &self,
128 mut stream: focus::FocusChainProviderRequestStream,
129 ) -> fasync::Task<()> {
130 let broker = self.broker.clone();
131 let counter = self.subscriber_counter.clone();
132 fasync::Task::local(
133 async move {
134 let subscriber = broker.lock().await.new_subscriber();
135 let _count_token = counter.make_token();
137 while let Some(req) = stream.try_next().await? {
138 match req {
139 focus::FocusChainProviderRequest::WatchFocusKoidChain {
140 payload: _payload,
141 responder,
142 } => {
143 subscriber.register(responder)?;
144 }
145 }
146 }
147 Ok(())
148 }
149 .unwrap_or_else(|e: anyhow::Error| error!("{e:#?}")),
150 )
151 }
152
153 pub fn subscriber_count(&self) -> usize {
155 self.subscriber_counter.count()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use fidl_fuchsia_ui_focus_test_helpers::make_focus_chain;
163
164 #[fuchsia::test]
166 async fn smoke_test() {
167 let (publisher, stream_handler) = super::make_publisher_and_stream_handler();
168 let (client, stream) =
169 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
170 stream_handler.handle_request_stream(stream).detach();
171 assert_eq!(stream_handler.subscriber_count(), 0);
172
173 let received_focus_koid_chain = client
174 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
175 .await
176 .expect("watch_focus_koid_chain");
177 assert!(received_focus_koid_chain.equivalent(&FocusKoidChain::default()).unwrap());
178 assert_eq!(stream_handler.subscriber_count(), 1);
179
180 let (served_focus_chain, _view_ref_controls) = make_focus_chain(2);
181 publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
182 let received_focus_koid_chain = client
183 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
184 .await
185 .expect("watch_focus_chain");
186 assert!(received_focus_koid_chain.equivalent(&served_focus_chain).unwrap());
187 assert_eq!(stream_handler.subscriber_count(), 1);
188 }
189
190 #[fuchsia::test]
191 async fn only_newest_value_is_sent() {
192 let (publisher, stream_handler) = super::make_publisher_and_stream_handler();
193 let (client, stream) =
194 fidl::endpoints::create_proxy_and_stream::<focus::FocusChainProviderMarker>();
195 stream_handler.handle_request_stream(stream).detach();
196
197 let received_focus_koid_chain = client
198 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
199 .await
200 .expect("watch_focus_koid_chain");
201 assert!(received_focus_koid_chain.equivalent(&FocusKoidChain::default()).unwrap());
202
203 let (served_focus_chain, _view_ref_controls) = make_focus_chain(2);
204 publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
205
206 let (served_focus_chain, _view_ref_controls) = make_focus_chain(3);
207 publisher.set_state_and_notify_if_changed(&served_focus_chain).expect("set_state");
208
209 let received_focus_koid_chain = client
210 .watch_focus_koid_chain(&focus::FocusChainProviderWatchFocusKoidChainRequest::default())
211 .await
212 .expect("watch_focus_chain");
213 assert_eq!(received_focus_koid_chain.len(), 3);
214 assert!(received_focus_koid_chain.equivalent(&served_focus_chain).unwrap());
215 }
216}