1#![cfg(fuchsia_api_level_at_least = "HEAD")]
5use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
6use crate::{input_device, metrics};
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 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<Task<()>>>,
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 idle_threshold_ms: zx::MonotonicDuration,
121 input_handlers_node: &fuchsia_inspect::Node,
122 metrics_logger: metrics::MetricsLogger,
123 state_publisher: InteractionStatePublisher,
124 suspend_enabled: bool,
125 enable_button_baton_passing: bool,
126 enable_mouse_baton_passing: bool,
127 enable_touch_baton_passing: bool,
128 ) -> Rc<Self> {
129 log::info!(
130 "InteractionStateHandler is initialized with idle_threshold_ms: {:?}",
131 idle_threshold_ms.into_millis()
132 );
133
134 let inspect_status =
135 InputHandlerStatus::new(input_handlers_node, "interaction_state_handler", false);
136
137 let lease_holder = match suspend_enabled {
138 true => {
139 let activity_governor = connect_to_protocol::<ActivityGovernorMarker>()
140 .expect("connect to fuchsia.power.system.ActivityGovernor");
141 match LeaseHolder::new(activity_governor).await {
142 Ok(holder) => Some(Rc::new(RefCell::new(holder))),
143 Err(e) => {
144 log::error!(
145 "Unable to integrate with power, system may incorrectly enter suspend: {:?}",
146 e
147 );
148 None
149 }
150 }
151 }
152 false => None,
153 };
154
155 Rc::new(Self::new_internal(
156 idle_threshold_ms,
157 zx::MonotonicInstant::get(),
158 metrics_logger,
159 lease_holder,
160 inspect_status,
161 state_publisher,
162 enable_button_baton_passing,
163 enable_mouse_baton_passing,
164 enable_touch_baton_passing,
165 ))
166 }
167
168 #[cfg(test)]
169 async fn new_for_test(
171 idle_threshold_ms: zx::MonotonicDuration,
172 metrics_logger: metrics::MetricsLogger,
173 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
174 state_publisher: InteractionStatePublisher,
175 ) -> Rc<Self> {
176 fuchsia_async::TestExecutor::advance_to(zx::MonotonicInstant::ZERO.into()).await;
177
178 let inspector = fuchsia_inspect::Inspector::default();
179 let test_node = inspector.root().create_child("test_node");
180 let inspect_status = InputHandlerStatus::new(
181 &test_node,
182 "interaction_state_handler",
183 false,
184 );
185 Rc::new(Self::new_internal(
186 idle_threshold_ms,
187 zx::MonotonicInstant::ZERO,
188 metrics_logger,
189 lease_holder,
190 inspect_status,
191 state_publisher,
192 false,
193 false,
194 false,
195 ))
196 }
197
198 fn new_internal(
199 idle_threshold_ms: zx::MonotonicDuration,
200 initial_timestamp: zx::MonotonicInstant,
201 metrics_logger: metrics::MetricsLogger,
202 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
203 inspect_status: InputHandlerStatus,
204 state_publisher: InteractionStatePublisher,
205 enable_button_baton_passing: bool,
206 enable_mouse_baton_passing: bool,
207 enable_touch_baton_passing: bool,
208 ) -> Self {
209 let task = Self::create_idle_transition_task(
210 initial_timestamp + idle_threshold_ms,
211 state_publisher.clone(),
212 lease_holder.clone(),
213 );
214
215 Self {
216 idle_threshold_ms,
217 idle_transition_task: Cell::new(Some(task)),
218 last_event_time: RefCell::new(initial_timestamp),
219 lease_holder,
220 metrics_logger,
221 state_publisher,
222 inspect_status,
223 enable_button_baton_passing,
224 enable_mouse_baton_passing,
225 enable_touch_baton_passing,
226 }
227 }
228
229 async fn transition_to_active(
230 state_publisher: &InteractionStatePublisher,
231 lease_holder: &Option<Rc<RefCell<LeaseHolder>>>,
232 ) {
233 if let Some(holder) = lease_holder {
234 if let Err(e) = holder.borrow_mut().create_lease().await {
235 log::warn!(
236 "Unable to create lease, system may incorrectly go into suspend: {:?}",
237 e
238 );
239 };
240 }
241 state_publisher.set(State::Active);
242 }
243
244 fn create_idle_transition_task(
245 timeout: zx::MonotonicInstant,
246 state_publisher: InteractionStatePublisher,
247 lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
248 ) -> Task<()> {
249 Task::local(async move {
250 Timer::new(timeout).await;
251 lease_holder.and_then(|holder| Some(holder.borrow_mut().drop_lease()));
252 state_publisher.set(State::Idle);
253 })
254 }
255
256 async fn transition_to_idle_after_new_time(&self, event_time: zx::MonotonicInstant) {
257 if *self.last_event_time.borrow() > event_time {
258 return;
259 }
260
261 *self.last_event_time.borrow_mut() = event_time;
262 if let Some(t) = self.idle_transition_task.take() {
263 if let Some(()) = t.abort().await {
266 Self::transition_to_active(&self.state_publisher, &self.lease_holder).await;
267 }
268 }
269
270 self.idle_transition_task.set(Some(Self::create_idle_transition_task(
271 event_time + self.idle_threshold_ms,
272 self.state_publisher.clone(),
273 self.lease_holder.clone(),
274 )));
275 }
276
277 #[cfg(test)]
278 fn is_holding_lease(&self) -> bool {
279 if let Some(holder) = &self.lease_holder {
280 return holder.borrow().is_holding_lease();
281 }
282
283 false
284 }
285}
286
287pub async fn handle_interaction_notifier_request_stream(
292 mut stream: NotifierRequestStream,
293 subscriber: InteractionStateSubscriber,
294) -> Result<(), Error> {
295 while let Some(notifier_request) = stream.next().await {
296 let NotifierRequest::WatchState { responder } = notifier_request?;
297 subscriber.register(responder)?;
298 }
299
300 Ok(())
301}
302
303pub fn init_interaction_hanging_get() -> InteractionHangingGet {
304 let notify_fn: NotifyFn = Box::new(|state, responder| {
305 if responder.send(*state).is_err() {
306 log::info!("Failed to send user input interaction state");
307 }
308
309 true
310 });
311
312 let initial_state = State::Active;
313 InteractionHangingGet::new(initial_state, notify_fn)
314}
315
316impl Handler for InteractionStateHandler {
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 fn get_name(&self) -> &'static str {
326 "InteractionStateHandler"
327 }
328
329 fn interest(&self) -> Vec<input_device::InputEventType> {
330 let mut interest = vec![];
331 if !self.enable_button_baton_passing {
332 interest.push(input_device::InputEventType::ConsumerControls);
333 }
334 if !self.enable_mouse_baton_passing {
335 interest.push(input_device::InputEventType::Mouse);
336 }
337 if !self.enable_touch_baton_passing {
338 interest.push(input_device::InputEventType::TouchScreen);
339 }
340 interest
341 }
342}
343
344#[async_trait(?Send)]
345impl UnhandledInputHandler for InteractionStateHandler {
346 async fn handle_unhandled_input_event(
349 self: Rc<Self>,
350 unhandled_input_event: input_device::UnhandledInputEvent,
351 ) -> Vec<input_device::InputEvent> {
352 fuchsia_trace::duration!("input", "interaction_state_handler");
353 let trace_id = unhandled_input_event.trace_id.unwrap_or_else(|| 0.into());
354 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id);
355
356 match unhandled_input_event.device_event {
357 input_device::InputDeviceEvent::ConsumerControls(..) => {
358 if self.enable_button_baton_passing {
359 self.metrics_logger.log_error(
360 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
361 "Button event with baton passing".to_string(),
362 );
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 input_device::InputDeviceEvent::Mouse(..) => {
380 if self.enable_mouse_baton_passing {
381 self.metrics_logger.log_error(
382 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
383 "Mouse event with baton passing".to_string(),
384 );
385 } else {
386 fuchsia_trace::duration!("input", "interaction_state_handler[processing]");
387 let event_time = unhandled_input_event.event_time.clamp(
393 zx::MonotonicInstant::ZERO,
394 fuchsia_async::MonotonicInstant::now().into_zx(),
395 );
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.event_time.clamp(
415 zx::MonotonicInstant::ZERO,
416 fuchsia_async::MonotonicInstant::now().into_zx(),
417 );
418
419 self.inspect_status.count_received_event(&event_time);
420 self.transition_to_idle_after_new_time(event_time).await;
421 }
422 }
423 _ => {
424 self.metrics_logger.log_error(
425 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
426 std::format!(
427 "uninterested input event: {:?}",
428 unhandled_input_event.get_event_type()
429 ),
430 );
431 }
432 }
433
434 vec![input_device::InputEvent::from(unhandled_input_event)]
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441 use crate::mouse_binding;
442 use crate::testing_utilities::{
443 consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
444 create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
445 get_touch_screen_device_descriptor,
446 };
447 use crate::utils::Position;
448 use assert_matches::assert_matches;
449 use async_utils::hanging_get::client::HangingGetStream;
450 use fidl::endpoints::create_proxy_and_stream;
451 use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
452 use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
453 use fidl_fuchsia_ui_input::PointerEventPhase;
454 use fuchsia_async::TestExecutor;
455 use futures::pin_mut;
456 use maplit::hashmap;
457 use std::collections::HashSet;
458 use std::task::Poll;
459 use test_case::test_case;
460
461 const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
462
463 async fn create_interaction_state_handler_and_notifier_proxy(
464 suspend_enabled: bool,
465 ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
466 let mut interaction_hanging_get = init_interaction_hanging_get();
467
468 let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
469 let stream_fut = handle_interaction_notifier_request_stream(
470 notifier_stream,
471 interaction_hanging_get.new_subscriber(),
472 );
473
474 Task::local(async move {
475 if stream_fut.await.is_err() {
476 panic!("Failed to handle notifier request stream");
477 }
478 })
479 .detach();
480
481 let lease_holder = match suspend_enabled {
482 true => {
483 let holder = LeaseHolder::new(fake_activity_governor_server())
484 .await
485 .expect("create lease holder for test");
486 Some(Rc::new(RefCell::new(holder)))
487 }
488 false => None,
489 };
490
491 (
492 InteractionStateHandler::new_for_test(
493 ACTIVITY_TIMEOUT,
494 metrics::MetricsLogger::default(),
495 lease_holder,
496 interaction_hanging_get.new_publisher(),
497 )
498 .await,
499 notifier_proxy,
500 )
501 }
502
503 fn fake_activity_governor_server() -> ActivityGovernorProxy {
504 let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
505 Task::local(async move {
506 while let Some(request) = stream.next().await {
507 match request {
508 Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
509 let (_, fake_wake_lease) = zx::EventPair::create();
510 responder.send(fake_wake_lease).expect("failed to send fake wake lease");
511 }
512 Ok(unexpected) => {
513 log::warn!(
514 "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
515 );
516 }
517 Err(e) => {
518 log::warn!(
519 "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
520 e
521 );
522 }
523 }
524 }
525 })
526 .detach();
527
528 proxy
529 }
530
531 #[test_case(true; "Suspend enabled")]
532 #[test_case(false; "Suspend disabled")]
533 #[fuchsia::test(allow_stalls = false)]
534 async fn notifier_sends_initial_state(suspend_enabled: bool) {
535 let (interaction_state_handler, notifier_proxy) =
536 create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
537 let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
538 assert_eq!(state, State::Active);
539 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
540 }
541
542 #[test_case(true; "Suspend enabled")]
543 #[test_case(false; "Suspend disabled")]
544 #[fuchsia::test]
545 fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
546 let mut executor = TestExecutor::new_with_fake_time();
547
548 let handler_and_proxy_fut =
549 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
550 pin_mut!(handler_and_proxy_fut);
551 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
552 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
553 Poll::Ready((handler, proxy)) => (handler, proxy),
554 _ => panic!("Unable to create interaction state handler and proxy"),
555 };
556
557 let mut watch_state_stream =
559 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
560 let state_fut = watch_state_stream.next();
561 pin_mut!(state_fut);
562 let initial_state = executor.run_until_stalled(&mut state_fut);
563 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
564 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
565
566 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
568
569 let idle_state_fut = watch_state_stream.next();
571 pin_mut!(idle_state_fut);
572 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
573 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
574 assert_eq!(interaction_state_handler.is_holding_lease(), false);
575
576 Ok(())
577 }
578
579 #[test_case(true; "Suspend enabled")]
580 #[test_case(false; "Suspend disabled")]
581 #[fuchsia::test]
582 fn interaction_state_handler_drops_first_timer_on_activity(
583 suspend_enabled: bool,
584 ) -> Result<(), Error> {
585 assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
599
600 let mut executor = TestExecutor::new_with_fake_time();
601
602 let handler_and_proxy_fut =
603 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
604 pin_mut!(handler_and_proxy_fut);
605 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
606 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
607 Poll::Ready((handler, proxy)) => (handler, proxy),
608 _ => panic!("Unable to create interaction state handler and proxy"),
609 };
610
611 let mut watch_state_stream =
613 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
614 let state_fut = watch_state_stream.next();
615 pin_mut!(state_fut);
616 let initial_state = executor.run_until_stalled(&mut state_fut);
617 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
618 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
619
620 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
622
623 let input_event =
625 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
626 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
627 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
628 ACTIVITY_TIMEOUT / 2,
629 )),
630 &consumer_controls_device_descriptor(),
631 ))
632 .unwrap();
633
634 let mut handle_event_fut =
635 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
636 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
637 assert!(handle_result.is_ready());
638
639 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
641
642 let watch_state_fut = watch_state_stream.next();
644 pin_mut!(watch_state_fut);
645 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
646 assert_matches!(watch_state_res, Poll::Pending);
647 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
648
649 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
651
652 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
654 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
655 assert_eq!(interaction_state_handler.is_holding_lease(), false);
656
657 Ok(())
658 }
659
660 #[test_case(true; "Suspend enabled")]
661 #[test_case(false; "Suspend disabled")]
662 #[fuchsia::test]
663 fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
664 let mut executor = TestExecutor::new_with_fake_time();
665
666 let handler_and_proxy_fut =
667 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
668 pin_mut!(handler_and_proxy_fut);
669 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
670 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
671 Poll::Ready((handler, proxy)) => (handler, proxy),
672 _ => panic!("Unable to create interaction state handler and proxy"),
673 };
674
675 let mut watch_state_stream =
677 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
678 let state_fut = watch_state_stream.next();
679 pin_mut!(state_fut);
680 let watch_state_res = executor.run_until_stalled(&mut state_fut);
681 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
682 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
683
684 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
686
687 let input_event =
689 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
690 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
691 zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
692 ACTIVITY_TIMEOUT / 2,
693 )),
694 &consumer_controls_device_descriptor(),
695 ))
696 .unwrap();
697
698 let mut handle_event_fut =
699 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
700 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
701 assert!(handle_result.is_ready());
702
703 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
705
706 let input_event =
708 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
709 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
710 zx::MonotonicInstant::ZERO,
711 &consumer_controls_device_descriptor(),
712 ))
713 .unwrap();
714
715 let mut handle_event_fut =
716 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
717 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
718 assert!(handle_result.is_ready());
719
720 let watch_state_fut = watch_state_stream.next();
723 pin_mut!(watch_state_fut);
724 let initial_state = executor.run_until_stalled(&mut watch_state_fut);
725 assert_matches!(initial_state, Poll::Pending);
726 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
727
728 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
730
731 let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
733 assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
734 assert_eq!(interaction_state_handler.is_holding_lease(), false);
735
736 Ok(())
737 }
738
739 #[test_case(true; "Suspend enabled")]
740 #[test_case(false; "Suspend disabled")]
741 #[fuchsia::test]
742 fn notifier_sends_active_state_with_button_input_event(
743 suspend_enabled: bool,
744 ) -> Result<(), Error> {
745 let mut executor = TestExecutor::new_with_fake_time();
746
747 let handler_and_proxy_fut =
748 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
749 pin_mut!(handler_and_proxy_fut);
750 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
751 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
752 Poll::Ready((handler, proxy)) => (handler, proxy),
753 _ => panic!("Unable to create interaction state handler and proxy"),
754 };
755
756 let mut watch_state_stream =
758 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
759 let state_fut = watch_state_stream.next();
760 pin_mut!(state_fut);
761 let initial_state = executor.run_until_stalled(&mut 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 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
767
768 let idle_state_fut = watch_state_stream.next();
770 pin_mut!(idle_state_fut);
771 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
772 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
773 assert_eq!(interaction_state_handler.is_holding_lease(), false);
774
775 let input_event =
777 input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
778 vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
779 zx::MonotonicInstant::get(),
780 &consumer_controls_device_descriptor(),
781 ))
782 .unwrap();
783
784 let mut handle_event_fut =
785 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
786 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
787
788 match handle_result {
790 Poll::Ready(res) => assert_matches!(
791 res.as_slice(),
792 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
793 ),
794 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
795 };
796
797 let active_state_fut = watch_state_stream.next();
799 pin_mut!(active_state_fut);
800 let initial_state = executor.run_until_stalled(&mut active_state_fut);
801 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
802 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
803
804 Ok(())
805 }
806
807 #[test_case(true; "Suspend enabled")]
808 #[test_case(false; "Suspend disabled")]
809 #[fuchsia::test]
810 fn notifier_sends_active_state_with_mouse_input_event(
811 suspend_enabled: bool,
812 ) -> Result<(), Error> {
813 let mut executor = TestExecutor::new_with_fake_time();
814
815 let handler_and_proxy_fut =
816 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
817 pin_mut!(handler_and_proxy_fut);
818 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
819 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
820 Poll::Ready((handler, proxy)) => (handler, proxy),
821 _ => panic!("Unable to create interaction state handler and proxy"),
822 };
823
824 let mut watch_state_stream =
826 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
827 let state_fut = watch_state_stream.next();
828 pin_mut!(state_fut);
829 let initial_state = executor.run_until_stalled(&mut state_fut);
830 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
831 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
832
833 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
835
836 let idle_state_fut = watch_state_stream.next();
838 pin_mut!(idle_state_fut);
839 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
840 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
841 assert_eq!(interaction_state_handler.is_holding_lease(), false);
842
843 let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
845 mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
846 None, None, None, mouse_binding::MousePhase::Down,
850 HashSet::new(),
851 HashSet::new(),
852 zx::MonotonicInstant::get(),
853 &get_mouse_device_descriptor(),
854 ))
855 .unwrap();
856
857 let mut handle_event_fut =
858 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
859 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
860
861 match handle_result {
863 Poll::Ready(res) => assert_matches!(
864 res.as_slice(),
865 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
866 ),
867 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
868 };
869
870 let active_state_fut = watch_state_stream.next();
872 pin_mut!(active_state_fut);
873 let initial_state = executor.run_until_stalled(&mut active_state_fut);
874 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
875 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
876
877 Ok(())
878 }
879
880 #[test_case(true; "Suspend enabled")]
881 #[test_case(false; "Suspend disabled")]
882 #[fuchsia::test]
883 fn notifier_sends_active_state_with_touch_input_event(
884 suspend_enabled: bool,
885 ) -> Result<(), Error> {
886 let mut executor = TestExecutor::new_with_fake_time();
887
888 let handler_and_proxy_fut =
889 create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
890 pin_mut!(handler_and_proxy_fut);
891 let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
892 let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
893 Poll::Ready((handler, proxy)) => (handler, proxy),
894 _ => panic!("Unable to create interaction state handler and proxy"),
895 };
896
897 let mut watch_state_stream =
899 HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
900 let state_fut = watch_state_stream.next();
901 pin_mut!(state_fut);
902 let initial_state = executor.run_until_stalled(&mut state_fut);
903 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
904 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
905
906 executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
908
909 let idle_state_fut = watch_state_stream.next();
911 pin_mut!(idle_state_fut);
912 let initial_state = executor.run_until_stalled(&mut idle_state_fut);
913 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
914 assert_eq!(interaction_state_handler.is_holding_lease(), false);
915
916 const TOUCH_ID: u32 = 1;
918 let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
919 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
920 hashmap! {
921 PointerEventPhase::Add
922 => vec![contact.clone()],
923 },
924 zx::MonotonicInstant::get(),
925 &get_touch_screen_device_descriptor(),
926 ))
927 .unwrap();
928
929 let mut handle_event_fut =
930 interaction_state_handler.clone().handle_unhandled_input_event(input_event);
931 let handle_result = executor.run_until_stalled(&mut handle_event_fut);
932
933 match handle_result {
935 Poll::Ready(res) => assert_matches!(
936 res.as_slice(),
937 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
938 ),
939 x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
940 };
941
942 let active_state_fut = watch_state_stream.next();
944 pin_mut!(active_state_fut);
945 let initial_state = executor.run_until_stalled(&mut active_state_fut);
946 assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
947 assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
948
949 Ok(())
950 }
951}