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 enable_button_baton_passing: bool,
108 enable_mouse_baton_passing: bool,
109 enable_touch_baton_passing: bool,
110}
111
112impl InteractionStateHandler {
113 pub async fn new(
116 idle_threshold_ms: zx::MonotonicDuration,
117 input_handlers_node: &fuchsia_inspect::Node,
118 state_publisher: InteractionStatePublisher,
119 suspend_enabled: bool,
120 enable_button_baton_passing: bool,
121 enable_mouse_baton_passing: bool,
122 enable_touch_baton_passing: bool,
123 ) -> Rc<Self> {
124 log::info!(
125 "InteractionStateHandler is initialized with idle_threshold_ms: {:?}",
126 idle_threshold_ms.into_millis()
127 );
128
129 let inspect_status =
130 InputHandlerStatus::new(input_handlers_node, "interaction_state_handler", false);
131
132 let lease_holder = match suspend_enabled {
133 true => {
134 let activity_governor = connect_to_protocol::<ActivityGovernorMarker>()
135 .expect("connect to fuchsia.power.system.ActivityGovernor");
136 match LeaseHolder::new(activity_governor).await {
137 Ok(holder) => Some(Rc::new(RefCell::new(holder))),
138 Err(e) => {
139 log::error!(
140 "Unable to integrate with power, system may incorrectly enter suspend: {:?}",
141 e
142 );
143 None
144 }
145 }
146 }
147 false => None,
148 };
149
150 Rc::new(Self::new_internal(
151 idle_threshold_ms,
152 zx::MonotonicInstant::get(),
153 lease_holder,
154 inspect_status,
155 state_publisher,
156 enable_button_baton_passing,
157 enable_mouse_baton_passing,
158 enable_touch_baton_passing,
159 ))
160 }
161
162 #[cfg(test)]
163 async fn new_for_test(
165 idle_threshold_ms: zx::MonotonicDuration,
166 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
167 state_publisher: InteractionStatePublisher,
168 ) -> Rc<Self> {
169 fuchsia_async::TestExecutor::advance_to(zx::MonotonicInstant::ZERO.into()).await;
170
171 let inspector = fuchsia_inspect::Inspector::default();
172 let test_node = inspector.root().create_child("test_node");
173 let inspect_status = InputHandlerStatus::new(
174 &test_node,
175 "interaction_state_handler",
176 false,
177 );
178 Rc::new(Self::new_internal(
179 idle_threshold_ms,
180 zx::MonotonicInstant::ZERO,
181 lease_holder,
182 inspect_status,
183 state_publisher,
184 false,
185 false,
186 false,
187 ))
188 }
189
190 fn new_internal(
191 idle_threshold_ms: zx::MonotonicDuration,
192 initial_timestamp: zx::MonotonicInstant,
193 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
194 inspect_status: InputHandlerStatus,
195 state_publisher: InteractionStatePublisher,
196 enable_button_baton_passing: bool,
197 enable_mouse_baton_passing: bool,
198 enable_touch_baton_passing: bool,
199 ) -> Self {
200 let task = Self::create_idle_transition_task(
201 initial_timestamp + idle_threshold_ms,
202 state_publisher.clone(),
203 lease_holder.clone(),
204 );
205
206 Self {
207 idle_threshold_ms,
208 idle_transition_task: Cell::new(Some(task)),
209 last_event_time: RefCell::new(initial_timestamp),
210 lease_holder,
211 state_publisher,
212 inspect_status,
213 enable_button_baton_passing,
214 enable_mouse_baton_passing,
215 enable_touch_baton_passing,
216 }
217 }
218
219 async fn transition_to_active(
220 state_publisher: &InteractionStatePublisher,
221 lease_holder: &Option<Rc<RefCell<LeaseHolder>>>,
222 ) {
223 if let Some(holder) = lease_holder {
224 if let Err(e) = holder.borrow_mut().create_lease().await {
225 log::warn!(
226 "Unable to create lease, system may incorrectly go into suspend: {:?}",
227 e
228 );
229 };
230 }
231 state_publisher.set(State::Active);
232 }
233
234 fn create_idle_transition_task(
235 timeout: zx::MonotonicInstant,
236 state_publisher: InteractionStatePublisher,
237 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
238 ) -> Task<()> {
239 Task::local(async move {
240 Timer::new(timeout).await;
241 lease_holder.and_then(|holder| Some(holder.borrow_mut().drop_lease()));
242 state_publisher.set(State::Idle);
243 })
244 }
245
246 async fn transition_to_idle_after_new_time(&self, event_time: zx::MonotonicInstant) {
247 if *self.last_event_time.borrow() > event_time {
248 return;
249 }
250
251 *self.last_event_time.borrow_mut() = event_time;
252 if let Some(t) = self.idle_transition_task.take() {
253 if let Some(()) = t.abort().await {
256 Self::transition_to_active(&self.state_publisher, &self.lease_holder).await;
257 }
258 }
259
260 self.idle_transition_task.set(Some(Self::create_idle_transition_task(
261 event_time + self.idle_threshold_ms,
262 self.state_publisher.clone(),
263 self.lease_holder.clone(),
264 )));
265 }
266
267 #[cfg(test)]
268 fn is_holding_lease(&self) -> bool {
269 if let Some(holder) = &self.lease_holder {
270 return holder.borrow().is_holding_lease();
271 }
272
273 false
274 }
275}
276
277pub async fn handle_interaction_notifier_request_stream(
282 mut stream: NotifierRequestStream,
283 subscriber: InteractionStateSubscriber,
284) -> Result<(), Error> {
285 while let Some(notifier_request) = stream.next().await {
286 let NotifierRequest::WatchState { responder } = notifier_request?;
287 subscriber.register(responder)?;
288 }
289
290 Ok(())
291}
292
293pub fn init_interaction_hanging_get() -> InteractionHangingGet {
294 let notify_fn: NotifyFn = Box::new(|state, responder| {
295 if responder.send(*state).is_err() {
296 log::info!("Failed to send user input interaction state");
297 }
298
299 true
300 });
301
302 let initial_state = State::Active;
303 InteractionHangingGet::new(initial_state, notify_fn)
304}
305
306#[async_trait(?Send)]
307impl UnhandledInputHandler for InteractionStateHandler {
308 async fn handle_unhandled_input_event(
311 self: Rc<Self>,
312 unhandled_input_event: input_device::UnhandledInputEvent,
313 ) -> Vec<input_device::InputEvent> {
314 fuchsia_trace::duration!(c"input", c"interaction_state_handler");
315 match unhandled_input_event.device_event {
316 input_device::InputDeviceEvent::ConsumerControls(ref consumer_controls_event) => {
317 if self.enable_button_baton_passing {
318 if !consumer_controls_event.wake_lease.is_some() {
319 log::debug!(
320 "InteractionStateHandler received a button event without a wake lease, even though one was expected."
321 );
322 }
323 } else {
324 fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
325 let event_time = unhandled_input_event.event_time.clamp(
331 zx::MonotonicInstant::ZERO,
332 fuchsia_async::MonotonicInstant::now().into_zx(),
333 );
334
335 self.inspect_status.count_received_event(&event_time);
336 self.transition_to_idle_after_new_time(event_time).await;
337 }
338 }
339 input_device::InputDeviceEvent::Mouse(ref mouse_event) => {
340 if self.enable_mouse_baton_passing {
341 if !mouse_event.wake_lease.lock().is_some() {
342 log::debug!(
343 "InteractionStateHandler received a mouse event without a wake lease, even though one was expected."
344 );
345 }
346 } else {
347 fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
348 let event_time = unhandled_input_event.event_time.clamp(
354 zx::MonotonicInstant::ZERO,
355 fuchsia_async::MonotonicInstant::now().into_zx(),
356 );
357
358 self.inspect_status.count_received_event(&event_time);
359 self.transition_to_idle_after_new_time(event_time).await;
360 }
361 }
362 input_device::InputDeviceEvent::TouchScreen(ref touch_event) => {
363 if self.enable_touch_baton_passing {
364 if !touch_event.wake_lease.is_some() {
365 log::debug!(
366 "InteractionStateHandler received a touch event without a wake lease, even though one was expected."
367 );
368 }
369 } else {
370 fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
371 let event_time = unhandled_input_event.event_time.clamp(
377 zx::MonotonicInstant::ZERO,
378 fuchsia_async::MonotonicInstant::now().into_zx(),
379 );
380
381 self.inspect_status.count_received_event(&event_time);
382 self.transition_to_idle_after_new_time(event_time).await;
383 }
384 }
385 _ => {}
386 }
387
388 vec![input_device::InputEvent::from(unhandled_input_event)]
389 }
390
391 fn set_handler_healthy(self: std::rc::Rc<Self>) {
392 self.inspect_status.health_node.borrow_mut().set_ok();
393 }
394
395 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
396 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 use crate::mouse_binding;
404 use crate::testing_utilities::{
405 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
406 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
407 get_touch_screen_device_descriptor,
408 };
409 use crate::utils::Position;
410 use assert_matches::assert_matches;
411 use async_utils::hanging_get::client::HangingGetStream;
412 use fidl::endpoints::create_proxy_and_stream;
413 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
414 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
415 use fidl_fuchsia_ui_input::PointerEventPhase;
416 use fuchsia_async::TestExecutor;
417 use futures::pin_mut;
418 use maplit::hashmap;
419 use std::collections::HashSet;
420 use std::task::Poll;
421 use test_case::test_case;
422
423 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
424
425 async fn create_interaction_state_handler_and_notifier_proxy(
426 suspend_enabled: bool,
427 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
428 let mut interaction_hanging_get = init_interaction_hanging_get();
429
430 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
431 let stream_fut = handle_interaction_notifier_request_stream(
432 notifier_stream,
433 interaction_hanging_get.new_subscriber(),
434 );
435
436 Task::local(async move {
437 if stream_fut.await.is_err() {
438 panic!("Failed to handle notifier request stream");
439 }
440 })
441 .detach();
442
443 let lease_holder = match suspend_enabled {
444 true => {
445 let holder = LeaseHolder::new(fake_activity_governor_server())
446 .await
447 .expect("create lease holder for test");
448 Some(Rc::new(RefCell::new(holder)))
449 }
450 false => None,
451 };
452
453 (
454 InteractionStateHandler::new_for_test(
455 ACTIVITY_TIMEOUT,
456 lease_holder,
457 interaction_hanging_get.new_publisher(),
458 )
459 .await,
460 notifier_proxy,
461 )
462 }
463
464 fn fake_activity_governor_server() -> ActivityGovernorProxy {
465 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
466 Task::local(async move {
467 while let Some(request) = stream.next().await {
468 match request {
469 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
470 let (_, fake_wake_lease) = zx::EventPair::create();
471 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
472 }
473 Ok(unexpected) => {
474 log::warn!(
475 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
476 );
477 }
478 Err(e) => {
479 log::warn!(
480 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
481 e
482 );
483 }
484 }
485 }
486 })
487 .detach();
488
489 proxy
490 }
491
492 #[test_case(true; "Suspend enabled")]
493 #[test_case(false; "Suspend disabled")]
494 #[fuchsia::test(allow_stalls = false)]
495 async fn notifier_sends_initial_state(suspend_enabled: bool) {
496 let (interaction_state_handler, notifier_proxy) =
497 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
498 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
499 assert_eq!(state, State::Active);
500 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
501 }
502
503 #[test_case(true; "Suspend enabled")]
504 #[test_case(false; "Suspend disabled")]
505 #[fuchsia::test]
506 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
507 let mut executor = TestExecutor::new_with_fake_time();
508
509 let handler_and_proxy_fut =
510 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
511 pin_mut!(handler_and_proxy_fut);
512 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
513 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
514 Poll::Ready((handler, proxy)) => (handler, proxy),
515 _ => panic!("Unable to create interaction state handler and proxy"),
516 };
517
518 let mut watch_state_stream =
520 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
521 let state_fut = watch_state_stream.next();
522 pin_mut!(state_fut);
523 let initial_state = executor.run_until_stalled(&mut state_fut);
524 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
525 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
526
527 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
529
530 let idle_state_fut = watch_state_stream.next();
532 pin_mut!(idle_state_fut);
533 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
534 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
535 assert_eq!(interaction_state_handler.is_holding_lease(), false);
536
537 Ok(())
538 }
539
540 #[test_case(true; "Suspend enabled")]
541 #[test_case(false; "Suspend disabled")]
542 #[fuchsia::test]
543 fn interaction_state_handler_drops_first_timer_on_activity(
544 suspend_enabled: bool,
545 ) -> Result<(), Error> {
546 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
560
561 let mut executor = TestExecutor::new_with_fake_time();
562
563 let handler_and_proxy_fut =
564 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
565 pin_mut!(handler_and_proxy_fut);
566 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
567 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
568 Poll::Ready((handler, proxy)) => (handler, proxy),
569 _ => panic!("Unable to create interaction state handler and proxy"),
570 };
571
572 let mut watch_state_stream =
574 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
575 let state_fut = watch_state_stream.next();
576 pin_mut!(state_fut);
577 let initial_state = executor.run_until_stalled(&mut state_fut);
578 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
579 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
580
581 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
583
584 let input_event =
586 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
587 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
588 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
589 ACTIVITY_TIMEOUT / 2,
590 )),
591 &consumer_controls_device_descriptor(),
592 ))
593 .unwrap();
594
595 let mut handle_event_fut =
596 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
597 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
598 assert!(handle_result.is_ready());
599
600 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
602
603 let watch_state_fut = watch_state_stream.next();
605 pin_mut!(watch_state_fut);
606 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
607 assert_matches!(watch_state_res, Poll::Pending);
608 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
609
610 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
612
613 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
615 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
616 assert_eq!(interaction_state_handler.is_holding_lease(), false);
617
618 Ok(())
619 }
620
621 #[test_case(true; "Suspend enabled")]
622 #[test_case(false; "Suspend disabled")]
623 #[fuchsia::test]
624 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
625 let mut executor = TestExecutor::new_with_fake_time();
626
627 let handler_and_proxy_fut =
628 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
629 pin_mut!(handler_and_proxy_fut);
630 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
631 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
632 Poll::Ready((handler, proxy)) => (handler, proxy),
633 _ => panic!("Unable to create interaction state handler and proxy"),
634 };
635
636 let mut watch_state_stream =
638 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
639 let state_fut = watch_state_stream.next();
640 pin_mut!(state_fut);
641 let watch_state_res = executor.run_until_stalled(&mut state_fut);
642 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
643 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
644
645 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
647
648 let input_event =
650 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
651 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
652 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
653 ACTIVITY_TIMEOUT / 2,
654 )),
655 &consumer_controls_device_descriptor(),
656 ))
657 .unwrap();
658
659 let mut handle_event_fut =
660 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
661 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
662 assert!(handle_result.is_ready());
663
664 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
666
667 let input_event =
669 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
670 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
671 zx::MonotonicInstant::ZERO,
672 &consumer_controls_device_descriptor(),
673 ))
674 .unwrap();
675
676 let mut handle_event_fut =
677 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
678 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
679 assert!(handle_result.is_ready());
680
681 let watch_state_fut = watch_state_stream.next();
684 pin_mut!(watch_state_fut);
685 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
686 assert_matches!(initial_state, Poll::Pending);
687 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
688
689 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
691
692 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
694 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
695 assert_eq!(interaction_state_handler.is_holding_lease(), false);
696
697 Ok(())
698 }
699
700 #[test_case(true; "Suspend enabled")]
701 #[test_case(false; "Suspend disabled")]
702 #[fuchsia::test]
703 fn notifier_sends_active_state_with_button_input_event(
704 suspend_enabled: bool,
705 ) -> Result<(), Error> {
706 let mut executor = TestExecutor::new_with_fake_time();
707
708 let handler_and_proxy_fut =
709 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
710 pin_mut!(handler_and_proxy_fut);
711 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
712 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
713 Poll::Ready((handler, proxy)) => (handler, proxy),
714 _ => panic!("Unable to create interaction state handler and proxy"),
715 };
716
717 let mut watch_state_stream =
719 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
720 let state_fut = watch_state_stream.next();
721 pin_mut!(state_fut);
722 let initial_state = executor.run_until_stalled(&mut state_fut);
723 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
724 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
725
726 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
728
729 let idle_state_fut = watch_state_stream.next();
731 pin_mut!(idle_state_fut);
732 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
733 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
734 assert_eq!(interaction_state_handler.is_holding_lease(), false);
735
736 let input_event =
738 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
739 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
740 zx::MonotonicInstant::get(),
741 &consumer_controls_device_descriptor(),
742 ))
743 .unwrap();
744
745 let mut handle_event_fut =
746 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
747 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
748
749 match handle_result {
751 Poll::Ready(res) => assert_matches!(
752 res.as_slice(),
753 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
754 ),
755 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
756 };
757
758 let active_state_fut = watch_state_stream.next();
760 pin_mut!(active_state_fut);
761 let initial_state = executor.run_until_stalled(&mut active_state_fut);
762 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
763 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
764
765 Ok(())
766 }
767
768 #[test_case(true; "Suspend enabled")]
769 #[test_case(false; "Suspend disabled")]
770 #[fuchsia::test]
771 fn notifier_sends_active_state_with_mouse_input_event(
772 suspend_enabled: bool,
773 ) -> Result<(), Error> {
774 let mut executor = TestExecutor::new_with_fake_time();
775
776 let handler_and_proxy_fut =
777 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
778 pin_mut!(handler_and_proxy_fut);
779 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
780 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
781 Poll::Ready((handler, proxy)) => (handler, proxy),
782 _ => panic!("Unable to create interaction state handler and proxy"),
783 };
784
785 let mut watch_state_stream =
787 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
788 let state_fut = watch_state_stream.next();
789 pin_mut!(state_fut);
790 let initial_state = executor.run_until_stalled(&mut state_fut);
791 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
792 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
793
794 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
796
797 let idle_state_fut = watch_state_stream.next();
799 pin_mut!(idle_state_fut);
800 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
801 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
802 assert_eq!(interaction_state_handler.is_holding_lease(), false);
803
804 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
806 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
807 None, None, None, mouse_binding::MousePhase::Down,
811 HashSet::new(),
812 HashSet::new(),
813 zx::MonotonicInstant::get(),
814 &get_mouse_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
841 #[test_case(true; "Suspend enabled")]
842 #[test_case(false; "Suspend disabled")]
843 #[fuchsia::test]
844 fn notifier_sends_active_state_with_touch_input_event(
845 suspend_enabled: bool,
846 ) -> Result<(), Error> {
847 let mut executor = TestExecutor::new_with_fake_time();
848
849 let handler_and_proxy_fut =
850 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
851 pin_mut!(handler_and_proxy_fut);
852 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
853 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
854 Poll::Ready((handler, proxy)) => (handler, proxy),
855 _ => panic!("Unable to create interaction state handler and proxy"),
856 };
857
858 let mut watch_state_stream =
860 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
861 let state_fut = watch_state_stream.next();
862 pin_mut!(state_fut);
863 let initial_state = executor.run_until_stalled(&mut state_fut);
864 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
865 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
866
867 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
869
870 let idle_state_fut = watch_state_stream.next();
872 pin_mut!(idle_state_fut);
873 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
874 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
875 assert_eq!(interaction_state_handler.is_holding_lease(), false);
876
877 const TOUCH_ID: u32 = 1;
879 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
880 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
881 hashmap! {
882 PointerEventPhase::Add
883 => vec![contact.clone()],
884 },
885 zx::MonotonicInstant::get(),
886 &get_touch_screen_device_descriptor(),
887 ))
888 .unwrap();
889
890 let mut handle_event_fut =
891 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
892 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
893
894 match handle_result {
896 Poll::Ready(res) => assert_matches!(
897 res.as_slice(),
898 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
899 ),
900 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
901 };
902
903 let active_state_fut = watch_state_stream.next();
905 pin_mut!(active_state_fut);
906 let initial_state = executor.run_until_stalled(&mut active_state_fut);
907 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
908 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
909
910 Ok(())
911 }
912}