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