input_pipeline/
interaction_state_handler.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#![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
83/// An [`InteractionStateHandler`] tracks the state of user input interaction.
84pub struct InteractionStateHandler {
85    // When `idle_threshold_ms` has transpired since the last user input
86    // interaction, the user interaction state will transition from active to idle.
87    idle_threshold_ms: zx::MonotonicDuration,
88
89    // The task holding the timer-based idle transition after last user input.
90    idle_transition_task: Cell<Option<Task<()>>>,
91
92    // The event time of the last user input interaction.
93    last_event_time: RefCell<zx::MonotonicInstant>,
94
95    // To support power management, the caller must provide `Some` value for
96    // `lease_holder`. The existence of a `LeaseHolder` implies power framework
97    // availability in the platform.
98    lease_holder: Option<Rc<RefCell<LeaseHolder>>>,
99
100    // The publisher used to set active/idle state with hanging-get subscribers.
101    state_publisher: InteractionStatePublisher,
102
103    /// The inventory of this handler's Inspect status.
104    pub inspect_status: InputHandlerStatus,
105
106    // TODO(b/443729860): Remove these temporary feature flags once enabled.
107    enable_button_baton_passing: bool,
108    enable_mouse_baton_passing: bool,
109    enable_touch_baton_passing: bool,
110}
111
112impl InteractionStateHandler {
113    /// Creates a new [`InteractionStateHandler`] that listens for user input
114    /// input interactions and notifies clients of interaction state changes.
115    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    /// Sets the initial idleness timer relative to fake time at 0 for tests.
164    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            /* generates_events */ 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            /* enable_button_baton_passing */ false,
185            /* enable_mouse_baton_passing */ false,
186            /* enable_touch_baton_passing */ 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 the task returns a completed output, we can assume the
254            // state has transitioned to Idle.
255            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
277/// Handles the request stream for fuchsia.input.interaction.Notifier.
278///
279/// # Parameters
280/// `stream`: The `NotifierRequestStream` to be handled.
281pub 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    /// This InputHandler doesn't consume any input events.
309    /// It just passes them on to the next handler in the pipeline.
310    async fn handle_unhandled_input_event(
311        self: Rc<Self>,
312        unhandled_input_event: input_device::UnhandledInputEvent,
313    ) -> Vec<input_device::InputEvent> {
314        fuchsia_trace::duration!(c"input", c"interaction_state_handler");
315        match unhandled_input_event.device_event {
316            input_device::InputDeviceEvent::ConsumerControls(ref consumer_controls_event) => {
317                if self.enable_button_baton_passing {
318                    if !consumer_controls_event.wake_lease.is_some() {
319                        log::debug!(
320                            "InteractionStateHandler received a button event without a wake lease, even though one was expected."
321                        );
322                    }
323                } else {
324                    fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
325                    // Clamp the time to now so that clients cannot send events far off
326                    // in the future to keep the system always active.
327                    // Note: We use the global executor to get the current time instead
328                    // of the kernel so that we do not unnecessarily clamp
329                    // test-injected times.
330                    let event_time = unhandled_input_event.event_time.clamp(
331                        zx::MonotonicInstant::ZERO,
332                        fuchsia_async::MonotonicInstant::now().into_zx(),
333                    );
334
335                    self.inspect_status.count_received_event(&event_time);
336                    self.transition_to_idle_after_new_time(event_time).await;
337                }
338            }
339            input_device::InputDeviceEvent::Mouse(ref mouse_event) => {
340                if self.enable_mouse_baton_passing {
341                    if !mouse_event.wake_lease.lock().is_some() {
342                        log::debug!(
343                            "InteractionStateHandler received a mouse event without a wake lease, even though one was expected."
344                        );
345                    }
346                } else {
347                    fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
348                    // Clamp the time to now so that clients cannot send events far off
349                    // in the future to keep the system always active.
350                    // Note: We use the global executor to get the current time instead
351                    // of the kernel so that we do not unnecessarily clamp
352                    // test-injected times.
353                    let event_time = unhandled_input_event.event_time.clamp(
354                        zx::MonotonicInstant::ZERO,
355                        fuchsia_async::MonotonicInstant::now().into_zx(),
356                    );
357
358                    self.inspect_status.count_received_event(&event_time);
359                    self.transition_to_idle_after_new_time(event_time).await;
360                }
361            }
362            input_device::InputDeviceEvent::TouchScreen(ref touch_event) => {
363                if self.enable_touch_baton_passing {
364                    if !touch_event.wake_lease.is_some() {
365                        log::debug!(
366                            "InteractionStateHandler received a touch event without a wake lease, even though one was expected."
367                        );
368                    }
369                } else {
370                    fuchsia_trace::duration!(c"input", c"interaction_state_handler[processing]");
371                    // Clamp the time to now so that clients cannot send events far off
372                    // in the future to keep the system always active.
373                    // Note: We use the global executor to get the current time instead
374                    // of the kernel so that we do not unnecessarily clamp
375                    // test-injected times.
376                    let event_time = unhandled_input_event.event_time.clamp(
377                        zx::MonotonicInstant::ZERO,
378                        fuchsia_async::MonotonicInstant::now().into_zx(),
379                    );
380
381                    self.inspect_status.count_received_event(&event_time);
382                    self.transition_to_idle_after_new_time(event_time).await;
383                }
384            }
385            _ => {}
386        }
387
388        vec![input_device::InputEvent::from(unhandled_input_event)]
389    }
390
391    fn set_handler_healthy(self: std::rc::Rc<Self>) {
392        self.inspect_status.health_node.borrow_mut().set_ok();
393    }
394
395    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
396        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::*;
403    use crate::mouse_binding;
404    use crate::testing_utilities::{
405        consumer_controls_device_descriptor, create_consumer_controls_event, create_mouse_event,
406        create_touch_contact, create_touch_screen_event, get_mouse_device_descriptor,
407        get_touch_screen_device_descriptor,
408    };
409    use crate::utils::Position;
410    use assert_matches::assert_matches;
411    use async_utils::hanging_get::client::HangingGetStream;
412    use fidl::endpoints::create_proxy_and_stream;
413    use fidl_fuchsia_input_interaction::{NotifierMarker, NotifierProxy};
414    use fidl_fuchsia_power_system::{ActivityGovernorMarker, ActivityGovernorRequest};
415    use fidl_fuchsia_ui_input::PointerEventPhase;
416    use fuchsia_async::TestExecutor;
417    use futures::pin_mut;
418    use maplit::hashmap;
419    use std::collections::HashSet;
420    use std::task::Poll;
421    use test_case::test_case;
422
423    const ACTIVITY_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(5000);
424
425    async fn create_interaction_state_handler_and_notifier_proxy(
426        suspend_enabled: bool,
427    ) -> (Rc<InteractionStateHandler>, NotifierProxy) {
428        let mut interaction_hanging_get = init_interaction_hanging_get();
429
430        let (notifier_proxy, notifier_stream) = create_proxy_and_stream::<NotifierMarker>();
431        let stream_fut = handle_interaction_notifier_request_stream(
432            notifier_stream,
433            interaction_hanging_get.new_subscriber(),
434        );
435
436        Task::local(async move {
437            if stream_fut.await.is_err() {
438                panic!("Failed to handle notifier request stream");
439            }
440        })
441        .detach();
442
443        let lease_holder = match suspend_enabled {
444            true => {
445                let holder = LeaseHolder::new(fake_activity_governor_server())
446                    .await
447                    .expect("create lease holder for test");
448                Some(Rc::new(RefCell::new(holder)))
449            }
450            false => None,
451        };
452
453        (
454            InteractionStateHandler::new_for_test(
455                ACTIVITY_TIMEOUT,
456                lease_holder,
457                interaction_hanging_get.new_publisher(),
458            )
459            .await,
460            notifier_proxy,
461        )
462    }
463
464    fn fake_activity_governor_server() -> ActivityGovernorProxy {
465        let (proxy, mut stream) = create_proxy_and_stream::<ActivityGovernorMarker>();
466        Task::local(async move {
467            while let Some(request) = stream.next().await {
468                match request {
469                    Ok(ActivityGovernorRequest::TakeWakeLease { responder, .. }) => {
470                        let (_, fake_wake_lease) = zx::EventPair::create();
471                        responder.send(fake_wake_lease).expect("failed to send fake wake lease");
472                    }
473                    Ok(unexpected) => {
474                        log::warn!(
475                            "Unexpected request {unexpected:?} serving fuchsia.power.system.ActivityGovernor"
476                        );
477                    }
478                    Err(e) => {
479                        log::warn!(
480                            "Error serving fuchsia.power.system.ActivityGovernor: {:?}",
481                            e
482                        );
483                    }
484                }
485            }
486        })
487        .detach();
488
489        proxy
490    }
491
492    #[test_case(true; "Suspend enabled")]
493    #[test_case(false; "Suspend disabled")]
494    #[fuchsia::test(allow_stalls = false)]
495    async fn notifier_sends_initial_state(suspend_enabled: bool) {
496        let (interaction_state_handler, notifier_proxy) =
497            create_interaction_state_handler_and_notifier_proxy(suspend_enabled).await;
498        let state = notifier_proxy.watch_state().await.expect("Failed to get interaction state");
499        assert_eq!(state, State::Active);
500        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
501    }
502
503    #[test_case(true; "Suspend enabled")]
504    #[test_case(false; "Suspend disabled")]
505    #[fuchsia::test]
506    fn notifier_sends_idle_state_after_timeout(suspend_enabled: bool) -> Result<(), Error> {
507        let mut executor = TestExecutor::new_with_fake_time();
508
509        let handler_and_proxy_fut =
510            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
511        pin_mut!(handler_and_proxy_fut);
512        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
513        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
514            Poll::Ready((handler, proxy)) => (handler, proxy),
515            _ => panic!("Unable to create interaction state handler and proxy"),
516        };
517
518        // Initial state is active.
519        let mut watch_state_stream =
520            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
521        let state_fut = watch_state_stream.next();
522        pin_mut!(state_fut);
523        let initial_state = executor.run_until_stalled(&mut state_fut);
524        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
525        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
526
527        // Skip ahead by the activity timeout.
528        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
529
530        // State transitions to Idle.
531        let idle_state_fut = watch_state_stream.next();
532        pin_mut!(idle_state_fut);
533        let initial_state = executor.run_until_stalled(&mut idle_state_fut);
534        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
535        assert_eq!(interaction_state_handler.is_holding_lease(), false);
536
537        Ok(())
538    }
539
540    #[test_case(true; "Suspend enabled")]
541    #[test_case(false; "Suspend disabled")]
542    #[fuchsia::test]
543    fn interaction_state_handler_drops_first_timer_on_activity(
544        suspend_enabled: bool,
545    ) -> Result<(), Error> {
546        // This test does the following:
547        //   - Start an InteractionStateHandler, whose initial timeout is set to
548        //     ACTIVITY_TIMEOUT.
549        //   - Send an activity at time ACTIVITY_TIMEOUT / 2.
550        //   - Observe that after ACTIVITY_TIMEOUT transpires, the initial
551        //     timeout to transition to idle state _does not_ fire, as we
552        //     expect it to be replaced by a new timeout in response to the
553        //     injected activity.
554        //   - Observe that after ACTIVITY_TIMEOUT * 1.5 transpires, the second
555        //     timeout to transition to idle state _does_ fire.
556        // Because division will round to 0, odd-number timeouts could cause an
557        // incorrect implementation to still pass the test. In order to catch
558        // these cases, we first assert that ACTIVITY_TIMEOUT is an even number.
559        assert_eq!(ACTIVITY_TIMEOUT.into_nanos() % 2, 0);
560
561        let mut executor = TestExecutor::new_with_fake_time();
562
563        let handler_and_proxy_fut =
564            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
565        pin_mut!(handler_and_proxy_fut);
566        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
567        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
568            Poll::Ready((handler, proxy)) => (handler, proxy),
569            _ => panic!("Unable to create interaction state handler and proxy"),
570        };
571
572        // Initial state is active.
573        let mut watch_state_stream =
574            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
575        let state_fut = watch_state_stream.next();
576        pin_mut!(state_fut);
577        let initial_state = executor.run_until_stalled(&mut state_fut);
578        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
579        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
580
581        // Skip ahead by half the activity timeout.
582        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
583
584        // Send an input event, replacing the initial idleness timer.
585        let input_event =
586            input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
587                vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
588                zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
589                    ACTIVITY_TIMEOUT / 2,
590                )),
591                &consumer_controls_device_descriptor(),
592            ))
593            .unwrap();
594
595        let mut handle_event_fut =
596            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
597        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
598        assert!(handle_result.is_ready());
599
600        // Skip ahead by half the activity timeout.
601        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
602
603        // Initial state does not change.
604        let watch_state_fut = watch_state_stream.next();
605        pin_mut!(watch_state_fut);
606        let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
607        assert_matches!(watch_state_res, Poll::Pending);
608        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
609
610        // Skip ahead by half the activity timeout.
611        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
612
613        // Interaction state does change.
614        let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
615        assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
616        assert_eq!(interaction_state_handler.is_holding_lease(), false);
617
618        Ok(())
619    }
620
621    #[test_case(true; "Suspend enabled")]
622    #[test_case(false; "Suspend disabled")]
623    #[fuchsia::test]
624    fn interaction_state_handler_drops_late_activities(suspend_enabled: bool) -> Result<(), Error> {
625        let mut executor = TestExecutor::new_with_fake_time();
626
627        let handler_and_proxy_fut =
628            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
629        pin_mut!(handler_and_proxy_fut);
630        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
631        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
632            Poll::Ready((handler, proxy)) => (handler, proxy),
633            _ => panic!("Unable to create interaction state handler and proxy"),
634        };
635
636        // Initial state is active.
637        let mut watch_state_stream =
638            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
639        let state_fut = watch_state_stream.next();
640        pin_mut!(state_fut);
641        let watch_state_res = executor.run_until_stalled(&mut state_fut);
642        assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Active))));
643        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
644
645        // Skip ahead by half the activity timeout.
646        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
647
648        // Send an input event, replacing the initial idleness timer.
649        let input_event =
650            input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
651                vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
652                zx::MonotonicInstant::from(fuchsia_async::MonotonicInstant::after(
653                    ACTIVITY_TIMEOUT / 2,
654                )),
655                &consumer_controls_device_descriptor(),
656            ))
657            .unwrap();
658
659        let mut handle_event_fut =
660            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
661        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
662        assert!(handle_result.is_ready());
663
664        // Skip ahead by half the activity timeout.
665        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
666
667        // Send an input event with an earlier event time.
668        let input_event =
669            input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
670                vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
671                zx::MonotonicInstant::ZERO,
672                &consumer_controls_device_descriptor(),
673            ))
674            .unwrap();
675
676        let mut handle_event_fut =
677            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
678        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
679        assert!(handle_result.is_ready());
680
681        // Initial task does not transition to idle, nor does one from the
682        // "earlier" activity that was received later.
683        let watch_state_fut = watch_state_stream.next();
684        pin_mut!(watch_state_fut);
685        let initial_state = executor.run_until_stalled(&mut watch_state_fut);
686        assert_matches!(initial_state, Poll::Pending);
687        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
688
689        // Skip ahead by half the activity timeout.
690        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT / 2));
691
692        // Interaction state does change.
693        let watch_state_res = executor.run_until_stalled(&mut watch_state_fut);
694        assert_matches!(watch_state_res, Poll::Ready(Some(Ok(State::Idle))));
695        assert_eq!(interaction_state_handler.is_holding_lease(), false);
696
697        Ok(())
698    }
699
700    #[test_case(true; "Suspend enabled")]
701    #[test_case(false; "Suspend disabled")]
702    #[fuchsia::test]
703    fn notifier_sends_active_state_with_button_input_event(
704        suspend_enabled: bool,
705    ) -> Result<(), Error> {
706        let mut executor = TestExecutor::new_with_fake_time();
707
708        let handler_and_proxy_fut =
709            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
710        pin_mut!(handler_and_proxy_fut);
711        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
712        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
713            Poll::Ready((handler, proxy)) => (handler, proxy),
714            _ => panic!("Unable to create interaction state handler and proxy"),
715        };
716
717        // Initial state is active.
718        let mut watch_state_stream =
719            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
720        let state_fut = watch_state_stream.next();
721        pin_mut!(state_fut);
722        let initial_state = executor.run_until_stalled(&mut state_fut);
723        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
724        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
725
726        // Skip ahead by the activity timeout.
727        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
728
729        // State transitions to Idle.
730        let idle_state_fut = watch_state_stream.next();
731        pin_mut!(idle_state_fut);
732        let initial_state = executor.run_until_stalled(&mut idle_state_fut);
733        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
734        assert_eq!(interaction_state_handler.is_holding_lease(), false);
735
736        // Send an input event.
737        let input_event =
738            input_device::UnhandledInputEvent::try_from(create_consumer_controls_event(
739                vec![fidl_fuchsia_input_report::ConsumerControlButton::Power],
740                zx::MonotonicInstant::get(),
741                &consumer_controls_device_descriptor(),
742            ))
743            .unwrap();
744
745        let mut handle_event_fut =
746            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
747        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
748
749        // Event is not handled.
750        match handle_result {
751            Poll::Ready(res) => assert_matches!(
752                res.as_slice(),
753                [input_device::InputEvent { handled: input_device::Handled::No, .. }]
754            ),
755            x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
756        };
757
758        // State transitions to Active.
759        let active_state_fut = watch_state_stream.next();
760        pin_mut!(active_state_fut);
761        let initial_state = executor.run_until_stalled(&mut active_state_fut);
762        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
763        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
764
765        Ok(())
766    }
767
768    #[test_case(true; "Suspend enabled")]
769    #[test_case(false; "Suspend disabled")]
770    #[fuchsia::test]
771    fn notifier_sends_active_state_with_mouse_input_event(
772        suspend_enabled: bool,
773    ) -> Result<(), Error> {
774        let mut executor = TestExecutor::new_with_fake_time();
775
776        let handler_and_proxy_fut =
777            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
778        pin_mut!(handler_and_proxy_fut);
779        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
780        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
781            Poll::Ready((handler, proxy)) => (handler, proxy),
782            _ => panic!("Unable to create interaction state handler and proxy"),
783        };
784
785        // Initial state is active.
786        let mut watch_state_stream =
787            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
788        let state_fut = watch_state_stream.next();
789        pin_mut!(state_fut);
790        let initial_state = executor.run_until_stalled(&mut state_fut);
791        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
792        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
793
794        // Skip ahead by the activity timeout.
795        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
796
797        // State transitions to Idle.
798        let idle_state_fut = watch_state_stream.next();
799        pin_mut!(idle_state_fut);
800        let initial_state = executor.run_until_stalled(&mut idle_state_fut);
801        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
802        assert_eq!(interaction_state_handler.is_holding_lease(), false);
803
804        // Send an input event.
805        let input_event = input_device::UnhandledInputEvent::try_from(create_mouse_event(
806            mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
807            None, /* wheel_delta_v */
808            None, /* wheel_delta_h */
809            None, /* is_precision_scroll */
810            mouse_binding::MousePhase::Down,
811            HashSet::new(),
812            HashSet::new(),
813            zx::MonotonicInstant::get(),
814            &get_mouse_device_descriptor(),
815        ))
816        .unwrap();
817
818        let mut handle_event_fut =
819            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
820        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
821
822        // Event is not handled.
823        match handle_result {
824            Poll::Ready(res) => assert_matches!(
825                res.as_slice(),
826                [input_device::InputEvent { handled: input_device::Handled::No, .. }]
827            ),
828            x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
829        };
830
831        // State transitions to Active.
832        let active_state_fut = watch_state_stream.next();
833        pin_mut!(active_state_fut);
834        let initial_state = executor.run_until_stalled(&mut active_state_fut);
835        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
836        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
837
838        Ok(())
839    }
840
841    #[test_case(true; "Suspend enabled")]
842    #[test_case(false; "Suspend disabled")]
843    #[fuchsia::test]
844    fn notifier_sends_active_state_with_touch_input_event(
845        suspend_enabled: bool,
846    ) -> Result<(), Error> {
847        let mut executor = TestExecutor::new_with_fake_time();
848
849        let handler_and_proxy_fut =
850            create_interaction_state_handler_and_notifier_proxy(suspend_enabled);
851        pin_mut!(handler_and_proxy_fut);
852        let handler_and_proxy_res = executor.run_until_stalled(&mut handler_and_proxy_fut);
853        let (interaction_state_handler, notifier_proxy) = match handler_and_proxy_res {
854            Poll::Ready((handler, proxy)) => (handler, proxy),
855            _ => panic!("Unable to create interaction state handler and proxy"),
856        };
857
858        // Initial state is active.
859        let mut watch_state_stream =
860            HangingGetStream::new(notifier_proxy, NotifierProxy::watch_state);
861        let state_fut = watch_state_stream.next();
862        pin_mut!(state_fut);
863        let initial_state = executor.run_until_stalled(&mut state_fut);
864        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
865        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
866
867        // Skip ahead by the activity timeout.
868        executor.set_fake_time(fuchsia_async::MonotonicInstant::after(ACTIVITY_TIMEOUT));
869
870        // State transitions to Idle.
871        let idle_state_fut = watch_state_stream.next();
872        pin_mut!(idle_state_fut);
873        let initial_state = executor.run_until_stalled(&mut idle_state_fut);
874        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Idle))));
875        assert_eq!(interaction_state_handler.is_holding_lease(), false);
876
877        // Send an input event.
878        const TOUCH_ID: u32 = 1;
879        let contact = create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 });
880        let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
881            hashmap! {
882                PointerEventPhase::Add
883                    => vec![contact.clone()],
884            },
885            zx::MonotonicInstant::get(),
886            &get_touch_screen_device_descriptor(),
887        ))
888        .unwrap();
889
890        let mut handle_event_fut =
891            interaction_state_handler.clone().handle_unhandled_input_event(input_event);
892        let handle_result = executor.run_until_stalled(&mut handle_event_fut);
893
894        // Event is not handled.
895        match handle_result {
896            Poll::Ready(res) => assert_matches!(
897                res.as_slice(),
898                [input_device::InputEvent { handled: input_device::Handled::No, .. }]
899            ),
900            x => panic!("expected Ready from handle_unhandled_input_event, got {:?}", x),
901        };
902
903        // State transitions to Active.
904        let active_state_fut = watch_state_stream.next();
905        pin_mut!(active_state_fut);
906        let initial_state = executor.run_until_stalled(&mut active_state_fut);
907        assert_matches!(initial_state, Poll::Ready(Some(Ok(State::Active))));
908        assert_eq!(interaction_state_handler.is_holding_lease(), suspend_enabled);
909
910        Ok(())
911    }
912}