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!("input", "interaction_state_handler");
315 let trace_id = unhandled_input_event.trace_id.unwrap_or_else(|| 0.into());
316 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id);
317
318 match unhandled_input_event.device_event {
319 input_device::InputDeviceEvent::ConsumerControls(..) => {
320 if self.enable_button_baton_passing {
321 log::warn!("Button event with baton passing");
323 } else {
324 fuchsia_trace::duration!("input", "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(..) => {
340 if self.enable_mouse_baton_passing {
341 log::warn!("Mouse event with baton passing");
343 } else {
344 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
345 let event_time = unhandled_input_event.event_time.clamp(
351 zx::MonotonicInstant::ZERO,
352 fuchsia_async::MonotonicInstant::now().into_zx(),
353 );
354
355 self.inspect_status.count_received_event(&event_time);
356 self.transition_to_idle_after_new_time(event_time).await;
357 }
358 }
359 input_device::InputDeviceEvent::TouchScreen(..) => {
360 if self.enable_touch_baton_passing {
361 log::warn!("Touch event with baton passing");
363 } else {
364 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
365 let event_time = unhandled_input_event.event_time.clamp(
371 zx::MonotonicInstant::ZERO,
372 fuchsia_async::MonotonicInstant::now().into_zx(),
373 );
374
375 self.inspect_status.count_received_event(&event_time);
376 self.transition_to_idle_after_new_time(event_time).await;
377 }
378 }
379 _ => {
380 log::warn!("Unhandled input event: {:?}", unhandled_input_event.get_event_type());
382 }
383 }
384
385 vec![input_device::InputEvent::from(unhandled_input_event)]
386 }
387
388 fn set_handler_healthy(self: std::rc::Rc<Self>) {
389 self.inspect_status.health_node.borrow_mut().set_ok();
390 }
391
392 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
393 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
394 }
395
396 fn get_name(&self) -> &'static str {
397 "InteractionStateHandler"
398 }
399
400 fn interest(&self) -> Vec<input_device::InputEventType> {
401 let mut interest = vec![];
402 if !self.enable_button_baton_passing {
403 interest.push(input_device::InputEventType::ConsumerControls);
404 }
405 if !self.enable_mouse_baton_passing {
406 interest.push(input_device::InputEventType::Mouse);
407 }
408 if !self.enable_touch_baton_passing {
409 interest.push(input_device::InputEventType::TouchScreen);
410 }
411 interest
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418 use crate::mouse_binding;
419 use crate::testing_utilities::{
420 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
421 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
422 get_touch_screen_device_descriptor,
423 };
424 use crate::utils::Position;
425 use assert_matches::assert_matches;
426 use async_utils::hanging_get::client::HangingGetStream;
427 use fidl::endpoints::create_proxy_and_stream;
428 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
429 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
430 use fidl_fuchsia_ui_input::PointerEventPhase;
431 use fuchsia_async::TestExecutor;
432 use futures::pin_mut;
433 use maplit::hashmap;
434 use std::collections::HashSet;
435 use std::task::Poll;
436 use test_case::test_case;
437
438 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
439
440 async fn create_interaction_state_handler_and_notifier_proxy(
441 suspend_enabled: bool,
442 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
443 let mut interaction_hanging_get = init_interaction_hanging_get();
444
445 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
446 let stream_fut = handle_interaction_notifier_request_stream(
447 notifier_stream,
448 interaction_hanging_get.new_subscriber(),
449 );
450
451 Task::local(async move {
452 if stream_fut.await.is_err() {
453 panic!("Failed to handle notifier request stream");
454 }
455 })
456 .detach();
457
458 let lease_holder = match suspend_enabled {
459 true => {
460 let holder = LeaseHolder::new(fake_activity_governor_server())
461 .await
462 .expect("create lease holder for test");
463 Some(Rc::new(RefCell::new(holder)))
464 }
465 false => None,
466 };
467
468 (
469 InteractionStateHandler::new_for_test(
470 ACTIVITY_TIMEOUT,
471 lease_holder,
472 interaction_hanging_get.new_publisher(),
473 )
474 .await,
475 notifier_proxy,
476 )
477 }
478
479 fn fake_activity_governor_server() -> ActivityGovernorProxy {
480 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
481 Task::local(async move {
482 while let Some(request) = stream.next().await {
483 match request {
484 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
485 let (_, fake_wake_lease) = zx::EventPair::create();
486 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
487 }
488 Ok(unexpected) => {
489 log::warn!(
490 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
491 );
492 }
493 Err(e) => {
494 log::warn!(
495 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
496 e
497 );
498 }
499 }
500 }
501 })
502 .detach();
503
504 proxy
505 }
506
507 #[test_case(true; "Suspend enabled")]
508 #[test_case(false; "Suspend disabled")]
509 #[fuchsia::test(allow_stalls = false)]
510 async fn notifier_sends_initial_state(suspend_enabled: bool) {
511 let (interaction_state_handler, notifier_proxy) =
512 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
513 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
514 assert_eq!(state, State::Active);
515 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
516 }
517
518 #[test_case(true; "Suspend enabled")]
519 #[test_case(false; "Suspend disabled")]
520 #[fuchsia::test]
521 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
522 let mut executor = TestExecutor::new_with_fake_time();
523
524 let handler_and_proxy_fut =
525 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
526 pin_mut!(handler_and_proxy_fut);
527 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
528 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
529 Poll::Ready((handler, proxy)) => (handler, proxy),
530 _ => panic!("Unable to create interaction state handler and proxy"),
531 };
532
533 let mut watch_state_stream =
535 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
536 let state_fut = watch_state_stream.next();
537 pin_mut!(state_fut);
538 let initial_state = executor.run_until_stalled(&mut state_fut);
539 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
540 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
541
542 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
544
545 let idle_state_fut = watch_state_stream.next();
547 pin_mut!(idle_state_fut);
548 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
549 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
550 assert_eq!(interaction_state_handler.is_holding_lease(), false);
551
552 Ok(())
553 }
554
555 #[test_case(true; "Suspend enabled")]
556 #[test_case(false; "Suspend disabled")]
557 #[fuchsia::test]
558 fn interaction_state_handler_drops_first_timer_on_activity(
559 suspend_enabled: bool,
560 ) -> Result<(), Error> {
561 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
575
576 let mut executor = TestExecutor::new_with_fake_time();
577
578 let handler_and_proxy_fut =
579 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
580 pin_mut!(handler_and_proxy_fut);
581 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
582 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
583 Poll::Ready((handler, proxy)) => (handler, proxy),
584 _ => panic!("Unable to create interaction state handler and proxy"),
585 };
586
587 let mut watch_state_stream =
589 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
590 let state_fut = watch_state_stream.next();
591 pin_mut!(state_fut);
592 let initial_state = executor.run_until_stalled(&mut state_fut);
593 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
594 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
595
596 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
598
599 let input_event =
601 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
602 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
603 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
604 ACTIVITY_TIMEOUT / 2,
605 )),
606 &consumer_controls_device_descriptor(),
607 ))
608 .unwrap();
609
610 let mut handle_event_fut =
611 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
612 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
613 assert!(handle_result.is_ready());
614
615 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
617
618 let watch_state_fut = watch_state_stream.next();
620 pin_mut!(watch_state_fut);
621 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
622 assert_matches!(watch_state_res, Poll::Pending);
623 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
624
625 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
627
628 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
630 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
631 assert_eq!(interaction_state_handler.is_holding_lease(), false);
632
633 Ok(())
634 }
635
636 #[test_case(true; "Suspend enabled")]
637 #[test_case(false; "Suspend disabled")]
638 #[fuchsia::test]
639 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
640 let mut executor = TestExecutor::new_with_fake_time();
641
642 let handler_and_proxy_fut =
643 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
644 pin_mut!(handler_and_proxy_fut);
645 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
646 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
647 Poll::Ready((handler, proxy)) => (handler, proxy),
648 _ => panic!("Unable to create interaction state handler and proxy"),
649 };
650
651 let mut watch_state_stream =
653 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
654 let state_fut = watch_state_stream.next();
655 pin_mut!(state_fut);
656 let watch_state_res = executor.run_until_stalled(&mut state_fut);
657 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
658 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
659
660 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
662
663 let input_event =
665 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
666 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
667 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
668 ACTIVITY_TIMEOUT / 2,
669 )),
670 &consumer_controls_device_descriptor(),
671 ))
672 .unwrap();
673
674 let mut handle_event_fut =
675 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
676 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
677 assert!(handle_result.is_ready());
678
679 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
681
682 let input_event =
684 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
685 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
686 zx::MonotonicInstant::ZERO,
687 &consumer_controls_device_descriptor(),
688 ))
689 .unwrap();
690
691 let mut handle_event_fut =
692 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
693 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
694 assert!(handle_result.is_ready());
695
696 let watch_state_fut = watch_state_stream.next();
699 pin_mut!(watch_state_fut);
700 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
701 assert_matches!(initial_state, Poll::Pending);
702 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
703
704 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
706
707 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
709 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
710 assert_eq!(interaction_state_handler.is_holding_lease(), false);
711
712 Ok(())
713 }
714
715 #[test_case(true; "Suspend enabled")]
716 #[test_case(false; "Suspend disabled")]
717 #[fuchsia::test]
718 fn notifier_sends_active_state_with_button_input_event(
719 suspend_enabled: bool,
720 ) -> Result<(), Error> {
721 let mut executor = TestExecutor::new_with_fake_time();
722
723 let handler_and_proxy_fut =
724 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
725 pin_mut!(handler_and_proxy_fut);
726 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
727 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
728 Poll::Ready((handler, proxy)) => (handler, proxy),
729 _ => panic!("Unable to create interaction state handler and proxy"),
730 };
731
732 let mut watch_state_stream =
734 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
735 let state_fut = watch_state_stream.next();
736 pin_mut!(state_fut);
737 let initial_state = executor.run_until_stalled(&mut state_fut);
738 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
739 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
740
741 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
743
744 let idle_state_fut = watch_state_stream.next();
746 pin_mut!(idle_state_fut);
747 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
748 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
749 assert_eq!(interaction_state_handler.is_holding_lease(), false);
750
751 let input_event =
753 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
754 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
755 zx::MonotonicInstant::get(),
756 &consumer_controls_device_descriptor(),
757 ))
758 .unwrap();
759
760 let mut handle_event_fut =
761 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
762 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
763
764 match handle_result {
766 Poll::Ready(res) => assert_matches!(
767 res.as_slice(),
768 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
769 ),
770 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
771 };
772
773 let active_state_fut = watch_state_stream.next();
775 pin_mut!(active_state_fut);
776 let initial_state = executor.run_until_stalled(&mut active_state_fut);
777 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
778 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
779
780 Ok(())
781 }
782
783 #[test_case(true; "Suspend enabled")]
784 #[test_case(false; "Suspend disabled")]
785 #[fuchsia::test]
786 fn notifier_sends_active_state_with_mouse_input_event(
787 suspend_enabled: bool,
788 ) -> Result<(), Error> {
789 let mut executor = TestExecutor::new_with_fake_time();
790
791 let handler_and_proxy_fut =
792 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
793 pin_mut!(handler_and_proxy_fut);
794 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
795 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
796 Poll::Ready((handler, proxy)) => (handler, proxy),
797 _ => panic!("Unable to create interaction state handler and proxy"),
798 };
799
800 let mut watch_state_stream =
802 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
803 let state_fut = watch_state_stream.next();
804 pin_mut!(state_fut);
805 let initial_state = executor.run_until_stalled(&mut state_fut);
806 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
807 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
808
809 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
811
812 let idle_state_fut = watch_state_stream.next();
814 pin_mut!(idle_state_fut);
815 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
816 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
817 assert_eq!(interaction_state_handler.is_holding_lease(), false);
818
819 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
821 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
822 None, None, None, mouse_binding::MousePhase::Down,
826 HashSet::new(),
827 HashSet::new(),
828 zx::MonotonicInstant::get(),
829 &get_mouse_device_descriptor(),
830 ))
831 .unwrap();
832
833 let mut handle_event_fut =
834 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
835 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
836
837 match handle_result {
839 Poll::Ready(res) => assert_matches!(
840 res.as_slice(),
841 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
842 ),
843 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
844 };
845
846 let active_state_fut = watch_state_stream.next();
848 pin_mut!(active_state_fut);
849 let initial_state = executor.run_until_stalled(&mut active_state_fut);
850 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
851 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
852
853 Ok(())
854 }
855
856 #[test_case(true; "Suspend enabled")]
857 #[test_case(false; "Suspend disabled")]
858 #[fuchsia::test]
859 fn notifier_sends_active_state_with_touch_input_event(
860 suspend_enabled: bool,
861 ) -> Result<(), Error> {
862 let mut executor = TestExecutor::new_with_fake_time();
863
864 let handler_and_proxy_fut =
865 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
866 pin_mut!(handler_and_proxy_fut);
867 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
868 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
869 Poll::Ready((handler, proxy)) => (handler, proxy),
870 _ => panic!("Unable to create interaction state handler and proxy"),
871 };
872
873 let mut watch_state_stream =
875 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
876 let state_fut = watch_state_stream.next();
877 pin_mut!(state_fut);
878 let initial_state = executor.run_until_stalled(&mut state_fut);
879 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
880 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
881
882 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
884
885 let idle_state_fut = watch_state_stream.next();
887 pin_mut!(idle_state_fut);
888 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
889 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
890 assert_eq!(interaction_state_handler.is_holding_lease(), false);
891
892 const TOUCH_ID: u32 = 1;
894 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
895 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
896 hashmap! {
897 PointerEventPhase::Add
898 => vec![contact.clone()],
899 },
900 zx::MonotonicInstant::get(),
901 &get_touch_screen_device_descriptor(),
902 ))
903 .unwrap();
904
905 let mut handle_event_fut =
906 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
907 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
908
909 match handle_result {
911 Poll::Ready(res) => assert_matches!(
912 res.as_slice(),
913 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
914 ),
915 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
916 };
917
918 let active_state_fut = watch_state_stream.next();
920 pin_mut!(active_state_fut);
921 let initial_state = executor.run_until_stalled(&mut active_state_fut);
922 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
923 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
924
925 Ok(())
926 }
927}