1#![cfg(fuchsia_api_level_at_least = "HEAD")]
5use crate::input_device;
6use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
7use anyhow::{Context, Error};
8use async_trait::async_trait;
9use async_utils::hanging_get::server::{HangingGet, Publisher, Subscriber};
10use fidl_fuchsia_input_interaction::{
11 NotifierRequest, NotifierRequestStream, NotifierWatchStateResponder, State,
12};
13use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorProxy};
14use fuchsia_async::{Task, Timer};
15use fuchsia_component::client::connect_to_protocol;
16
17use fuchsia_inspect::health::Reporter;
18use futures::StreamExt;
19use std::cell::{Cell, RefCell};
20use std::rc::Rc;
21
22struct LeaseHolder {
23 activity_governor: ActivityGovernorProxy,
24 wake_lease: Option<zx::EventPair>,
25}
26
27impl LeaseHolder {
28 async fn new(activity_governor: ActivityGovernorProxy) -> Result<Self, Error> {
29 let wake_lease = activity_governor
30 .take_wake_lease("scene_manager")
31 .await
32 .context("cannot get wake lease from SAG")?;
33 log::info!("InteractionStateHandler created a wake lease during initialization.");
34
35 Ok(Self { activity_governor, wake_lease: Some(wake_lease) })
36 }
37
38 async fn create_lease(&mut self) -> Result<(), Error> {
39 if self.wake_lease.is_some() {
40 log::warn!(
41 "InteractionStateHandler already held a wake lease when trying to create one, please investigate."
42 );
43 return Ok(());
44 }
45
46 let wake_lease = self
47 .activity_governor
48 .take_wake_lease("scene_manager")
49 .await
50 .context("cannot get wake lease from SAG")?;
51 self.wake_lease = Some(wake_lease);
52 log::info!(
53 "InteractionStateHandler created a wake lease due to receiving recent user input."
54 );
55
56 Ok(())
57 }
58
59 fn drop_lease(&mut self) {
60 if let Some(lease) = self.wake_lease.take() {
61 log::info!(
62 "InteractionStateHandler is dropping the wake lease due to not receiving any recent user input."
63 );
64 std::mem::drop(lease);
65 } else {
66 log::warn!(
67 "InteractionStateHandler was not holding a wake lease when trying to drop one, please investigate."
68 );
69 }
70 }
71
72 #[cfg(test)]
73 fn is_holding_lease(&self) -> bool {
74 self.wake_lease.is_some()
75 }
76}
77
78pub type NotifyFn = Box<dyn Fn(&State, NotifierWatchStateResponder) -> bool>;
79pub type InteractionStatePublisher = Publisher<State, NotifierWatchStateResponder, NotifyFn>;
80pub type InteractionStateSubscriber = Subscriber<State, NotifierWatchStateResponder, NotifyFn>;
81type InteractionHangingGet = HangingGet<State, NotifierWatchStateResponder, NotifyFn>;
82
83pub struct InteractionStateHandler {
85 idle_threshold_ms: zx::MonotonicDuration,
88
89 idle_transition_task: Cell<Option<Task<()>>>,
91
92 last_event_time: RefCell<zx::MonotonicInstant>,
94
95 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
99
100 state_publisher: InteractionStatePublisher,
102
103 pub inspect_status: InputHandlerStatus,
105}
106
107impl InteractionStateHandler {
108 pub async fn new(
111 idle_threshold_ms: zx::MonotonicDuration,
112 input_handlers_node: &fuchsia_inspect::Node,
113 state_publisher: InteractionStatePublisher,
114 suspend_enabled: bool,
115 ) -> Rc<Self> {
116 log::info!(
117 "InteractionStateHandler is initialized with idle_threshold_ms: {:?}",
118 idle_threshold_ms.into_millis()
119 );
120
121 let inspect_status =
122 InputHandlerStatus::new(input_handlers_node, "interaction_state_handler", false);
123
124 let lease_holder = match suspend_enabled {
125 true => {
126 let activity_governor = connect_to_protocol::<ActivityGovernorMarker>()
127 .expect("connect to fuchsia.power.system.ActivityGovernor");
128 match LeaseHolder::new(activity_governor).await {
129 Ok(holder) => Some(Rc::new(RefCell::new(holder))),
130 Err(e) => {
131 log::error!(
132 "Unable to integrate with power, system may incorrectly enter suspend: {:?}",
133 e
134 );
135 None
136 }
137 }
138 }
139 false => None,
140 };
141
142 Rc::new(Self::new_internal(
143 idle_threshold_ms,
144 zx::MonotonicInstant::get(),
145 lease_holder,
146 inspect_status,
147 state_publisher,
148 ))
149 }
150
151 #[cfg(test)]
152 async fn new_for_test(
154 idle_threshold_ms: zx::MonotonicDuration,
155 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
156 state_publisher: InteractionStatePublisher,
157 ) -> Rc<Self> {
158 fuchsia_async::TestExecutor::advance_to(zx::MonotonicInstant::ZERO.into()).await;
159
160 let inspector = fuchsia_inspect::Inspector::default();
161 let test_node = inspector.root().create_child("test_node");
162 let inspect_status = InputHandlerStatus::new(
163 &test_node,
164 "interaction_state_handler",
165 false,
166 );
167 Rc::new(Self::new_internal(
168 idle_threshold_ms,
169 zx::MonotonicInstant::ZERO,
170 lease_holder,
171 inspect_status,
172 state_publisher,
173 ))
174 }
175
176 fn new_internal(
177 idle_threshold_ms: zx::MonotonicDuration,
178 initial_timestamp: zx::MonotonicInstant,
179 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
180 inspect_status: InputHandlerStatus,
181 state_publisher: InteractionStatePublisher,
182 ) -> Self {
183 let task = Self::create_idle_transition_task(
184 initial_timestamp + idle_threshold_ms,
185 state_publisher.clone(),
186 lease_holder.clone(),
187 );
188
189 Self {
190 idle_threshold_ms,
191 idle_transition_task: Cell::new(Some(task)),
192 last_event_time: RefCell::new(initial_timestamp),
193 lease_holder,
194 state_publisher,
195 inspect_status,
196 }
197 }
198
199 async fn transition_to_active(
200 state_publisher: &InteractionStatePublisher,
201 lease_holder: &Option<Rc<RefCell<LeaseHolder>>>,
202 ) {
203 if let Some(holder) = lease_holder {
204 if let Err(e) = holder.borrow_mut().create_lease().await {
205 log::warn!(
206 "Unable to create lease, system may incorrectly go into suspend: {:?}",
207 e
208 );
209 };
210 }
211 state_publisher.set(State::Active);
212 }
213
214 fn create_idle_transition_task(
215 timeout: zx::MonotonicInstant,
216 state_publisher: InteractionStatePublisher,
217 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
218 ) -> Task<()> {
219 Task::local(async move {
220 Timer::new(timeout).await;
221 lease_holder.and_then(|holder| Some(holder.borrow_mut().drop_lease()));
222 state_publisher.set(State::Idle);
223 })
224 }
225
226 async fn transition_to_idle_after_new_time(&self, event_time: zx::MonotonicInstant) {
227 if *self.last_event_time.borrow() > event_time {
228 return;
229 }
230
231 *self.last_event_time.borrow_mut() = event_time;
232 if let Some(t) = self.idle_transition_task.take() {
233 if let Some(()) = t.abort().await {
236 Self::transition_to_active(&self.state_publisher, &self.lease_holder).await;
237 }
238 }
239
240 self.idle_transition_task.set(Some(Self::create_idle_transition_task(
241 event_time + self.idle_threshold_ms,
242 self.state_publisher.clone(),
243 self.lease_holder.clone(),
244 )));
245 }
246
247 #[cfg(test)]
248 fn is_holding_lease(&self) -> bool {
249 if let Some(holder) = &self.lease_holder {
250 return holder.borrow().is_holding_lease();
251 }
252
253 false
254 }
255}
256
257pub async fn handle_interaction_notifier_request_stream(
262 mut stream: NotifierRequestStream,
263 subscriber: InteractionStateSubscriber,
264) -> Result<(), Error> {
265 while let Some(notifier_request) = stream.next().await {
266 let NotifierRequest::WatchState { responder } = notifier_request?;
267 subscriber.register(responder)?;
268 }
269
270 Ok(())
271}
272
273pub fn init_interaction_hanging_get() -> InteractionHangingGet {
274 let notify_fn: NotifyFn = Box::new(|state, responder| {
275 if responder.send(*state).is_err() {
276 log::info!("Failed to send user input interaction state");
277 }
278
279 true
280 });
281
282 let initial_state = State::Active;
283 InteractionHangingGet::new(initial_state, notify_fn)
284}
285
286#[async_trait(?Send)]
287impl UnhandledInputHandler for InteractionStateHandler {
288 async fn handle_unhandled_input_event(
291 self: Rc<Self>,
292 unhandled_input_event: input_device::UnhandledInputEvent,
293 ) -> Vec<input_device::InputEvent> {
294 fuchsia_trace::duration!(c"input", c"interaction_state_handler");
295 match unhandled_input_event.device_event {
296 input_device::InputDeviceEvent::ConsumerControls(_)
297 | input_device::InputDeviceEvent::Mouse(_)
298 | input_device::InputDeviceEvent::TouchScreen(_) => {
299 fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
300 let event_time = unhandled_input_event.event_time.clamp(
306 zx::MonotonicInstant::ZERO,
307 fuchsia_async::MonotonicInstant::now().into_zx(),
308 );
309
310 self.inspect_status.count_received_event(&event_time);
311 self.transition_to_idle_after_new_time(event_time).await;
312 }
313 _ => {}
314 }
315
316 vec![input_device::InputEvent::from(unhandled_input_event)]
317 }
318
319 fn set_handler_healthy(self: std::rc::Rc<Self>) {
320 self.inspect_status.health_node.borrow_mut().set_ok();
321 }
322
323 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
324 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::mouse_binding;
332 use crate::testing_utilities::{
333 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
334 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
335 get_touch_screen_device_descriptor,
336 };
337 use crate::utils::Position;
338 use assert_matches::assert_matches;
339 use async_utils::hanging_get::client::HangingGetStream;
340 use fidl::endpoints::create_proxy_and_stream;
341 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
342 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
343 use fidl_fuchsia_ui_input::PointerEventPhase;
344 use fuchsia_async::TestExecutor;
345 use futures::pin_mut;
346 use maplit::hashmap;
347 use std::collections::HashSet;
348 use std::task::Poll;
349 use test_case::test_case;
350
351 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
352
353 async fn create_interaction_state_handler_and_notifier_proxy(
354 suspend_enabled: bool,
355 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
356 let mut interaction_hanging_get = init_interaction_hanging_get();
357
358 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
359 let stream_fut = handle_interaction_notifier_request_stream(
360 notifier_stream,
361 interaction_hanging_get.new_subscriber(),
362 );
363
364 Task::local(async move {
365 if stream_fut.await.is_err() {
366 panic!("Failed to handle notifier request stream");
367 }
368 })
369 .detach();
370
371 let lease_holder = match suspend_enabled {
372 true => {
373 let holder = LeaseHolder::new(fake_activity_governor_server())
374 .await
375 .expect("create lease holder for test");
376 Some(Rc::new(RefCell::new(holder)))
377 }
378 false => None,
379 };
380
381 (
382 InteractionStateHandler::new_for_test(
383 ACTIVITY_TIMEOUT,
384 lease_holder,
385 interaction_hanging_get.new_publisher(),
386 )
387 .await,
388 notifier_proxy,
389 )
390 }
391
392 fn fake_activity_governor_server() -> ActivityGovernorProxy {
393 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
394 Task::local(async move {
395 while let Some(request) = stream.next().await {
396 match request {
397 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
398 let (_, fake_wake_lease) = zx::EventPair::create();
399 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
400 }
401 Ok(unexpected) => {
402 log::warn!(
403 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
404 );
405 }
406 Err(e) => {
407 log::warn!(
408 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
409 e
410 );
411 }
412 }
413 }
414 })
415 .detach();
416
417 proxy
418 }
419
420 #[test_case(true; "Suspend enabled")]
421 #[test_case(false; "Suspend disabled")]
422 #[fuchsia::test(allow_stalls = false)]
423 async fn notifier_sends_initial_state(suspend_enabled: bool) {
424 let (interaction_state_handler, notifier_proxy) =
425 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
426 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
427 assert_eq!(state, State::Active);
428 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
429 }
430
431 #[test_case(true; "Suspend enabled")]
432 #[test_case(false; "Suspend disabled")]
433 #[fuchsia::test]
434 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
435 let mut executor = TestExecutor::new_with_fake_time();
436
437 let handler_and_proxy_fut =
438 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
439 pin_mut!(handler_and_proxy_fut);
440 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
441 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
442 Poll::Ready((handler, proxy)) => (handler, proxy),
443 _ => panic!("Unable to create interaction state handler and proxy"),
444 };
445
446 let mut watch_state_stream =
448 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
449 let state_fut = watch_state_stream.next();
450 pin_mut!(state_fut);
451 let initial_state = executor.run_until_stalled(&mut state_fut);
452 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
453 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
454
455 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
457
458 let idle_state_fut = watch_state_stream.next();
460 pin_mut!(idle_state_fut);
461 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
462 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
463 assert_eq!(interaction_state_handler.is_holding_lease(), false);
464
465 Ok(())
466 }
467
468 #[test_case(true; "Suspend enabled")]
469 #[test_case(false; "Suspend disabled")]
470 #[fuchsia::test]
471 fn interaction_state_handler_drops_first_timer_on_activity(
472 suspend_enabled: bool,
473 ) -> Result<(), Error> {
474 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
488
489 let mut executor = TestExecutor::new_with_fake_time();
490
491 let handler_and_proxy_fut =
492 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
493 pin_mut!(handler_and_proxy_fut);
494 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
495 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
496 Poll::Ready((handler, proxy)) => (handler, proxy),
497 _ => panic!("Unable to create interaction state handler and proxy"),
498 };
499
500 let mut watch_state_stream =
502 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
503 let state_fut = watch_state_stream.next();
504 pin_mut!(state_fut);
505 let initial_state = executor.run_until_stalled(&mut state_fut);
506 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
507 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
508
509 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
511
512 let input_event =
514 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
515 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
516 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
517 ACTIVITY_TIMEOUT / 2,
518 )),
519 &consumer_controls_device_descriptor(),
520 ))
521 .unwrap();
522
523 let mut handle_event_fut =
524 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
525 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
526 assert!(handle_result.is_ready());
527
528 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
530
531 let watch_state_fut = watch_state_stream.next();
533 pin_mut!(watch_state_fut);
534 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
535 assert_matches!(watch_state_res, Poll::Pending);
536 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
537
538 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
540
541 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
543 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
544 assert_eq!(interaction_state_handler.is_holding_lease(), false);
545
546 Ok(())
547 }
548
549 #[test_case(true; "Suspend enabled")]
550 #[test_case(false; "Suspend disabled")]
551 #[fuchsia::test]
552 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
553 let mut executor = TestExecutor::new_with_fake_time();
554
555 let handler_and_proxy_fut =
556 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
557 pin_mut!(handler_and_proxy_fut);
558 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
559 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
560 Poll::Ready((handler, proxy)) => (handler, proxy),
561 _ => panic!("Unable to create interaction state handler and proxy"),
562 };
563
564 let mut watch_state_stream =
566 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
567 let state_fut = watch_state_stream.next();
568 pin_mut!(state_fut);
569 let watch_state_res = executor.run_until_stalled(&mut state_fut);
570 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
571 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
572
573 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
575
576 let input_event =
578 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
579 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
580 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
581 ACTIVITY_TIMEOUT / 2,
582 )),
583 &consumer_controls_device_descriptor(),
584 ))
585 .unwrap();
586
587 let mut handle_event_fut =
588 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
589 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
590 assert!(handle_result.is_ready());
591
592 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
594
595 let input_event =
597 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
598 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
599 zx::MonotonicInstant::ZERO,
600 &consumer_controls_device_descriptor(),
601 ))
602 .unwrap();
603
604 let mut handle_event_fut =
605 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
606 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
607 assert!(handle_result.is_ready());
608
609 let watch_state_fut = watch_state_stream.next();
612 pin_mut!(watch_state_fut);
613 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
614 assert_matches!(initial_state, Poll::Pending);
615 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
616
617 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
619
620 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
622 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
623 assert_eq!(interaction_state_handler.is_holding_lease(), false);
624
625 Ok(())
626 }
627
628 #[test_case(true; "Suspend enabled")]
629 #[test_case(false; "Suspend disabled")]
630 #[fuchsia::test]
631 fn notifier_sends_active_state_with_button_input_event(
632 suspend_enabled: bool,
633 ) -> Result<(), Error> {
634 let mut executor = TestExecutor::new_with_fake_time();
635
636 let handler_and_proxy_fut =
637 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
638 pin_mut!(handler_and_proxy_fut);
639 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
640 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
641 Poll::Ready((handler, proxy)) => (handler, proxy),
642 _ => panic!("Unable to create interaction state handler and proxy"),
643 };
644
645 let mut watch_state_stream =
647 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
648 let state_fut = watch_state_stream.next();
649 pin_mut!(state_fut);
650 let initial_state = executor.run_until_stalled(&mut state_fut);
651 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
652 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
653
654 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
656
657 let idle_state_fut = watch_state_stream.next();
659 pin_mut!(idle_state_fut);
660 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
661 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
662 assert_eq!(interaction_state_handler.is_holding_lease(), false);
663
664 let input_event =
666 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
667 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
668 zx::MonotonicInstant::get(),
669 &consumer_controls_device_descriptor(),
670 ))
671 .unwrap();
672
673 let mut handle_event_fut =
674 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
675 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
676
677 match handle_result {
679 Poll::Ready(res) => assert_matches!(
680 res.as_slice(),
681 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
682 ),
683 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
684 };
685
686 let active_state_fut = watch_state_stream.next();
688 pin_mut!(active_state_fut);
689 let initial_state = executor.run_until_stalled(&mut active_state_fut);
690 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
691 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
692
693 Ok(())
694 }
695
696 #[test_case(true; "Suspend enabled")]
697 #[test_case(false; "Suspend disabled")]
698 #[fuchsia::test]
699 fn notifier_sends_active_state_with_mouse_input_event(
700 suspend_enabled: bool,
701 ) -> Result<(), Error> {
702 let mut executor = TestExecutor::new_with_fake_time();
703
704 let handler_and_proxy_fut =
705 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
706 pin_mut!(handler_and_proxy_fut);
707 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
708 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
709 Poll::Ready((handler, proxy)) => (handler, proxy),
710 _ => panic!("Unable to create interaction state handler and proxy"),
711 };
712
713 let mut watch_state_stream =
715 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
716 let state_fut = watch_state_stream.next();
717 pin_mut!(state_fut);
718 let initial_state = executor.run_until_stalled(&mut state_fut);
719 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
720 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
721
722 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
724
725 let idle_state_fut = watch_state_stream.next();
727 pin_mut!(idle_state_fut);
728 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
729 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
730 assert_eq!(interaction_state_handler.is_holding_lease(), false);
731
732 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
734 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
735 None, None, None, mouse_binding::MousePhase::Down,
739 HashSet::new(),
740 HashSet::new(),
741 zx::MonotonicInstant::get(),
742 &get_mouse_device_descriptor(),
743 ))
744 .unwrap();
745
746 let mut handle_event_fut =
747 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
748 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
749
750 match handle_result {
752 Poll::Ready(res) => assert_matches!(
753 res.as_slice(),
754 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
755 ),
756 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
757 };
758
759 let active_state_fut = watch_state_stream.next();
761 pin_mut!(active_state_fut);
762 let initial_state = executor.run_until_stalled(&mut active_state_fut);
763 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
764 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
765
766 Ok(())
767 }
768
769 #[test_case(true; "Suspend enabled")]
770 #[test_case(false; "Suspend disabled")]
771 #[fuchsia::test]
772 fn notifier_sends_active_state_with_touch_input_event(
773 suspend_enabled: bool,
774 ) -> Result<(), Error> {
775 let mut executor = TestExecutor::new_with_fake_time();
776
777 let handler_and_proxy_fut =
778 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
779 pin_mut!(handler_and_proxy_fut);
780 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
781 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
782 Poll::Ready((handler, proxy)) => (handler, proxy),
783 _ => panic!("Unable to create interaction state handler and proxy"),
784 };
785
786 let mut watch_state_stream =
788 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
789 let state_fut = watch_state_stream.next();
790 pin_mut!(state_fut);
791 let initial_state = executor.run_until_stalled(&mut state_fut);
792 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
793 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
794
795 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
797
798 let idle_state_fut = watch_state_stream.next();
800 pin_mut!(idle_state_fut);
801 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
802 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
803 assert_eq!(interaction_state_handler.is_holding_lease(), false);
804
805 const TOUCH_ID: u32 = 1;
807 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
808 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
809 hashmap! {
810 PointerEventPhase::Add
811 => vec![contact.clone()],
812 },
813 zx::MonotonicInstant::get(),
814 &get_touch_screen_device_descriptor(),
815 ))
816 .unwrap();
817
818 let mut handle_event_fut =
819 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
820 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
821
822 match handle_result {
824 Poll::Ready(res) => assert_matches!(
825 res.as_slice(),
826 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
827 ),
828 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
829 };
830
831 let active_state_fut = watch_state_stream.next();
833 pin_mut!(active_state_fut);
834 let initial_state = executor.run_until_stalled(&mut active_state_fut);
835 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
836 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
837
838 Ok(())
839 }
840}