Skip to main content

input_pipeline/
display_ownership.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
5use crate::input_device::{self, InputEvent, UnhandledInputEvent};
6use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
7use crate::keyboard_binding::{KeyboardDeviceDescriptor, KeyboardEvent};
8use crate::metrics;
9use anyhow::{Context, Result};
10use async_trait::async_trait;
11use fidl_fuchsia_ui_input_internal as finput;
12use fidl_fuchsia_ui_input3::KeyEventType;
13use fuchsia_async::{OnSignals, Task};
14use fuchsia_inspect::health::Reporter;
15use futures::StreamExt;
16use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
17use futures::future::LocalBoxFuture;
18use keymaps::KeyState;
19use metrics_registry::InputPipelineErrorMetricDimensionEvent;
20use std::cell::RefCell;
21use std::rc::Rc;
22use std::sync::LazyLock;
23use zx::{
24    AsHandleRef, MonotonicDuration, MonotonicInstant, NullableHandle, Rights, Signals, Status,
25    WaitResult,
26};
27
28// Signal value corresponding to `DISPLAY_UNOWNED` (ZX_USER_SIGNAL_0).
29pub static DISPLAY_UNOWNED: LazyLock<Signals> = LazyLock::new(|| {
30    Signals::from_bits(finput::InputOwnershipSignal::DISPLAY_UNOWNED.bits())
31        .expect("static init should not fail")
32});
33
34// Signal value corresponding to `DISPLAY_PLATFORM_OWNED` (ZX_USER_SIGNAL_1).
35pub static DISPLAY_PLATFORM_OWNED: LazyLock<Signals> = LazyLock::new(|| {
36    Signals::from_bits(finput::InputOwnershipSignal::DISPLAY_PLATFORM_OWNED.bits())
37        .expect("static init should not fail")
38});
39
40// Signal value corresponding to `INPUT_CLIENT_OWNED` (ZX_USER_SIGNAL_3).
41pub static INPUT_CLIENT_OWNED: LazyLock<Signals> = LazyLock::new(|| {
42    Signals::from_bits(finput::InputOwnershipSignal::INPUT_CLIENT_OWNED.bits())
43        .expect("static init should not fail")
44});
45
46// Signal value corresponding to `INPUT_PLATFORM_OWNED` (ZX_USER_SIGNAL_4).
47pub static INPUT_PLATFORM_OWNED: LazyLock<Signals> = LazyLock::new(|| {
48    Signals::from_bits(finput::InputOwnershipSignal::INPUT_PLATFORM_OWNED.bits())
49        .expect("static init should not fail")
50});
51
52// Any ownership-related signal.
53pub static ANY_OWNERSHIP_SIGNAL: LazyLock<Signals> = LazyLock::new(|| {
54    *DISPLAY_UNOWNED | *DISPLAY_PLATFORM_OWNED | *INPUT_CLIENT_OWNED | *INPUT_PLATFORM_OWNED
55});
56
57// Stores the last received ownership signals.
58#[derive(Debug, Clone, PartialEq)]
59pub struct Ownership {
60    pub signals: Signals,
61}
62
63impl std::convert::From<Signals> for Ownership {
64    fn from(signals: Signals) -> Self {
65        Ownership { signals }
66    }
67}
68
69impl Ownership {
70    /// Returns true if the platform does not currently own input.
71    pub fn is_display_ownership_lost(&self) -> bool {
72        Self::is_signals_ownership_lost(self.signals)
73    }
74
75    /// Evaluates the 4-bit orthogonal signals on `signals` to determine whether
76    /// platform input ownership is lost.
77    ///
78    /// Platform owns input iff `DISPLAY_PLATFORM_OWNED` (Bit 1) is asserted AND
79    /// `INPUT_PLATFORM_OWNED` (Bit 4) is asserted, and neither `DISPLAY_UNOWNED` (Bit 0)
80    /// nor `INPUT_CLIENT_OWNED` (Bit 3) is asserted.
81    pub fn is_signals_ownership_lost(signals: Signals) -> bool {
82        if signals.contains(Signals::OBJECT_PEER_CLOSED) {
83            return true;
84        }
85        let has_input_signals =
86            signals.contains(*INPUT_CLIENT_OWNED) || signals.contains(*INPUT_PLATFORM_OWNED);
87        let input_owned = if has_input_signals {
88            signals.contains(*INPUT_PLATFORM_OWNED) && !signals.contains(*INPUT_CLIENT_OWNED)
89        } else {
90            // Fallback for legacy display-only signals if ViewTree bits are absent.
91            true
92        };
93        let display_owned =
94            signals.contains(*DISPLAY_PLATFORM_OWNED) && !signals.contains(*DISPLAY_UNOWNED);
95        !(display_owned && input_owned)
96    }
97
98    fn read_current_signals<T: AsHandleRef>(event: &T) -> Result<Signals, Status> {
99        match event.as_handle_ref().wait_one(Signals::empty(), MonotonicInstant::INFINITE_PAST) {
100            WaitResult::Ok(signals) | WaitResult::TimedOut(signals) => Ok(signals),
101            WaitResult::Canceled(signals) => Ok(signals),
102            WaitResult::Err(Status::PEER_CLOSED) => Ok(Signals::OBJECT_PEER_CLOSED),
103            WaitResult::Err(status) => Err(status),
104        }
105    }
106
107    /// Waits for the next ownership state transition.
108    ///
109    /// If currently owned, waits until ownership is lost (either DISPLAY_UNOWNED
110    /// or INPUT_CLIENT_OWNED is asserted, or peer closed).
111    /// If currently unowned, waits until platform ownership is regained (both
112    /// DISPLAY_PLATFORM_OWNED and INPUT_PLATFORM_OWNED are asserted, and neither
113    /// unowned signal is asserted).
114    pub async fn wait_ownership_change<T: AsHandleRef>(
115        &self,
116        event: &T,
117    ) -> Result<Signals, Status> {
118        let currently_lost = self.is_display_ownership_lost();
119        loop {
120            let wait_signals = if !currently_lost {
121                *DISPLAY_UNOWNED | *INPUT_CLIENT_OWNED | Signals::OBJECT_PEER_CLOSED
122            } else {
123                let cur = Self::read_current_signals(event)?;
124                if cur.contains(Signals::OBJECT_PEER_CLOSED) {
125                    return Err(Status::PEER_CLOSED);
126                }
127                if !Self::is_signals_ownership_lost(cur) {
128                    return Ok(cur);
129                }
130                let missing_display = !cur.contains(*DISPLAY_PLATFORM_OWNED);
131                let has_input_signals =
132                    cur.contains(*INPUT_CLIENT_OWNED) || cur.contains(*INPUT_PLATFORM_OWNED);
133                let missing_input =
134                    if has_input_signals { !cur.contains(*INPUT_PLATFORM_OWNED) } else { false };
135
136                let mut signals_to_wait = Signals::OBJECT_PEER_CLOSED;
137                if missing_display && missing_input {
138                    signals_to_wait |= *DISPLAY_PLATFORM_OWNED | *INPUT_PLATFORM_OWNED;
139                } else if missing_display {
140                    signals_to_wait |= *DISPLAY_PLATFORM_OWNED;
141                } else if missing_input {
142                    signals_to_wait |= *INPUT_PLATFORM_OWNED;
143                } else {
144                    log::error!(
145                        "display_ownership: peer asserted invalid contradictory signals (ownership lost but neither platform signal missing): {:?}",
146                        cur
147                    );
148                    return Err(Status::BAD_STATE);
149                }
150                signals_to_wait
151            };
152
153            OnSignals::new(event, wait_signals).await?;
154
155            let cur = Self::read_current_signals(event)?;
156            if currently_lost && cur.contains(Signals::OBJECT_PEER_CLOSED) {
157                return Err(Status::PEER_CLOSED);
158            }
159            let new_lost = Self::is_signals_ownership_lost(cur);
160            if new_lost != currently_lost {
161                return Ok(cur);
162            }
163            if cur.contains(Signals::OBJECT_PEER_CLOSED) {
164                return Err(Status::PEER_CLOSED);
165            }
166        }
167    }
168}
169
170type OwnershipChangeCallback = Rc<dyn Fn(bool) -> LocalBoxFuture<'static, ()>>;
171
172/// A handler that turns the input pipeline off or on based on whether
173/// Scenic and ViewTree grant input ownership to the platform.
174///
175/// This allows us to pause input processing when the user switches away
176/// from the product (e.g. terminal) into virtual console or when a client view
177/// (such as Starnix) has direct hardware input ownership.
178///
179/// See the `README.md` file in this crate for details.
180///
181/// # Safety and Concurrency
182///
183/// This struct uses `RefCell` to manage internal state. While `DisplayOwnership`
184/// logic is split between multiple tasks (`handle_ownership_change` and
185/// `handle_unhandled_input_event`), safety is maintained because:
186/// 1. The pipeline runs on a single-threaded `LocalExecutor`.
187/// 2. Borrows of `RefCell`s (like `ownership` and `key_state`) are never held
188///    across `await` points.
189///
190/// If asynchronous calls are added to critical sections in the future,
191/// ensure that all borrows are dropped before the `await`.
192pub struct DisplayOwnership {
193    /// The current view of the display ownership. It is mutated by the
194    /// display ownership task when appropriate signals arrive.
195    ownership: Rc<RefCell<Ownership>>,
196
197    /// The registry of currently pressed keys.
198    key_state: RefCell<KeyState>,
199
200    /// The source of ownership change events for the main loop.
201    display_ownership_change_receiver: RefCell<Option<UnboundedReceiver<Ownership>>>,
202
203    /// An optional callback invoked whenever ownership transitions.
204    /// Parameter is `true` if platform owns input, `false` if lost.
205    ownership_change_callback: RefCell<Option<OwnershipChangeCallback>>,
206
207    /// A background task that watches for display ownership changes. We keep
208    /// it alive to ensure that it keeps running.
209    _display_ownership_task: Task<()>,
210
211    /// The metrics logger.
212    metrics_logger: metrics::MetricsLogger,
213
214    /// The inventory of this handler's Inspect status.
215    inspect_status: InputHandlerStatus,
216
217    /// The event processing loop will do an `unbounded_send(())` on this
218    /// channel once at the end of each loop pass, in test configurations only.
219    /// The test fixture uses this channel to execute test fixture in
220    /// lock-step with the event processing loop for test cases where the
221    /// precise event sequencing is relevant.
222    #[cfg(test)]
223    loop_done: RefCell<Option<UnboundedSender<()>>>,
224    display_ownership_event: NullableHandle,
225}
226
227impl DisplayOwnership {
228    /// Creates a new handler that watches `display_ownership_event` for events.
229    ///
230    /// The `display_ownership_event` is assumed to be an [Event] obtained from
231    /// `fuchsia.ui.input.internal.InputOwnership/GetEvent` (or legacy DisplayOwnership).
232    pub fn new(
233        display_ownership_event: impl AsHandleRef + 'static,
234        input_handlers_node: &fuchsia_inspect::Node,
235        metrics_logger: metrics::MetricsLogger,
236    ) -> Rc<Self> {
237        DisplayOwnership::new_internal(
238            display_ownership_event,
239            None,
240            input_handlers_node,
241            metrics_logger,
242        )
243    }
244
245    #[cfg(test)]
246    pub fn new_for_test(
247        display_ownership_event: impl AsHandleRef + 'static,
248        loop_done: UnboundedSender<()>,
249        metrics_logger: metrics::MetricsLogger,
250    ) -> Rc<Self> {
251        let inspector = fuchsia_inspect::Inspector::default();
252        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
253        DisplayOwnership::new_internal(
254            display_ownership_event,
255            Some(loop_done),
256            &fake_handlers_node,
257            metrics_logger,
258        )
259    }
260
261    fn new_internal(
262        display_ownership_event: impl AsHandleRef + 'static,
263        _loop_done: Option<UnboundedSender<()>>,
264        input_handlers_node: &fuchsia_inspect::Node,
265        metrics_logger: metrics::MetricsLogger,
266    ) -> Rc<Self> {
267        let event_handle = display_ownership_event
268            .as_handle_ref()
269            .duplicate_handle(Rights::SAME_RIGHTS)
270            .expect("unable to duplicate display ownership event");
271        let initial_state = match display_ownership_event
272            .as_handle_ref()
273            .wait_one(*ANY_OWNERSHIP_SIGNAL, MonotonicInstant::INFINITE_PAST)
274        {
275            WaitResult::Ok(signals) | WaitResult::TimedOut(signals) => signals,
276            WaitResult::Canceled(signals) => signals,
277            WaitResult::Err(e) => panic!("unable to set the initial display state: {:?}", e),
278        };
279        log::debug!("setting initial display ownership to: {:?}", initial_state);
280        let initial_ownership: Ownership = initial_state.into();
281        let ownership = Rc::new(RefCell::new(initial_ownership.clone()));
282
283        let mut ownership_clone = initial_ownership;
284        let (ownership_sender, ownership_receiver) = mpsc::unbounded();
285        let display_ownership_task = Task::local(async move {
286            loop {
287                let signals = ownership_clone.wait_ownership_change(&display_ownership_event).await;
288                match signals {
289                    Err(e) => {
290                        log::warn!("could not read display state: {:?}", e);
291                        break;
292                    }
293                    Ok(signals) => {
294                        log::debug!("setting display ownership to: {:?}", signals);
295                        ownership_sender.unbounded_send(signals.into()).unwrap();
296                        ownership_clone = signals.into();
297                    }
298                }
299            }
300            log::warn!(
301                "display loop exiting and will no longer monitor display changes - this is not expected"
302            );
303        });
304        log::info!("Display ownership handler installed");
305        let inspect_status = InputHandlerStatus::new(
306            input_handlers_node,
307            "display_ownership",
308            /* generates_events */ false,
309        );
310        Rc::new(Self {
311            ownership,
312            key_state: RefCell::new(KeyState::new()),
313            display_ownership_change_receiver: RefCell::new(Some(ownership_receiver)),
314            ownership_change_callback: RefCell::new(None),
315            _display_ownership_task: display_ownership_task,
316            metrics_logger,
317            inspect_status,
318            #[cfg(test)]
319            loop_done: RefCell::new(_loop_done),
320            display_ownership_event: event_handle,
321        })
322    }
323
324    /// Registers a callback to be invoked whenever ownership transitions.
325    /// The callback receives `true` when platform ownership is gained,
326    /// and `false` when platform ownership is lost.
327    pub fn set_ownership_change_callback(
328        &self,
329        callback: impl Fn(bool) -> LocalBoxFuture<'static, ()> + 'static,
330    ) {
331        *self.ownership_change_callback.borrow_mut() = Some(Rc::new(callback));
332    }
333
334    /// Returns true if the platform does not currently own input.
335    pub fn is_display_ownership_lost(&self) -> bool {
336        // Query the signal state synchronously with INFINITE_PAST (non-blocking)
337        // to get the real-time state and avoid TOCTOU race conditions during
338        // display transitions. While this introduces a syscall overhead, it is
339        // negligible for low-frequency keyboard events.
340        match self
341            .display_ownership_event
342            .wait_one(Signals::empty(), MonotonicInstant::INFINITE_PAST)
343        {
344            WaitResult::Ok(signals) | WaitResult::TimedOut(signals) => {
345                Ownership::is_signals_ownership_lost(signals)
346            }
347            WaitResult::Err(Status::PEER_CLOSED) | WaitResult::Canceled(_) => true,
348            WaitResult::Err(e) => {
349                log::error!("Unexpected error on display ownership event: {:?}", e);
350                self.ownership.borrow().is_display_ownership_lost()
351            }
352        }
353    }
354
355    /// Watches for display ownership changes and sends cancel/sync events.
356    ///
357    /// NOTE: RefCell safety relies on the single-threaded nature of the executor.
358    /// No borrows of `ownership` or `key_state` must be held across the `await`
359    /// below to avoid panics if `handle_unhandled_input_event` runs while this
360    /// task is suspended.
361    pub async fn handle_ownership_change(
362        self: &Rc<Self>,
363        output: UnboundedSender<Vec<InputEvent>>,
364    ) -> Result<()> {
365        let mut ownership_source = self
366            .display_ownership_change_receiver
367            .borrow_mut()
368            .take()
369            .context("display_ownership_change_receiver already taken")?;
370        while let Some(new_ownership) = ownership_source.next().await {
371            let was_ownership_lost = self.ownership.borrow().is_display_ownership_lost();
372            let is_display_ownership_lost = new_ownership.is_display_ownership_lost();
373            // When the ownership is modified, float a set of cancel or sync
374            // events to scoop up stale keyboard state, treating it the same
375            // as loss of focus.
376            let event_type = match is_display_ownership_lost {
377                true => KeyEventType::Cancel,
378                false => KeyEventType::Sync,
379            };
380            let keys = self.key_state.borrow().get_set();
381            let mut event_time = MonotonicInstant::get();
382            for key in keys.into_iter() {
383                let key_event = KeyboardEvent::new(key, event_type);
384                output
385                    .unbounded_send(vec![into_input_event(key_event, event_time)])
386                    .context("unable to send display updates")?;
387                event_time = event_time + MonotonicDuration::from_nanos(1);
388            }
389            *(self.ownership.borrow_mut()) = new_ownership;
390            if was_ownership_lost != is_display_ownership_lost {
391                let callback = self.ownership_change_callback.borrow().clone();
392                if let Some(cb) = callback {
393                    cb(!is_display_ownership_lost).await;
394                }
395            }
396            #[cfg(test)]
397            {
398                if let Some(loop_done) = self.loop_done.borrow().as_ref() {
399                    loop_done.unbounded_send(()).unwrap();
400                }
401            }
402        }
403        Ok(())
404    }
405}
406
407impl Handler for DisplayOwnership {
408    fn set_handler_healthy(self: std::rc::Rc<Self>) {
409        self.inspect_status.health_node.borrow_mut().set_ok();
410    }
411
412    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
413        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
414    }
415
416    fn get_name(&self) -> &'static str {
417        "DisplayOwnership"
418    }
419
420    fn interest(&self) -> Vec<input_device::InputEventType> {
421        vec![input_device::InputEventType::Keyboard]
422    }
423}
424
425#[async_trait(?Send)]
426impl UnhandledInputHandler for DisplayOwnership {
427    async fn handle_unhandled_input_event(
428        self: Rc<Self>,
429        unhandled_input_event: UnhandledInputEvent,
430    ) -> Vec<input_device::InputEvent> {
431        fuchsia_trace::duration!("input", "display_ownership");
432        self.inspect_status.count_received_event(&unhandled_input_event.event_time);
433        match unhandled_input_event.device_event {
434            input_device::InputDeviceEvent::Keyboard(ref e) => {
435                self.key_state.borrow_mut().update(e.get_event_type(), e.get_key());
436            }
437            _ => {
438                self.metrics_logger.log_error(
439                    InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
440                    std::format!(
441                        "{} uninterested input event: {:?}",
442                        self.get_name(),
443                        unhandled_input_event.get_event_type()
444                    ),
445                );
446            }
447        }
448        let is_display_ownership_lost = self.is_display_ownership_lost();
449        if is_display_ownership_lost {
450            self.inspect_status.count_handled_event();
451        }
452
453        #[cfg(test)]
454        {
455            if let Some(loop_done) = self.loop_done.borrow().as_ref() {
456                loop_done.unbounded_send(()).unwrap();
457            }
458        }
459
460        vec![
461            input_device::InputEvent::from(unhandled_input_event)
462                .into_handled_if(is_display_ownership_lost),
463        ]
464    }
465}
466
467fn empty_keyboard_device_descriptor() -> input_device::InputDeviceDescriptor {
468    input_device::InputDeviceDescriptor::Keyboard(
469        // Should descriptor be something sensible?
470        KeyboardDeviceDescriptor {
471            keys: vec![],
472            device_information: fidl_fuchsia_input_report::DeviceInformation {
473                vendor_id: Some(0),
474                product_id: Some(0),
475                version: Some(0),
476                polling_rate: Some(0),
477                ..Default::default()
478            },
479            device_id: 0,
480        },
481    )
482}
483
484fn into_input_event(
485    keyboard_event: KeyboardEvent,
486    event_time: MonotonicInstant,
487) -> input_device::InputEvent {
488    input_device::InputEvent {
489        device_event: input_device::InputDeviceEvent::Keyboard(keyboard_event),
490        device_descriptor: empty_keyboard_device_descriptor(),
491        event_time,
492        handled: input_device::Handled::No,
493        trace_id: None,
494    }
495}
496
497#[cfg(test)]
498mod tests {
499    use super::*;
500    use crate::testing_utilities::{create_fake_input_event, create_input_event};
501    use fidl_fuchsia_input::Key;
502    use fuchsia_async as fasync;
503    use pretty_assertions::assert_eq;
504    use std::convert::TryFrom as _;
505    use zx::{EventPair, Peered};
506
507    // Manages losing and regaining display and input ownership, since manual management
508    // is error-prone: if signal_peer does not change the signal state, the waiting
509    // process will block forever, which makes tests run longer than needed.
510    struct DisplayWrangler {
511        event: EventPair,
512        last_display: Signals,
513        last_input: Signals,
514    }
515
516    impl DisplayWrangler {
517        fn new(event: EventPair) -> Self {
518            let mut instance = DisplayWrangler {
519                event,
520                last_display: *DISPLAY_PLATFORM_OWNED,
521                last_input: *INPUT_PLATFORM_OWNED,
522            };
523            // Initialize the signal state to unowned before the handler reads it,
524            // establishing a predictable baseline state for the test cases.
525            instance
526                .event
527                .signal_peer(Signals::empty(), *DISPLAY_UNOWNED | *INPUT_PLATFORM_OWNED)
528                .unwrap();
529            instance.last_display = *DISPLAY_UNOWNED;
530            instance.last_input = *INPUT_PLATFORM_OWNED;
531            instance
532        }
533
534        fn set_unowned(&mut self) {
535            assert!(self.last_display != *DISPLAY_UNOWNED, "display is already unowned");
536            self.event.signal_peer(*DISPLAY_PLATFORM_OWNED, *DISPLAY_UNOWNED).unwrap();
537            self.last_display = *DISPLAY_UNOWNED;
538        }
539
540        fn set_owned(&mut self) {
541            assert!(self.last_display != *DISPLAY_PLATFORM_OWNED, "display is already owned");
542            self.event.signal_peer(*DISPLAY_UNOWNED, *DISPLAY_PLATFORM_OWNED).unwrap();
543            self.last_display = *DISPLAY_PLATFORM_OWNED;
544        }
545
546        fn set_client_input_owned(&mut self) {
547            assert!(self.last_input != *INPUT_CLIENT_OWNED, "input is already client owned");
548            self.event.signal_peer(*INPUT_PLATFORM_OWNED, *INPUT_CLIENT_OWNED).unwrap();
549            self.last_input = *INPUT_CLIENT_OWNED;
550        }
551
552        fn set_platform_input_owned(&mut self) {
553            assert!(self.last_input != *INPUT_PLATFORM_OWNED, "input is already platform owned");
554            self.event.signal_peer(*INPUT_CLIENT_OWNED, *INPUT_PLATFORM_OWNED).unwrap();
555            self.last_input = *INPUT_PLATFORM_OWNED;
556        }
557    }
558
559    #[fuchsia::test]
560    async fn display_ownership_change() {
561        // handler_event is the event that the unit under test will examine for
562        // display ownership changes. test_event is used to set the appropriate
563        // signals.
564        let (test_event, handler_event) = EventPair::create();
565
566        // test_sender is used to pipe input events into the handler.
567        let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
568
569        // test_receiver is used to pipe input events out of the handler.
570        let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
571
572        // The unit under test adds a () each time it completes one pass through
573        // its event loop. Use to ensure synchronization.
574        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
575
576        // We use a wrapper to signal test_event correctly, since doing it wrong
577        // by hand causes tests to hang, which isn't the best dev experience.
578        let mut wrangler = DisplayWrangler::new(test_event);
579        let handler = DisplayOwnership::new_for_test(
580            handler_event,
581            loop_done_sender,
582            metrics::MetricsLogger::default(),
583        );
584
585        let handler_clone = handler.clone();
586        let handler_sender_clone = handler_sender.clone();
587        let _task = fasync::Task::local(async move {
588            handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
589        });
590
591        let handler_clone_2 = handler.clone();
592        let _input_task = fasync::Task::local(async move {
593            let mut receiver = handler_receiver;
594            while let Some(event) = receiver.next().await {
595                let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
596                let out_events =
597                    handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
598                handler_sender.unbounded_send(out_events).unwrap();
599            }
600        });
601
602        let fake_time = MonotonicInstant::from_nanos(42);
603
604        // Go two full circles of signaling.
605
606        // 1
607        wrangler.set_owned();
608        loop_done.next().await;
609        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
610        loop_done.next().await;
611
612        // 2
613        wrangler.set_unowned();
614        loop_done.next().await;
615        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
616        loop_done.next().await;
617
618        // 3
619        wrangler.set_owned();
620        loop_done.next().await;
621        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
622        loop_done.next().await;
623
624        // 4
625        wrangler.set_unowned();
626        loop_done.next().await;
627        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
628        loop_done.next().await;
629
630        let actual: Vec<InputEvent> = test_receiver
631            .take(4)
632            .flat_map(|events| futures::stream::iter(events))
633            .map(|e| e.into_with_event_time(fake_time))
634            .collect()
635            .await;
636
637        assert_eq!(
638            actual,
639            vec![
640                // Event received while we owned the display.
641                create_fake_input_event(fake_time),
642                // Event received when we lost the display.
643                create_fake_input_event(fake_time).into_handled(),
644                // Display ownership regained.
645                create_fake_input_event(fake_time),
646                // Display ownership lost.
647                create_fake_input_event(fake_time).into_handled(),
648            ]
649        );
650    }
651
652    fn new_keyboard_input_event(key: Key, event_type: KeyEventType) -> InputEvent {
653        let fake_time = MonotonicInstant::from_nanos(42);
654        create_input_event(
655            KeyboardEvent::new(key, event_type),
656            &input_device::InputDeviceDescriptor::Fake,
657            fake_time,
658            input_device::Handled::No,
659        )
660    }
661
662    #[fuchsia::test]
663    async fn basic_key_state_handling() {
664        let (test_event, handler_event) = EventPair::create();
665        let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
666        let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
667        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
668        let mut wrangler = DisplayWrangler::new(test_event);
669        let handler = DisplayOwnership::new_for_test(
670            handler_event,
671            loop_done_sender,
672            metrics::MetricsLogger::default(),
673        );
674
675        let handler_clone = handler.clone();
676        let handler_sender_clone = handler_sender.clone();
677        let _task = fasync::Task::local(async move {
678            handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
679        });
680
681        let handler_clone_2 = handler.clone();
682        let _input_task = fasync::Task::local(async move {
683            let mut receiver = handler_receiver;
684            while let Some(event) = receiver.next().await {
685                let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
686                let out_events =
687                    handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
688                handler_sender.unbounded_send(out_events).unwrap();
689            }
690        });
691
692        let fake_time = MonotonicInstant::from_nanos(42);
693
694        // Gain the display, and press a key.
695        wrangler.set_owned();
696        loop_done.next().await;
697        test_sender
698            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
699            .unwrap();
700        loop_done.next().await;
701
702        // Lose display.
703        wrangler.set_unowned();
704        loop_done.next().await;
705
706        // Regain display
707        wrangler.set_owned();
708        loop_done.next().await;
709
710        // Key event after regaining.
711        test_sender
712            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
713            .unwrap();
714        loop_done.next().await;
715
716        let actual: Vec<InputEvent> = test_receiver
717            .take(4)
718            .flat_map(|events| futures::stream::iter(events))
719            .map(|e| e.into_with_event_time(fake_time))
720            .collect()
721            .await;
722
723        assert_eq!(
724            actual,
725            vec![
726                new_keyboard_input_event(Key::A, KeyEventType::Pressed),
727                new_keyboard_input_event(Key::A, KeyEventType::Cancel)
728                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
729                new_keyboard_input_event(Key::A, KeyEventType::Sync)
730                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
731                new_keyboard_input_event(Key::A, KeyEventType::Released),
732            ]
733        );
734    }
735
736    #[fuchsia::test]
737    async fn more_key_state_handling() {
738        let (test_event, handler_event) = EventPair::create();
739        let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
740        let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
741        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
742        let mut wrangler = DisplayWrangler::new(test_event);
743        let handler = DisplayOwnership::new_for_test(
744            handler_event,
745            loop_done_sender,
746            metrics::MetricsLogger::default(),
747        );
748
749        let handler_clone = handler.clone();
750        let handler_sender_clone = handler_sender.clone();
751        let _task = fasync::Task::local(async move {
752            handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
753        });
754
755        let handler_clone_2 = handler.clone();
756        let _input_task = fasync::Task::local(async move {
757            let mut receiver = handler_receiver;
758            while let Some(event) = receiver.next().await {
759                let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
760                let out_events =
761                    handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
762                handler_sender.unbounded_send(out_events).unwrap();
763            }
764        });
765
766        let fake_time = MonotonicInstant::from_nanos(42);
767
768        wrangler.set_owned();
769        loop_done.next().await;
770        test_sender
771            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
772            .unwrap();
773        loop_done.next().await;
774        test_sender
775            .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Pressed))
776            .unwrap();
777        loop_done.next().await;
778
779        // Lose display, release a key, press a key.
780        wrangler.set_unowned();
781        loop_done.next().await;
782        test_sender
783            .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Released))
784            .unwrap();
785        loop_done.next().await;
786        test_sender
787            .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Pressed))
788            .unwrap();
789        loop_done.next().await;
790
791        // Regain display
792        wrangler.set_owned();
793        loop_done.next().await;
794
795        // Key event after regaining.
796        test_sender
797            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
798            .unwrap();
799        loop_done.next().await;
800        test_sender
801            .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Released))
802            .unwrap();
803        loop_done.next().await;
804
805        let actual: Vec<InputEvent> = test_receiver
806            .take(10) // 2 pressed, 2 cancelled, 1 released (handled), 1 pressed (handled), 2 synced, 2 released
807            .flat_map(|events| futures::stream::iter(events))
808            .map(|e| e.into_with_event_time(fake_time))
809            .collect()
810            .await;
811
812        assert_eq!(
813            actual,
814            vec![
815                new_keyboard_input_event(Key::A, KeyEventType::Pressed),
816                new_keyboard_input_event(Key::B, KeyEventType::Pressed),
817                new_keyboard_input_event(Key::A, KeyEventType::Cancel)
818                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
819                new_keyboard_input_event(Key::B, KeyEventType::Cancel)
820                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
821                new_keyboard_input_event(Key::B, KeyEventType::Released).into_handled(),
822                new_keyboard_input_event(Key::C, KeyEventType::Pressed).into_handled(),
823                // The CANCEL and SYNC events are emitted in the sort ordering of the
824                // `Key` enum values. Perhaps they should be emitted instead in the order
825                // they have been received for SYNC, and in reverse order for CANCEL.
826                new_keyboard_input_event(Key::A, KeyEventType::Sync)
827                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
828                new_keyboard_input_event(Key::C, KeyEventType::Sync)
829                    .into_with_device_descriptor(empty_keyboard_device_descriptor()),
830                new_keyboard_input_event(Key::A, KeyEventType::Released),
831                new_keyboard_input_event(Key::C, KeyEventType::Released),
832            ]
833        );
834    }
835
836    #[fuchsia::test]
837    async fn display_ownership_initialized_with_inspect_node() {
838        let (test_event, handler_event) = EventPair::create();
839        let (loop_done_sender, _) = mpsc::unbounded::<()>();
840        let inspector = fuchsia_inspect::Inspector::default();
841        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
842        // Signal needs to be initialized first so DisplayOwnership::new doesn't panic with a TIMEOUT error
843        let _ = DisplayWrangler::new(test_event);
844        let _handler = DisplayOwnership::new_internal(
845            handler_event,
846            Some(loop_done_sender),
847            &fake_handlers_node,
848            metrics::MetricsLogger::default(),
849        );
850        diagnostics_assertions::assert_data_tree!(inspector, root: {
851            input_handlers_node: {
852                display_ownership: {
853                    events_received_count: 0u64,
854                    events_handled_count: 0u64,
855                    last_received_timestamp_ns: 0u64,
856                    "fuchsia.inspect.Health": {
857                        status: "STARTING_UP",
858                        // Timestamp value is unpredictable and not relevant in this context,
859                        // so we only assert that the property is present.
860                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
861                    },
862                }
863            }
864        });
865    }
866
867    #[fuchsia::test]
868    async fn display_ownership_inspect_counts_events() {
869        let (test_event, handler_event) = EventPair::create();
870        let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
871        let (handler_sender, _test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
872        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
873        let mut wrangler = DisplayWrangler::new(test_event);
874        let inspector = fuchsia_inspect::Inspector::default();
875        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
876        let handler = DisplayOwnership::new_internal(
877            handler_event,
878            Some(loop_done_sender),
879            &fake_handlers_node,
880            metrics::MetricsLogger::default(),
881        );
882        let handler_clone = handler.clone();
883        let handler_sender_clone = handler_sender.clone();
884        let _task = fasync::Task::local(async move {
885            handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
886        });
887
888        let handler_clone_2 = handler.clone();
889        let _input_task = fasync::Task::local(async move {
890            let mut receiver = handler_receiver;
891            while let Some(event) = receiver.next().await {
892                let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
893                let out_events =
894                    handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
895                handler_sender.unbounded_send(out_events).unwrap();
896            }
897        });
898
899        // Gain the display, and press a key.
900        wrangler.set_owned();
901        loop_done.next().await;
902        test_sender
903            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
904            .unwrap();
905        loop_done.next().await;
906
907        // Lose display
908        // Input event is marked `Handled` if received after display ownership is lost
909        wrangler.set_unowned();
910        loop_done.next().await;
911        test_sender
912            .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Pressed))
913            .unwrap();
914        loop_done.next().await;
915
916        // Regain display
917        wrangler.set_owned();
918        loop_done.next().await;
919
920        // Key event after regaining.
921        test_sender
922            .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
923            .unwrap();
924        loop_done.next().await;
925
926        diagnostics_assertions::assert_data_tree!(inspector, root: {
927            input_handlers_node: {
928                display_ownership: {
929                    events_received_count: 3u64,
930                    events_handled_count: 1u64,
931                    last_received_timestamp_ns: 42u64,
932                    "fuchsia.inspect.Health": {
933                        status: "STARTING_UP",
934                        // Timestamp value is unpredictable and not relevant in this context,
935                        // so we only assert that the property is present.
936                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
937                    },
938                }
939            }
940        });
941    }
942
943    #[fuchsia::test]
944    async fn display_ownership_peer_closed() {
945        let (test_event, handler_event) = EventPair::create();
946        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
947
948        let mut wrangler = DisplayWrangler::new(test_event);
949        let handler = DisplayOwnership::new_for_test(
950            handler_event,
951            loop_done_sender,
952            metrics::MetricsLogger::default(),
953        );
954
955        let (handler_sender, _test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
956        let handler_clone = handler.clone();
957        let _task = fasync::Task::local(async move {
958            handler_clone.handle_ownership_change(handler_sender).await.unwrap();
959        });
960
961        // 1. Set to owned.
962        wrangler.set_owned();
963        loop_done.next().await;
964
965        // Verify it is owned (not lost).
966        assert!(!handler.is_display_ownership_lost());
967
968        // 2. Close the peer by dropping wrangler.
969        std::mem::drop(wrangler);
970
971        // Verify it is now lost.
972        assert!(handler.is_display_ownership_lost());
973    }
974
975    #[fuchsia::test]
976    async fn orthogonal_input_client_owned_transitions() {
977        let (test_event, handler_event) = EventPair::create();
978        let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
979        let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
980        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
981
982        let mut wrangler = DisplayWrangler::new(test_event);
983        let handler = DisplayOwnership::new_for_test(
984            handler_event,
985            loop_done_sender,
986            metrics::MetricsLogger::default(),
987        );
988
989        let handler_clone = handler.clone();
990        let handler_sender_clone = handler_sender.clone();
991        let _task = fasync::Task::local(async move {
992            handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
993        });
994
995        let handler_clone_2 = handler.clone();
996        let _input_task = fasync::Task::local(async move {
997            let mut receiver = handler_receiver;
998            while let Some(event) = receiver.next().await {
999                let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
1000                let out_events =
1001                    handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
1002                handler_sender.unbounded_send(out_events).unwrap();
1003            }
1004        });
1005
1006        let fake_time = MonotonicInstant::from_nanos(42);
1007
1008        // 1. Gain display: both DISPLAY_PLATFORM_OWNED and INPUT_PLATFORM_OWNED are asserted.
1009        wrangler.set_owned();
1010        loop_done.next().await;
1011        assert!(!handler.is_display_ownership_lost());
1012        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
1013        loop_done.next().await;
1014
1015        // 2. Client takes direct input (INPUT_CLIENT_OWNED), display remains owned.
1016        wrangler.set_client_input_owned();
1017        loop_done.next().await;
1018        assert!(handler.is_display_ownership_lost());
1019        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
1020        loop_done.next().await;
1021
1022        // 3. Client direct input ends, platform input restored (INPUT_PLATFORM_OWNED).
1023        wrangler.set_platform_input_owned();
1024        loop_done.next().await;
1025        assert!(!handler.is_display_ownership_lost());
1026        test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
1027        loop_done.next().await;
1028
1029        let actual: Vec<InputEvent> = test_receiver
1030            .take(3)
1031            .flat_map(|events| futures::stream::iter(events))
1032            .map(|e| e.into_with_event_time(fake_time))
1033            .collect()
1034            .await;
1035
1036        assert_eq!(
1037            actual,
1038            vec![
1039                create_fake_input_event(fake_time),
1040                create_fake_input_event(fake_time).into_handled(),
1041                create_fake_input_event(fake_time),
1042            ]
1043        );
1044    }
1045
1046    #[fuchsia::test]
1047    async fn orthogonal_virtcon_while_client_owned() {
1048        let (test_event, handler_event) = EventPair::create();
1049        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
1050
1051        let mut wrangler = DisplayWrangler::new(test_event);
1052        let handler = DisplayOwnership::new_for_test(
1053            handler_event,
1054            loop_done_sender,
1055            metrics::MetricsLogger::default(),
1056        );
1057
1058        let (handler_sender, _test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
1059        let handler_clone = handler.clone();
1060        let _task = fasync::Task::local(async move {
1061            handler_clone.handle_ownership_change(handler_sender).await.unwrap();
1062        });
1063
1064        // 1. Gain normal UI ownership.
1065        wrangler.set_owned();
1066        loop_done.next().await;
1067        assert!(!handler.is_display_ownership_lost());
1068
1069        // 2. Client takes input (Bit 1 | Bit 3).
1070        wrangler.set_client_input_owned();
1071        loop_done.next().await;
1072        assert!(handler.is_display_ownership_lost());
1073
1074        // 3. User switches to Virtcon while client owned (Bit 0 | Bit 3).
1075        // Pipeline was already unowned, so this is still unowned.
1076        wrangler.set_unowned();
1077        assert!(handler.is_display_ownership_lost());
1078
1079        // 4. User switches back from Virtcon to Scenic (Bit 1 | Bit 3).
1080        // Display is owned, but client still owns input, so pipeline is still unowned.
1081        wrangler.set_owned();
1082        assert!(handler.is_display_ownership_lost());
1083
1084        // 5. Client exits direct input (Bit 1 | Bit 4).
1085        // Now pipeline regains ownership!
1086        wrangler.set_platform_input_owned();
1087        loop_done.next().await;
1088        assert!(!handler.is_display_ownership_lost());
1089    }
1090
1091    #[fuchsia::test]
1092    async fn ownership_change_callback_invoked() {
1093        let (test_event, handler_event) = EventPair::create();
1094        let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
1095
1096        let mut wrangler = DisplayWrangler::new(test_event);
1097        let handler = DisplayOwnership::new_for_test(
1098            handler_event,
1099            loop_done_sender,
1100            metrics::MetricsLogger::default(),
1101        );
1102
1103        let (cb_sender, mut cb_receiver) = mpsc::unbounded::<bool>();
1104        handler.set_ownership_change_callback(move |owned| {
1105            let cb_sender = cb_sender.clone();
1106            Box::pin(async move {
1107                cb_sender.unbounded_send(owned).unwrap();
1108            })
1109        });
1110
1111        let (handler_sender, _test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
1112        let handler_clone = handler.clone();
1113        let _task = fasync::Task::local(async move {
1114            handler_clone.handle_ownership_change(handler_sender).await.unwrap();
1115        });
1116
1117        // 1. Set to owned -> callback invoked with true.
1118        wrangler.set_owned();
1119        loop_done.next().await;
1120        assert_eq!(cb_receiver.next().await, Some(true));
1121
1122        // 2. Switch to Virtcon (unowned) -> callback invoked with false.
1123        wrangler.set_unowned();
1124        loop_done.next().await;
1125        assert_eq!(cb_receiver.next().await, Some(false));
1126
1127        // 3. Switch back to Scenic -> callback invoked with true.
1128        wrangler.set_owned();
1129        loop_done.next().await;
1130        assert_eq!(cb_receiver.next().await, Some(true));
1131
1132        // 4. Client takes direct input -> callback invoked with false.
1133        wrangler.set_client_input_owned();
1134        loop_done.next().await;
1135        assert_eq!(cb_receiver.next().await, Some(false));
1136
1137        // 5. Client releases direct input -> callback invoked with true.
1138        wrangler.set_platform_input_owned();
1139        loop_done.next().await;
1140        assert_eq!(cb_receiver.next().await, Some(true));
1141    }
1142
1143    #[fuchsia::test]
1144    async fn peer_closed_terminates_without_busy_loop() {
1145        let (test_event, handler_event) = EventPair::create();
1146        // Initialize as unowned
1147        test_event.signal_peer(Signals::empty(), *DISPLAY_UNOWNED | *INPUT_PLATFORM_OWNED).unwrap();
1148
1149        let ownership = Ownership::from(*DISPLAY_UNOWNED | *INPUT_PLATFORM_OWNED);
1150        assert!(ownership.is_display_ownership_lost());
1151
1152        // Drop peer while ownership is unowned.
1153        drop(test_event);
1154
1155        // wait_ownership_change should return Err(Status::PEER_CLOSED) immediately without hanging.
1156        let result = ownership.wait_ownership_change(&handler_event).await;
1157        assert_eq!(result, Err(Status::PEER_CLOSED));
1158    }
1159
1160    #[fuchsia::test]
1161    async fn peer_closed_transitions_to_unowned_then_terminates() {
1162        let (test_event, handler_event) = EventPair::create();
1163        // Initialize as owned
1164        test_event
1165            .signal_peer(Signals::empty(), *DISPLAY_PLATFORM_OWNED | *INPUT_PLATFORM_OWNED)
1166            .unwrap();
1167
1168        let ownership = Ownership::from(*DISPLAY_PLATFORM_OWNED | *INPUT_PLATFORM_OWNED);
1169        assert!(!ownership.is_display_ownership_lost());
1170
1171        // Drop peer.
1172        drop(test_event);
1173
1174        // First wait: detects transition to unowned because of peer closed.
1175        let result = ownership.wait_ownership_change(&handler_event).await;
1176        assert!(result.is_ok());
1177        let cur_signals = result.unwrap();
1178        let unowned_ownership = Ownership::from(cur_signals);
1179        assert!(unowned_ownership.is_display_ownership_lost());
1180
1181        // Second wait: since it is already unowned and peer is closed, returns PEER_CLOSED without busy loop.
1182        let result2 = unowned_ownership.wait_ownership_change(&handler_event).await;
1183        assert_eq!(result2, Err(Status::PEER_CLOSED));
1184    }
1185
1186    #[fuchsia::test]
1187    async fn contradictory_display_signals_terminates_with_bad_state() {
1188        let (test_event, handler_event) = EventPair::create();
1189        // Peer asserts both DISPLAY_PLATFORM_OWNED and DISPLAY_UNOWNED (contradictory)
1190        // along with normal INPUT_PLATFORM_OWNED.
1191        let signals = *DISPLAY_PLATFORM_OWNED | *DISPLAY_UNOWNED | *INPUT_PLATFORM_OWNED;
1192        test_event.signal_peer(Signals::empty(), signals).unwrap();
1193
1194        let ownership = Ownership::from(signals);
1195        assert!(ownership.is_display_ownership_lost());
1196
1197        let result = ownership.wait_ownership_change(&handler_event).await;
1198        assert_eq!(result, Err(Status::BAD_STATE));
1199    }
1200
1201    #[fuchsia::test]
1202    async fn contradictory_input_signals_terminates_with_bad_state() {
1203        let (test_event, handler_event) = EventPair::create();
1204        // Peer asserts both INPUT_PLATFORM_OWNED and INPUT_CLIENT_OWNED (contradictory)
1205        // along with normal DISPLAY_PLATFORM_OWNED.
1206        let signals = *DISPLAY_PLATFORM_OWNED | *INPUT_PLATFORM_OWNED | *INPUT_CLIENT_OWNED;
1207        test_event.signal_peer(Signals::empty(), signals).unwrap();
1208
1209        let ownership = Ownership::from(signals);
1210        assert!(ownership.is_display_ownership_lost());
1211
1212        let result = ownership.wait_ownership_change(&handler_event).await;
1213        assert_eq!(result, Err(Status::BAD_STATE));
1214    }
1215}