blob: 5682af0dd03822a65614d24334d96c88233ec0d6 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::input_device,
async_trait::async_trait,
fuchsia_inspect::{health::Reporter, NumericProperty, Property},
std::any::Any,
std::cell::RefCell,
std::fmt::{Debug, Formatter},
std::rc::Rc,
};
pub trait AsRcAny {
fn as_rc_any(self: Rc<Self>) -> Rc<dyn Any>;
}
impl<T: Any> AsRcAny for T {
fn as_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
/// An [`InputHandler`] dispatches InputEvents to an external service. It maintains
/// service connections necessary to handle the events.
///
/// For example, an [`ImeInputHandler`] holds a proxy to IME and keyboard services.
///
/// [`InputHandler`]s process individual input events through [`handle_input_event()`], which can
/// produce multiple events as an outcome. If the [`InputHandler`] sends an [`InputEvent`] to a
/// service that consumes the event, then the [`InputHandler`] updates the [`InputEvent.handled`]
/// accordingly.
///
/// # Notes
/// * _Callers_ should not invoke [`handle_input_event()`] concurrently since sequences of events
/// must be preserved. The state created by event n may affect the interpretation of event n+1.
/// * _Callees_ should avoid blocking unnecessarily, as that prevents `InputEvent`s from
/// propagating to downstream handlers in a timely manner. See
/// [further discussion of blocking](https://cs.opensource.google/fuchsia/fuchsia/+/main:src/ui/lib/input_pipeline/docs/coding.md).
#[async_trait(?Send)]
pub trait InputHandler: AsRcAny {
/// Returns a vector of InputEvents to propagate to the next InputHandler.
///
/// * The vector may be empty if, e.g., the handler chose to buffer the
/// event.
/// * The vector may have multiple events if, e.g.,
/// * the handler chose to release previously buffered events, or
/// * the handler unpacked a single event into multiple events
///
/// # Parameters
/// `input_event`: The InputEvent to be handled.
async fn handle_input_event(
self: std::rc::Rc<Self>,
input_event: input_device::InputEvent,
) -> Vec<input_device::InputEvent>;
fn set_handler_healthy(self: std::rc::Rc<Self>);
fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str);
/// Returns the name of the input handler.
///
/// The default implementation returns the name of the struct implementing
/// the trait.
fn get_name(&self) -> &'static str {
let full_name = std::any::type_name::<Self>();
match full_name.rmatch_indices("::").nth(0) {
Some((i, _matched_substr)) => &full_name[i + 2..],
None => full_name,
}
}
}
/// An [`UnhandledInputHandler`] is like an [`InputHandler`], but only deals in unhandled events.
#[async_trait(?Send)]
pub trait UnhandledInputHandler: AsRcAny {
/// Returns a vector of InputEvents to propagate to the next InputHandler.
///
/// * The vector may be empty if, e.g., the handler chose to buffer the
/// event.
/// * The vector may have multiple events if, e.g.,
/// * the handler chose to release previously buffered events, or
/// * the handler unpacked a single event into multiple events
///
/// # Parameters
/// `input_event`: The InputEvent to be handled.
async fn handle_unhandled_input_event(
self: std::rc::Rc<Self>,
unhandled_input_event: input_device::UnhandledInputEvent,
) -> Vec<input_device::InputEvent>;
fn set_handler_healthy(self: std::rc::Rc<Self>);
fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str);
}
#[async_trait(?Send)]
impl<T> InputHandler for T
where
T: UnhandledInputHandler,
{
async fn handle_input_event(
self: std::rc::Rc<Self>,
input_event: input_device::InputEvent,
) -> Vec<input_device::InputEvent> {
match input_event.handled {
input_device::Handled::Yes => return vec![input_event],
input_device::Handled::No => {
T::handle_unhandled_input_event(
self,
input_device::UnhandledInputEvent {
device_event: input_event.device_event,
device_descriptor: input_event.device_descriptor,
event_time: input_event.event_time,
trace_id: input_event.trace_id,
},
)
.await
}
}
}
fn set_handler_healthy(self: std::rc::Rc<Self>) {
T::set_handler_healthy(self);
}
fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
T::set_handler_unhealthy(self, msg);
}
}
pub struct InputHandlerStatus {
/// A node that contains the state below.
pub inspect_node: fuchsia_inspect::Node,
/// The number of unhandled events received by the handler.
events_received_count: fuchsia_inspect::UintProperty,
/// The number of reports handled by the handler.
events_handled_count: fuchsia_inspect::UintProperty,
/// The event time the last received InputEvent was received.
last_received_timestamp_ns: fuchsia_inspect::UintProperty,
// This node records the health status of the `InputHandler`.
pub health_node: RefCell<fuchsia_inspect::health::Node>,
}
impl PartialEq for InputHandlerStatus {
fn eq(&self, other: &Self) -> bool {
self.inspect_node == other.inspect_node
&& self.events_received_count == other.events_received_count
&& self.events_handled_count == other.events_handled_count
&& self.last_received_timestamp_ns == other.last_received_timestamp_ns
}
}
impl Debug for InputHandlerStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inspect_node.fmt(f)
}
}
impl Default for InputHandlerStatus {
fn default() -> Self {
let inspector = fuchsia_inspect::Inspector::default();
Self::new(&inspector.root(), "default", false)
}
}
impl InputHandlerStatus {
pub fn new(node: &fuchsia_inspect::Node, name: &str, _generates_events: bool) -> Self {
let handler_node = node.create_child(name);
let events_received_count = handler_node.create_uint("events_received_count", 0);
let events_handled_count = handler_node.create_uint("events_handled_count", 0);
let last_received_timestamp_ns = handler_node.create_uint("last_received_timestamp_ns", 0);
let mut health_node = fuchsia_inspect::health::Node::new(&handler_node);
health_node.set_starting_up();
Self {
inspect_node: handler_node,
events_received_count: events_received_count,
events_handled_count: events_handled_count,
last_received_timestamp_ns: last_received_timestamp_ns,
health_node: RefCell::new(health_node),
}
}
pub fn count_received_event(&self, event: input_device::InputEvent) {
self.events_received_count.add(1);
self.last_received_timestamp_ns.set(event.event_time.into_nanos().try_into().unwrap());
}
pub fn count_handled_event(&self) {
self.events_handled_count.add(1);
}
}
#[cfg(test)]
mod tests {
use {
super::{async_trait, InputHandler, UnhandledInputHandler},
crate::{
input_device::{
Handled, InputDeviceDescriptor, InputDeviceEvent, InputEvent, UnhandledInputEvent,
},
input_handler::InputHandlerStatus,
},
fuchsia_zircon as zx,
futures::{channel::mpsc, StreamExt as _},
pretty_assertions::assert_eq,
test_case::test_case,
};
struct FakeUnhandledInputHandler {
event_sender: mpsc::UnboundedSender<UnhandledInputEvent>,
mark_events_handled: bool,
}
#[async_trait(?Send)]
impl UnhandledInputHandler for FakeUnhandledInputHandler {
async fn handle_unhandled_input_event(
self: std::rc::Rc<Self>,
unhandled_input_event: UnhandledInputEvent,
) -> Vec<InputEvent> {
self.event_sender
.unbounded_send(unhandled_input_event.clone())
.expect("failed to send");
vec![InputEvent::from(unhandled_input_event).into_handled_if(self.mark_events_handled)]
}
fn set_handler_healthy(self: std::rc::Rc<Self>) {
// No inspect data on FakeUnhandledInputHandler. Do nothing.
}
fn set_handler_unhealthy(self: std::rc::Rc<Self>, _msg: &str) {
// No inspect data on FakeUnhandledInputHandler. Do nothing.
}
}
#[fuchsia::test(allow_stalls = false)]
async fn blanket_impl_passes_unhandled_events_to_wrapped_handler() {
let expected_trace_id: Option<fuchsia_trace::Id> = Some(1234.into());
let (event_sender, mut event_receiver) = mpsc::unbounded();
let handler = std::rc::Rc::new(FakeUnhandledInputHandler {
event_sender,
mark_events_handled: false,
});
handler
.clone()
.handle_input_event(InputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
handled: Handled::No,
trace_id: expected_trace_id,
})
.await;
assert_eq!(
event_receiver.next().await,
Some(UnhandledInputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
trace_id: expected_trace_id,
})
)
}
#[test_case(false; "not marked by handler")]
#[test_case(true; "marked by handler")]
#[fuchsia::test(allow_stalls = false)]
async fn blanket_impl_propagates_wrapped_handlers_return_value(mark_events_handled: bool) {
let (event_sender, _event_receiver) = mpsc::unbounded();
let handler =
std::rc::Rc::new(FakeUnhandledInputHandler { event_sender, mark_events_handled });
let input_event = InputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
handled: Handled::No,
trace_id: None,
};
let expected_propagated_event = input_event.clone().into_handled_if(mark_events_handled);
pretty_assertions::assert_eq!(
handler.clone().handle_input_event(input_event).await.as_slice(),
[expected_propagated_event]
);
}
#[fuchsia::test(allow_stalls = false)]
async fn blanket_impl_filters_handled_events_from_wrapped_handler() {
let (event_sender, mut event_receiver) = mpsc::unbounded();
let handler = std::rc::Rc::new(FakeUnhandledInputHandler {
event_sender,
mark_events_handled: false,
});
handler
.clone()
.handle_input_event(InputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
handled: Handled::Yes,
trace_id: None,
})
.await;
// Drop `handler` to dispose of `event_sender`. This ensures
// that `event_receiver.next()` does not block.
std::mem::drop(handler);
assert_eq!(event_receiver.next().await, None)
}
#[fuchsia::test(allow_stalls = false)]
async fn blanket_impl_propagates_handled_events_to_next_handler() {
let (event_sender, _event_receiver) = mpsc::unbounded();
let handler = std::rc::Rc::new(FakeUnhandledInputHandler {
event_sender,
mark_events_handled: false,
});
assert_eq!(
handler
.clone()
.handle_input_event(InputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
handled: Handled::Yes,
trace_id: None,
})
.await
.as_slice(),
[InputEvent {
device_event: InputDeviceEvent::Fake,
device_descriptor: InputDeviceDescriptor::Fake,
event_time: zx::Time::from_nanos(1),
handled: Handled::Yes,
trace_id: None,
}]
);
}
#[fuchsia::test]
fn get_name() {
struct NeuralInputHandler {}
#[async_trait(?Send)]
impl InputHandler for NeuralInputHandler {
async fn handle_input_event(
self: std::rc::Rc<Self>,
_input_event: InputEvent,
) -> Vec<InputEvent> {
unimplemented!()
}
fn set_handler_healthy(self: std::rc::Rc<Self>) {
unimplemented!()
}
fn set_handler_unhealthy(self: std::rc::Rc<Self>, _msg: &str) {
unimplemented!()
}
}
let handler = std::rc::Rc::new(NeuralInputHandler {});
assert_eq!(handler.get_name(), "NeuralInputHandler");
}
#[fuchsia::test]
async fn input_handler_status_initialized_with_correct_properties() {
let inspector = fuchsia_inspect::Inspector::default();
let input_pipeline_node = inspector.root().create_child("input_pipeline");
let input_handlers_node = input_pipeline_node.create_child("input_handlers");
let _input_handler_status =
InputHandlerStatus::new(&input_handlers_node, "test_handler", false);
diagnostics_assertions::assert_data_tree!(inspector, root: {
input_pipeline: {
input_handlers: {
test_handler: {
events_received_count: 0u64,
events_handled_count: 0u64,
last_received_timestamp_ns: 0u64,
"fuchsia.inspect.Health": {
status: "STARTING_UP",
// Timestamp value is unpredictable and not relevant in this context,
// so we only assert that the property is present.
start_timestamp_nanos: diagnostics_assertions::AnyProperty
},
}
}
}
});
}
}