1#![cfg(fuchsia_api_level_at_least = "HEAD")]
6
7use crate::dispatcher::TaskHandle;
8use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
9use crate::{Dispatcher, Incoming, MonotonicInstant, input_device, metrics};
10use anyhow::{Context, Error};
11use async_trait::async_trait;
12use async_utils::hanging_get::server::{HangingGet, Publisher, Subscriber};
13use fidl_fuchsia_input_interaction::{
14 NotifierRequest, NotifierRequestStream, NotifierWatchStateResponder, State,
15};
16use fidl_fuchsia_power_system::ActivityGovernorProxy;
17use fuchsia_inspect::health::Reporter;
18use futures::StreamExt;
19use metrics_registry::InputPipelineErrorMetricDimensionEvent;
20use std::cell::{Cell, RefCell};
21use std::rc::Rc;
22
23struct LeaseHolder {
24 activity_governor: ActivityGovernorProxy,
25 wake_lease: Option<zx::EventPair>,
26}
27
28impl LeaseHolder {
29 async fn new(activity_governor: ActivityGovernorProxy) -> Result<Self, Error> {
30 let wake_lease = activity_governor
31 .take_wake_lease("scene_manager")
32 .await
33 .context("cannot get wake lease from SAG")?;
34 log::info!("InteractionStateHandler created a wake lease during initialization.");
35
36 Ok(Self { activity_governor, wake_lease: Some(wake_lease) })
37 }
38
39 async fn create_lease(&mut self) -> Result<(), Error> {
40 if self.wake_lease.is_some() {
41 log::warn!(
42 "InteractionStateHandler already held a wake lease when trying to create one, please investigate."
43 );
44 return Ok(());
45 }
46
47 let wake_lease = self
48 .activity_governor
49 .take_wake_lease("scene_manager")
50 .await
51 .context("cannot get wake lease from SAG")?;
52 self.wake_lease = Some(wake_lease);
53 log::info!(
54 "InteractionStateHandler created a wake lease due to receiving recent user input."
55 );
56
57 Ok(())
58 }
59
60 fn drop_lease(&mut self) {
61 if let Some(lease) = self.wake_lease.take() {
62 log::info!(
63 "InteractionStateHandler is dropping the wake lease due to not receiving any recent user input."
64 );
65 std::mem::drop(lease);
66 } else {
67 log::warn!(
68 "InteractionStateHandler was not holding a wake lease when trying to drop one, please investigate."
69 );
70 }
71 }
72
73 #[cfg(test)]
74 fn is_holding_lease(&self) -> bool {
75 self.wake_lease.is_some()
76 }
77}
78
79pub type NotifyFn = Box<dyn Fn(&State, NotifierWatchStateResponder) -> bool>;
80pub type InteractionStatePublisher = Publisher<State, NotifierWatchStateResponder, NotifyFn>;
81pub type InteractionStateSubscriber = Subscriber<State, NotifierWatchStateResponder, NotifyFn>;
82type InteractionHangingGet = HangingGet<State, NotifierWatchStateResponder, NotifyFn>;
83
84pub struct InteractionStateHandler {
86 idle_threshold_ms: zx::MonotonicDuration,
89
90 idle_transition_task: Cell<Option<TaskHandle<()>>>,
92
93 last_event_time: RefCell<zx::MonotonicInstant>,
95
96 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
100
101 state_publisher: InteractionStatePublisher,
103
104 pub inspect_status: InputHandlerStatus,
106
107 metrics_logger: metrics::MetricsLogger,
109
110 enable_button_baton_passing: bool,
112 enable_mouse_baton_passing: bool,
113 enable_touch_baton_passing: bool,
114}
115
116impl InteractionStateHandler {
117 pub async fn new(
120 incoming: &Incoming,
121 idle_threshold_ms: zx::MonotonicDuration,
122 input_handlers_node: &fuchsia_inspect::Node,
123 metrics_logger: metrics::MetricsLogger,
124 state_publisher: InteractionStatePublisher,
125 suspend_enabled: bool,
126 enable_button_baton_passing: bool,
127 enable_mouse_baton_passing: bool,
128 enable_touch_baton_passing: bool,
129 ) -> Rc<Self> {
130 log::info!(
131 "InteractionStateHandler is initialized with idle_threshold_ms: {:?}",
132 idle_threshold_ms.into_millis()
133 );
134
135 let inspect_status =
136 InputHandlerStatus::new(input_handlers_node, "interaction_state_handler", false);
137
138 let lease_holder = match suspend_enabled {
139 true => {
140 let activity_governor = incoming
141 .connect_protocol::<ActivityGovernorProxy>()
142 .expect("connect to fuchsia.power.system.ActivityGovernor");
143 match LeaseHolder::new(activity_governor).await {
144 Ok(holder) => Some(Rc::new(RefCell::new(holder))),
145 Err(e) => {
146 log::error!(
147 "Unable to integrate with power, system may incorrectly enter suspend: {:?}",
148 e
149 );
150 None
151 }
152 }
153 }
154 false => None,
155 };
156
157 Rc::new(Self::new_internal(
158 idle_threshold_ms,
159 zx::MonotonicInstant::get(),
160 metrics_logger,
161 lease_holder,
162 inspect_status,
163 state_publisher,
164 enable_button_baton_passing,
165 enable_mouse_baton_passing,
166 enable_touch_baton_passing,
167 ))
168 }
169
170 #[cfg(test)]
171 async fn new_for_test(
173 idle_threshold_ms: zx::MonotonicDuration,
174 metrics_logger: metrics::MetricsLogger,
175 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
176 state_publisher: InteractionStatePublisher,
177 ) -> Rc<Self> {
178 fuchsia_async::TestExecutor::advance_to(zx::MonotonicInstant::ZERO.into()).await;
179
180 let inspector = fuchsia_inspect::Inspector::default();
181 let test_node = inspector.root().create_child("test_node");
182 let inspect_status = InputHandlerStatus::new(
183 &test_node,
184 "interaction_state_handler",
185 false,
186 );
187 Rc::new(Self::new_internal(
188 idle_threshold_ms,
189 zx::MonotonicInstant::ZERO,
190 metrics_logger,
191 lease_holder,
192 inspect_status,
193 state_publisher,
194 false,
195 false,
196 false,
197 ))
198 }
199
200 fn new_internal(
201 idle_threshold_ms: zx::MonotonicDuration,
202 initial_timestamp: zx::MonotonicInstant,
203 metrics_logger: metrics::MetricsLogger,
204 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
205 inspect_status: InputHandlerStatus,
206 state_publisher: InteractionStatePublisher,
207 enable_button_baton_passing: bool,
208 enable_mouse_baton_passing: bool,
209 enable_touch_baton_passing: bool,
210 ) -> Self {
211 let task = Self::create_idle_transition_task(
212 initial_timestamp + idle_threshold_ms,
213 state_publisher.clone(),
214 lease_holder.clone(),
215 );
216
217 Self {
218 idle_threshold_ms,
219 idle_transition_task: Cell::new(Some(task)),
220 last_event_time: RefCell::new(initial_timestamp),
221 lease_holder,
222 metrics_logger,
223 state_publisher,
224 inspect_status,
225 enable_button_baton_passing,
226 enable_mouse_baton_passing,
227 enable_touch_baton_passing,
228 }
229 }
230
231 async fn transition_to_active(
232 state_publisher: &InteractionStatePublisher,
233 lease_holder: &Option<Rc<RefCell<LeaseHolder>>>,
234 ) {
235 if let Some(holder) = lease_holder {
236 if let Err(e) = holder.borrow_mut().create_lease().await {
237 log::warn!(
238 "Unable to create lease, system may incorrectly go into suspend: {:?}",
239 e
240 );
241 };
242 }
243 state_publisher.set(State::Active);
244 }
245
246 fn create_idle_transition_task(
247 timeout: zx::MonotonicInstant,
248 state_publisher: InteractionStatePublisher,
249 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
250 ) -> TaskHandle<()> {
251 Dispatcher::spawn_local(async move {
252 Dispatcher::after_deadline(timeout.into()).await;
253 lease_holder.and_then(|holder| Some(holder.borrow_mut().drop_lease()));
254 state_publisher.set(State::Idle);
255 })
256 }
257
258 async fn transition_to_idle_after_new_time(&self, event_time: zx::MonotonicInstant) {
259 if *self.last_event_time.borrow() > event_time {
260 return;
261 }
262
263 *self.last_event_time.borrow_mut() = event_time;
264 if let Some(t) = self.idle_transition_task.take() {
265 if let Some(()) = t.abort().await {
268 Self::transition_to_active(&self.state_publisher, &self.lease_holder).await;
269 }
270 }
271
272 self.idle_transition_task.set(Some(Self::create_idle_transition_task(
273 event_time + self.idle_threshold_ms,
274 self.state_publisher.clone(),
275 self.lease_holder.clone(),
276 )));
277 }
278
279 #[cfg(test)]
280 fn is_holding_lease(&self) -> bool {
281 if let Some(holder) = &self.lease_holder {
282 return holder.borrow().is_holding_lease();
283 }
284
285 false
286 }
287}
288
289pub async fn handle_interaction_notifier_request_stream(
294 mut stream: NotifierRequestStream,
295 subscriber: InteractionStateSubscriber,
296) -> Result<(), Error> {
297 while let Some(notifier_request) = stream.next().await {
298 let NotifierRequest::WatchState { responder } = notifier_request?;
299 subscriber.register(responder)?;
300 }
301
302 Ok(())
303}
304
305pub fn init_interaction_hanging_get() -> InteractionHangingGet {
306 let notify_fn: NotifyFn = Box::new(|state, responder| {
307 if responder.send(*state).is_err() {
308 log::info!("Failed to send user input interaction state");
309 }
310
311 true
312 });
313
314 let initial_state = State::Active;
315 InteractionHangingGet::new(initial_state, notify_fn)
316}
317
318impl Handler for InteractionStateHandler {
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 fn get_name(&self) -> &'static str {
328 "InteractionStateHandler"
329 }
330
331 fn interest(&self) -> Vec<input_device::InputEventType> {
332 let mut interest = vec![];
333 if !self.enable_button_baton_passing {
334 interest.push(input_device::InputEventType::ConsumerControls);
335 }
336 if !self.enable_mouse_baton_passing {
337 interest.push(input_device::InputEventType::Mouse);
338 }
339 if !self.enable_touch_baton_passing {
340 interest.push(input_device::InputEventType::TouchScreen);
341 }
342 interest
343 }
344}
345
346#[async_trait(?Send)]
347impl UnhandledInputHandler for InteractionStateHandler {
348 async fn handle_unhandled_input_event(
351 self: Rc<Self>,
352 unhandled_input_event: input_device::UnhandledInputEvent,
353 ) -> Vec<input_device::InputEvent> {
354 fuchsia_trace::duration!("input", "interaction_state_handler");
355 let trace_id = unhandled_input_event.trace_id.unwrap_or_else(|| 0.into());
356 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id);
357
358 match unhandled_input_event.device_event {
359 input_device::InputDeviceEvent::ConsumerControls(..) => {
360 if self.enable_button_baton_passing {
361 self.metrics_logger.log_error(
362 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
363 "Button event with baton passing".to_string(),
364 );
365 } else {
366 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
367 let event_time = unhandled_input_event
373 .event_time
374 .clamp(zx::MonotonicInstant::ZERO, MonotonicInstant::now().into_zx());
375
376 self.inspect_status.count_received_event(&event_time);
377 self.transition_to_idle_after_new_time(event_time).await;
378 }
379 }
380 input_device::InputDeviceEvent::Mouse(..) => {
381 if self.enable_mouse_baton_passing {
382 self.metrics_logger.log_error(
383 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
384 "Mouse event with baton passing".to_string(),
385 );
386 } else {
387 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
388 let event_time = unhandled_input_event
394 .event_time
395 .clamp(zx::MonotonicInstant::ZERO, MonotonicInstant::now().into_zx());
396
397 self.inspect_status.count_received_event(&event_time);
398 self.transition_to_idle_after_new_time(event_time).await;
399 }
400 }
401 input_device::InputDeviceEvent::TouchScreen(..) => {
402 if self.enable_touch_baton_passing {
403 self.metrics_logger.log_error(
404 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
405 "Touch event with baton passing".to_string(),
406 );
407 } else {
408 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
409 let event_time = unhandled_input_event
415 .event_time
416 .clamp(zx::MonotonicInstant::ZERO, MonotonicInstant::now().into_zx());
417
418 self.inspect_status.count_received_event(&event_time);
419 self.transition_to_idle_after_new_time(event_time).await;
420 }
421 }
422 _ => {
423 self.metrics_logger.log_error(
424 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
425 std::format!(
426 "uninterested input event: {:?}",
427 unhandled_input_event.get_event_type()
428 ),
429 );
430 }
431 }
432
433 vec![input_device::InputEvent::from(unhandled_input_event)]
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 use crate::mouse_binding;
441 use crate::testing_utilities::{
442 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
443 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
444 get_touch_screen_device_descriptor,
445 };
446 use crate::utils::Position;
447 use assert_matches::assert_matches;
448 use async_utils::hanging_get::client::HangingGetStream;
449 use fidl::endpoints::create_proxy_and_stream;
450 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
451 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
452 use fidl_fuchsia_ui_input::PointerEventPhase;
453 use fuchsia_async::{Task, TestExecutor};
454 use futures::pin_mut;
455 use maplit::hashmap;
456 use std::collections::HashSet;
457 use std::task::Poll;
458 use test_case::test_case;
459
460 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
461
462 async fn create_interaction_state_handler_and_notifier_proxy(
463 suspend_enabled: bool,
464 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
465 let mut interaction_hanging_get = init_interaction_hanging_get();
466
467 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
468 let stream_fut = handle_interaction_notifier_request_stream(
469 notifier_stream,
470 interaction_hanging_get.new_subscriber(),
471 );
472
473 Task::local(async move {
474 if stream_fut.await.is_err() {
475 panic!("Failed to handle notifier request stream");
476 }
477 })
478 .detach();
479
480 let lease_holder = match suspend_enabled {
481 true => {
482 let holder = LeaseHolder::new(fake_activity_governor_server())
483 .await
484 .expect("create lease holder for test");
485 Some(Rc::new(RefCell::new(holder)))
486 }
487 false => None,
488 };
489
490 (
491 InteractionStateHandler::new_for_test(
492 ACTIVITY_TIMEOUT,
493 metrics::MetricsLogger::default(),
494 lease_holder,
495 interaction_hanging_get.new_publisher(),
496 )
497 .await,
498 notifier_proxy,
499 )
500 }
501
502 fn fake_activity_governor_server() -> ActivityGovernorProxy {
503 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
504 Task::local(async move {
505 while let Some(request) = stream.next().await {
506 match request {
507 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
508 let (_, fake_wake_lease) = zx::EventPair::create();
509 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
510 }
511 Ok(unexpected) => {
512 log::warn!(
513 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
514 );
515 }
516 Err(e) => {
517 log::warn!(
518 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
519 e
520 );
521 }
522 }
523 }
524 })
525 .detach();
526
527 proxy
528 }
529
530 #[test_case(true; "Suspend enabled")]
531 #[test_case(false; "Suspend disabled")]
532 #[fuchsia::test(allow_stalls = false)]
533 async fn notifier_sends_initial_state(suspend_enabled: bool) {
534 let (interaction_state_handler, notifier_proxy) =
535 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
536 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
537 assert_eq!(state, State::Active);
538 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
539 }
540
541 #[test_case(true; "Suspend enabled")]
542 #[test_case(false; "Suspend disabled")]
543 #[fuchsia::test]
544 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
545 let mut executor = TestExecutor::new_with_fake_time();
546
547 let handler_and_proxy_fut =
548 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
549 pin_mut!(handler_and_proxy_fut);
550 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
551 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
552 Poll::Ready((handler, proxy)) => (handler, proxy),
553 _ => panic!("Unable to create interaction state handler and proxy"),
554 };
555
556 let mut watch_state_stream =
558 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
559 let state_fut = watch_state_stream.next();
560 pin_mut!(state_fut);
561 let initial_state = executor.run_until_stalled(&mut state_fut);
562 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
563 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
564
565 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
567
568 let idle_state_fut = watch_state_stream.next();
570 pin_mut!(idle_state_fut);
571 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
572 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
573 assert_eq!(interaction_state_handler.is_holding_lease(), false);
574
575 Ok(())
576 }
577
578 #[test_case(true; "Suspend enabled")]
579 #[test_case(false; "Suspend disabled")]
580 #[fuchsia::test]
581 fn interaction_state_handler_drops_first_timer_on_activity(
582 suspend_enabled: bool,
583 ) -> Result<(), Error> {
584 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
598
599 let mut executor = TestExecutor::new_with_fake_time();
600
601 let handler_and_proxy_fut =
602 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
603 pin_mut!(handler_and_proxy_fut);
604 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
605 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
606 Poll::Ready((handler, proxy)) => (handler, proxy),
607 _ => panic!("Unable to create interaction state handler and proxy"),
608 };
609
610 let mut watch_state_stream =
612 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
613 let state_fut = watch_state_stream.next();
614 pin_mut!(state_fut);
615 let initial_state = executor.run_until_stalled(&mut state_fut);
616 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
617 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
618
619 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
621
622 let input_event =
624 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
625 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
626 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
627 ACTIVITY_TIMEOUT / 2,
628 )),
629 &consumer_controls_device_descriptor(),
630 ))
631 .unwrap();
632
633 let mut handle_event_fut =
634 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
635 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
636 assert!(handle_result.is_ready());
637
638 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
640
641 let watch_state_fut = watch_state_stream.next();
643 pin_mut!(watch_state_fut);
644 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
645 assert_matches!(watch_state_res, Poll::Pending);
646 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
647
648 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
650
651 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
653 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
654 assert_eq!(interaction_state_handler.is_holding_lease(), false);
655
656 Ok(())
657 }
658
659 #[test_case(true; "Suspend enabled")]
660 #[test_case(false; "Suspend disabled")]
661 #[fuchsia::test]
662 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
663 let mut executor = TestExecutor::new_with_fake_time();
664
665 let handler_and_proxy_fut =
666 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
667 pin_mut!(handler_and_proxy_fut);
668 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
669 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
670 Poll::Ready((handler, proxy)) => (handler, proxy),
671 _ => panic!("Unable to create interaction state handler and proxy"),
672 };
673
674 let mut watch_state_stream =
676 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
677 let state_fut = watch_state_stream.next();
678 pin_mut!(state_fut);
679 let watch_state_res = executor.run_until_stalled(&mut state_fut);
680 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
681 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
682
683 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
685
686 let input_event =
688 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
689 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
690 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
691 ACTIVITY_TIMEOUT / 2,
692 )),
693 &consumer_controls_device_descriptor(),
694 ))
695 .unwrap();
696
697 let mut handle_event_fut =
698 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
699 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
700 assert!(handle_result.is_ready());
701
702 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
704
705 let input_event =
707 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
708 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
709 zx::MonotonicInstant::ZERO,
710 &consumer_controls_device_descriptor(),
711 ))
712 .unwrap();
713
714 let mut handle_event_fut =
715 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
716 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
717 assert!(handle_result.is_ready());
718
719 let watch_state_fut = watch_state_stream.next();
722 pin_mut!(watch_state_fut);
723 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
724 assert_matches!(initial_state, Poll::Pending);
725 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
726
727 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
729
730 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
732 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
733 assert_eq!(interaction_state_handler.is_holding_lease(), false);
734
735 Ok(())
736 }
737
738 #[test_case(true; "Suspend enabled")]
739 #[test_case(false; "Suspend disabled")]
740 #[fuchsia::test]
741 fn notifier_sends_active_state_with_button_input_event(
742 suspend_enabled: bool,
743 ) -> Result<(), Error> {
744 let mut executor = TestExecutor::new_with_fake_time();
745
746 let handler_and_proxy_fut =
747 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
748 pin_mut!(handler_and_proxy_fut);
749 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
750 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
751 Poll::Ready((handler, proxy)) => (handler, proxy),
752 _ => panic!("Unable to create interaction state handler and proxy"),
753 };
754
755 let mut watch_state_stream =
757 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
758 let state_fut = watch_state_stream.next();
759 pin_mut!(state_fut);
760 let initial_state = executor.run_until_stalled(&mut 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 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
766
767 let idle_state_fut = watch_state_stream.next();
769 pin_mut!(idle_state_fut);
770 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
771 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
772 assert_eq!(interaction_state_handler.is_holding_lease(), false);
773
774 let input_event =
776 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
777 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
778 zx::MonotonicInstant::get(),
779 &consumer_controls_device_descriptor(),
780 ))
781 .unwrap();
782
783 let mut handle_event_fut =
784 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
785 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
786
787 match handle_result {
789 Poll::Ready(res) => assert_matches!(
790 res.as_slice(),
791 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
792 ),
793 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
794 };
795
796 let active_state_fut = watch_state_stream.next();
798 pin_mut!(active_state_fut);
799 let initial_state = executor.run_until_stalled(&mut active_state_fut);
800 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
801 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
802
803 Ok(())
804 }
805
806 #[test_case(true; "Suspend enabled")]
807 #[test_case(false; "Suspend disabled")]
808 #[fuchsia::test]
809 fn notifier_sends_active_state_with_mouse_input_event(
810 suspend_enabled: bool,
811 ) -> Result<(), Error> {
812 let mut executor = TestExecutor::new_with_fake_time();
813
814 let handler_and_proxy_fut =
815 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
816 pin_mut!(handler_and_proxy_fut);
817 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
818 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
819 Poll::Ready((handler, proxy)) => (handler, proxy),
820 _ => panic!("Unable to create interaction state handler and proxy"),
821 };
822
823 let mut watch_state_stream =
825 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
826 let state_fut = watch_state_stream.next();
827 pin_mut!(state_fut);
828 let initial_state = executor.run_until_stalled(&mut state_fut);
829 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
830 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
831
832 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
834
835 let idle_state_fut = watch_state_stream.next();
837 pin_mut!(idle_state_fut);
838 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
839 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
840 assert_eq!(interaction_state_handler.is_holding_lease(), false);
841
842 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
844 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
845 None, None, None, mouse_binding::MousePhase::Down,
849 HashSet::new(),
850 HashSet::new(),
851 zx::MonotonicInstant::get(),
852 &get_mouse_device_descriptor(),
853 ))
854 .unwrap();
855
856 let mut handle_event_fut =
857 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
858 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
859
860 match handle_result {
862 Poll::Ready(res) => assert_matches!(
863 res.as_slice(),
864 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
865 ),
866 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
867 };
868
869 let active_state_fut = watch_state_stream.next();
871 pin_mut!(active_state_fut);
872 let initial_state = executor.run_until_stalled(&mut active_state_fut);
873 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
874 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
875
876 Ok(())
877 }
878
879 #[test_case(true; "Suspend enabled")]
880 #[test_case(false; "Suspend disabled")]
881 #[fuchsia::test]
882 fn notifier_sends_active_state_with_touch_input_event(
883 suspend_enabled: bool,
884 ) -> Result<(), Error> {
885 let mut executor = TestExecutor::new_with_fake_time();
886
887 let handler_and_proxy_fut =
888 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
889 pin_mut!(handler_and_proxy_fut);
890 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
891 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
892 Poll::Ready((handler, proxy)) => (handler, proxy),
893 _ => panic!("Unable to create interaction state handler and proxy"),
894 };
895
896 let mut watch_state_stream =
898 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
899 let state_fut = watch_state_stream.next();
900 pin_mut!(state_fut);
901 let initial_state = executor.run_until_stalled(&mut state_fut);
902 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
903 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
904
905 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
907
908 let idle_state_fut = watch_state_stream.next();
910 pin_mut!(idle_state_fut);
911 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
912 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
913 assert_eq!(interaction_state_handler.is_holding_lease(), false);
914
915 const TOUCH_ID: u32 = 1;
917 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
918 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
919 hashmap! {
920 PointerEventPhase::Add
921 => vec![contact.clone()],
922 },
923 zx::MonotonicInstant::get(),
924 &get_touch_screen_device_descriptor(),
925 ))
926 .unwrap();
927
928 let mut handle_event_fut =
929 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
930 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
931
932 match handle_result {
934 Poll::Ready(res) => assert_matches!(
935 res.as_slice(),
936 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
937 ),
938 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
939 };
940
941 let active_state_fut = watch_state_stream.next();
943 pin_mut!(active_state_fut);
944 let initial_state = executor.run_until_stalled(&mut active_state_fut);
945 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
946 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
947
948 Ok(())
949 }
950}