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 match unhandled_input_event.device_event {
295 input_device::InputDeviceEvent::ConsumerControls(_)
296 | input_device::InputDeviceEvent::Mouse(_)
297 | input_device::InputDeviceEvent::TouchScreen(_) => {
298 let event_time = unhandled_input_event.event_time.clamp(
304 zx::MonotonicInstant::ZERO,
305 fuchsia_async::MonotonicInstant::now().into_zx(),
306 );
307
308 self.inspect_status.count_received_event(&event_time);
309 self.transition_to_idle_after_new_time(event_time).await;
310 }
311 _ => {}
312 }
313
314 vec![input_device::InputEvent::from(unhandled_input_event)]
315 }
316
317 fn set_handler_healthy(self: std::rc::Rc<Self>) {
318 self.inspect_status.health_node.borrow_mut().set_ok();
319 }
320
321 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
322 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329 use crate::mouse_binding;
330 use crate::testing_utilities::{
331 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
332 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
333 get_touch_screen_device_descriptor,
334 };
335 use crate::utils::Position;
336 use assert_matches::assert_matches;
337 use async_utils::hanging_get::client::HangingGetStream;
338 use fidl::endpoints::create_proxy_and_stream;
339 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
340 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
341 use fidl_fuchsia_ui_input::PointerEventPhase;
342 use fuchsia_async::TestExecutor;
343 use futures::pin_mut;
344 use maplit::hashmap;
345 use std::collections::HashSet;
346 use std::task::Poll;
347 use test_case::test_case;
348
349 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
350
351 async fn create_interaction_state_handler_and_notifier_proxy(
352 suspend_enabled: bool,
353 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
354 let mut interaction_hanging_get = init_interaction_hanging_get();
355
356 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
357 let stream_fut = handle_interaction_notifier_request_stream(
358 notifier_stream,
359 interaction_hanging_get.new_subscriber(),
360 );
361
362 Task::local(async move {
363 if stream_fut.await.is_err() {
364 panic!("Failed to handle notifier request stream");
365 }
366 })
367 .detach();
368
369 let lease_holder = match suspend_enabled {
370 true => {
371 let holder = LeaseHolder::new(fake_activity_governor_server())
372 .await
373 .expect("create lease holder for test");
374 Some(Rc::new(RefCell::new(holder)))
375 }
376 false => None,
377 };
378
379 (
380 InteractionStateHandler::new_for_test(
381 ACTIVITY_TIMEOUT,
382 lease_holder,
383 interaction_hanging_get.new_publisher(),
384 )
385 .await,
386 notifier_proxy,
387 )
388 }
389
390 fn fake_activity_governor_server() -> ActivityGovernorProxy {
391 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
392 Task::local(async move {
393 while let Some(request) = stream.next().await {
394 match request {
395 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
396 let (_, fake_wake_lease) = zx::EventPair::create();
397 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
398 }
399 Ok(unexpected) => {
400 log::warn!(
401 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
402 );
403 }
404 Err(e) => {
405 log::warn!(
406 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
407 e
408 );
409 }
410 }
411 }
412 })
413 .detach();
414
415 proxy
416 }
417
418 #[test_case(true; "Suspend enabled")]
419 #[test_case(false; "Suspend disabled")]
420 #[fuchsia::test(allow_stalls = false)]
421 async fn notifier_sends_initial_state(suspend_enabled: bool) {
422 let (interaction_state_handler, notifier_proxy) =
423 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
424 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
425 assert_eq!(state, State::Active);
426 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
427 }
428
429 #[test_case(true; "Suspend enabled")]
430 #[test_case(false; "Suspend disabled")]
431 #[fuchsia::test]
432 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
433 let mut executor = TestExecutor::new_with_fake_time();
434
435 let handler_and_proxy_fut =
436 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
437 pin_mut!(handler_and_proxy_fut);
438 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
439 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
440 Poll::Ready((handler, proxy)) => (handler, proxy),
441 _ => panic!("Unable to create interaction state handler and proxy"),
442 };
443
444 let mut watch_state_stream =
446 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
447 let state_fut = watch_state_stream.next();
448 pin_mut!(state_fut);
449 let initial_state = executor.run_until_stalled(&mut state_fut);
450 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
451 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
452
453 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
455
456 let idle_state_fut = watch_state_stream.next();
458 pin_mut!(idle_state_fut);
459 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
460 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
461 assert_eq!(interaction_state_handler.is_holding_lease(), false);
462
463 Ok(())
464 }
465
466 #[test_case(true; "Suspend enabled")]
467 #[test_case(false; "Suspend disabled")]
468 #[fuchsia::test]
469 fn interaction_state_handler_drops_first_timer_on_activity(
470 suspend_enabled: bool,
471 ) -> Result<(), Error> {
472 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
486
487 let mut executor = TestExecutor::new_with_fake_time();
488
489 let handler_and_proxy_fut =
490 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
491 pin_mut!(handler_and_proxy_fut);
492 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
493 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
494 Poll::Ready((handler, proxy)) => (handler, proxy),
495 _ => panic!("Unable to create interaction state handler and proxy"),
496 };
497
498 let mut watch_state_stream =
500 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
501 let state_fut = watch_state_stream.next();
502 pin_mut!(state_fut);
503 let initial_state = executor.run_until_stalled(&mut state_fut);
504 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
505 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
506
507 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
509
510 let input_event =
512 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
513 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
514 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
515 ACTIVITY_TIMEOUT / 2,
516 )),
517 &consumer_controls_device_descriptor(),
518 ))
519 .unwrap();
520
521 let mut handle_event_fut =
522 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
523 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
524 assert!(handle_result.is_ready());
525
526 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
528
529 let watch_state_fut = watch_state_stream.next();
531 pin_mut!(watch_state_fut);
532 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
533 assert_matches!(watch_state_res, Poll::Pending);
534 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
535
536 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
538
539 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
541 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
542 assert_eq!(interaction_state_handler.is_holding_lease(), false);
543
544 Ok(())
545 }
546
547 #[test_case(true; "Suspend enabled")]
548 #[test_case(false; "Suspend disabled")]
549 #[fuchsia::test]
550 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
551 let mut executor = TestExecutor::new_with_fake_time();
552
553 let handler_and_proxy_fut =
554 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
555 pin_mut!(handler_and_proxy_fut);
556 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
557 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
558 Poll::Ready((handler, proxy)) => (handler, proxy),
559 _ => panic!("Unable to create interaction state handler and proxy"),
560 };
561
562 let mut watch_state_stream =
564 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
565 let state_fut = watch_state_stream.next();
566 pin_mut!(state_fut);
567 let watch_state_res = executor.run_until_stalled(&mut state_fut);
568 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
569 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
570
571 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
573
574 let input_event =
576 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
577 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
578 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
579 ACTIVITY_TIMEOUT / 2,
580 )),
581 &consumer_controls_device_descriptor(),
582 ))
583 .unwrap();
584
585 let mut handle_event_fut =
586 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
587 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
588 assert!(handle_result.is_ready());
589
590 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
592
593 let input_event =
595 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
596 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
597 zx::MonotonicInstant::ZERO,
598 &consumer_controls_device_descriptor(),
599 ))
600 .unwrap();
601
602 let mut handle_event_fut =
603 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
604 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
605 assert!(handle_result.is_ready());
606
607 let watch_state_fut = watch_state_stream.next();
610 pin_mut!(watch_state_fut);
611 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
612 assert_matches!(initial_state, Poll::Pending);
613 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
614
615 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
617
618 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
620 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
621 assert_eq!(interaction_state_handler.is_holding_lease(), false);
622
623 Ok(())
624 }
625
626 #[test_case(true; "Suspend enabled")]
627 #[test_case(false; "Suspend disabled")]
628 #[fuchsia::test]
629 fn notifier_sends_active_state_with_button_input_event(
630 suspend_enabled: bool,
631 ) -> Result<(), Error> {
632 let mut executor = TestExecutor::new_with_fake_time();
633
634 let handler_and_proxy_fut =
635 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
636 pin_mut!(handler_and_proxy_fut);
637 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
638 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
639 Poll::Ready((handler, proxy)) => (handler, proxy),
640 _ => panic!("Unable to create interaction state handler and proxy"),
641 };
642
643 let mut watch_state_stream =
645 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
646 let state_fut = watch_state_stream.next();
647 pin_mut!(state_fut);
648 let initial_state = executor.run_until_stalled(&mut state_fut);
649 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
650 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
651
652 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
654
655 let idle_state_fut = watch_state_stream.next();
657 pin_mut!(idle_state_fut);
658 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
659 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
660 assert_eq!(interaction_state_handler.is_holding_lease(), false);
661
662 let input_event =
664 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
665 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
666 zx::MonotonicInstant::get(),
667 &consumer_controls_device_descriptor(),
668 ))
669 .unwrap();
670
671 let mut handle_event_fut =
672 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
673 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
674
675 match handle_result {
677 Poll::Ready(res) => assert_matches!(
678 res.as_slice(),
679 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
680 ),
681 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
682 };
683
684 let active_state_fut = watch_state_stream.next();
686 pin_mut!(active_state_fut);
687 let initial_state = executor.run_until_stalled(&mut active_state_fut);
688 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
689 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
690
691 Ok(())
692 }
693
694 #[test_case(true; "Suspend enabled")]
695 #[test_case(false; "Suspend disabled")]
696 #[fuchsia::test]
697 fn notifier_sends_active_state_with_mouse_input_event(
698 suspend_enabled: bool,
699 ) -> Result<(), Error> {
700 let mut executor = TestExecutor::new_with_fake_time();
701
702 let handler_and_proxy_fut =
703 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
704 pin_mut!(handler_and_proxy_fut);
705 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
706 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
707 Poll::Ready((handler, proxy)) => (handler, proxy),
708 _ => panic!("Unable to create interaction state handler and proxy"),
709 };
710
711 let mut watch_state_stream =
713 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
714 let state_fut = watch_state_stream.next();
715 pin_mut!(state_fut);
716 let initial_state = executor.run_until_stalled(&mut state_fut);
717 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
718 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
719
720 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
722
723 let idle_state_fut = watch_state_stream.next();
725 pin_mut!(idle_state_fut);
726 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
727 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
728 assert_eq!(interaction_state_handler.is_holding_lease(), false);
729
730 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
732 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
733 None, None, None, mouse_binding::MousePhase::Down,
737 HashSet::new(),
738 HashSet::new(),
739 zx::MonotonicInstant::get(),
740 &get_mouse_device_descriptor(),
741 ))
742 .unwrap();
743
744 let mut handle_event_fut =
745 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
746 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
747
748 match handle_result {
750 Poll::Ready(res) => assert_matches!(
751 res.as_slice(),
752 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
753 ),
754 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
755 };
756
757 let active_state_fut = watch_state_stream.next();
759 pin_mut!(active_state_fut);
760 let initial_state = executor.run_until_stalled(&mut active_state_fut);
761 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
762 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
763
764 Ok(())
765 }
766
767 #[test_case(true; "Suspend enabled")]
768 #[test_case(false; "Suspend disabled")]
769 #[fuchsia::test]
770 fn notifier_sends_active_state_with_touch_input_event(
771 suspend_enabled: bool,
772 ) -> Result<(), Error> {
773 let mut executor = TestExecutor::new_with_fake_time();
774
775 let handler_and_proxy_fut =
776 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
777 pin_mut!(handler_and_proxy_fut);
778 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
779 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
780 Poll::Ready((handler, proxy)) => (handler, proxy),
781 _ => panic!("Unable to create interaction state handler and proxy"),
782 };
783
784 let mut watch_state_stream =
786 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
787 let state_fut = watch_state_stream.next();
788 pin_mut!(state_fut);
789 let initial_state = executor.run_until_stalled(&mut state_fut);
790 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
791 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
792
793 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
795
796 let idle_state_fut = watch_state_stream.next();
798 pin_mut!(idle_state_fut);
799 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
800 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
801 assert_eq!(interaction_state_handler.is_holding_lease(), false);
802
803 const TOUCH_ID: u32 = 1;
805 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
806 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
807 hashmap! {
808 PointerEventPhase::Add
809 => vec![contact.clone()],
810 },
811 zx::MonotonicInstant::get(),
812 &get_touch_screen_device_descriptor(),
813 ))
814 .unwrap();
815
816 let mut handle_event_fut =
817 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
818 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
819
820 match handle_result {
822 Poll::Ready(res) => assert_matches!(
823 res.as_slice(),
824 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
825 ),
826 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
827 };
828
829 let active_state_fut = watch_state_stream.next();
831 pin_mut!(active_state_fut);
832 let initial_state = executor.run_until_stalled(&mut active_state_fut);
833 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
834 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
835
836 Ok(())
837 }
838}