use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
use crate::metrics;
use anyhow::{format_err, Error};
use async_trait::async_trait;
use fidl_fuchsia_input_report::{
self as fidl_input_report, ConsumerControlButton, InputDeviceProxy, InputReport,
};
use fuchsia_inspect::health::Reporter;
use fuchsia_inspect::ArrayProperty;
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use metrics_registry::*;
#[derive(Clone, Debug, PartialEq)]
pub struct ConsumerControlsEvent {
pub pressed_buttons: Vec<ConsumerControlButton>,
}
impl ConsumerControlsEvent {
pub fn new(pressed_buttons: Vec<ConsumerControlButton>) -> Self {
Self { pressed_buttons }
}
pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
let pressed_buttons_node =
node.create_string_array("pressed_buttons", self.pressed_buttons.len());
self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
let button_name: String = match button {
ConsumerControlButton::VolumeUp => "volume_up".into(),
ConsumerControlButton::VolumeDown => "volume_down".into(),
ConsumerControlButton::Pause => "pause".into(),
ConsumerControlButton::FactoryReset => "factory_reset".into(),
ConsumerControlButton::MicMute => "mic_mute".into(),
ConsumerControlButton::Reboot => "reboot".into(),
ConsumerControlButton::CameraDisable => "camera_disable".into(),
ConsumerControlButton::Power => "power".into(),
ConsumerControlButton::Function => "function".into(),
unknown_value => {
format!("unknown({:?})", unknown_value)
}
};
pressed_buttons_node.set(i, &button_name);
});
node.record(pressed_buttons_node);
}
}
pub struct ConsumerControlsBinding {
event_sender: UnboundedSender<input_device::InputEvent>,
device_descriptor: ConsumerControlsDeviceDescriptor,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ConsumerControlsDeviceDescriptor {
pub buttons: Vec<ConsumerControlButton>,
pub device_id: u32,
}
#[async_trait]
impl input_device::InputDeviceBinding for ConsumerControlsBinding {
fn input_event_sender(&self) -> UnboundedSender<input_device::InputEvent> {
self.event_sender.clone()
}
fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
input_device::InputDeviceDescriptor::ConsumerControls(self.device_descriptor.clone())
}
}
impl ConsumerControlsBinding {
pub async fn new(
device_proxy: InputDeviceProxy,
device_id: u32,
input_event_sender: UnboundedSender<input_device::InputEvent>,
device_node: fuchsia_inspect::Node,
metrics_logger: metrics::MetricsLogger,
) -> Result<Self, Error> {
let (device_binding, mut inspect_status) =
Self::bind_device(&device_proxy, device_id, input_event_sender, device_node).await?;
inspect_status.health_node.set_ok();
input_device::initialize_report_stream(
device_proxy,
device_binding.get_device_descriptor(),
device_binding.input_event_sender(),
inspect_status,
metrics_logger,
Self::process_reports,
);
Ok(device_binding)
}
async fn bind_device(
device: &InputDeviceProxy,
device_id: u32,
input_event_sender: UnboundedSender<input_device::InputEvent>,
device_node: fuchsia_inspect::Node,
) -> Result<(Self, InputDeviceStatus), Error> {
let mut input_device_status = InputDeviceStatus::new(device_node);
let device_descriptor: fidl_input_report::DeviceDescriptor = match device
.get_descriptor()
.await
{
Ok(descriptor) => descriptor,
Err(_) => {
input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
}
};
let consumer_controls_descriptor = device_descriptor.consumer_control.ok_or_else(|| {
input_device_status
.health_node
.set_unhealthy("DeviceDescriptor does not have a ConsumerControlDescriptor.");
format_err!("DeviceDescriptor does not have a ConsumerControlDescriptor")
})?;
let consumer_controls_input_descriptor =
consumer_controls_descriptor.input.ok_or_else(|| {
input_device_status.health_node.set_unhealthy(
"ConsumerControlDescriptor does not have a ConsumerControlInputDescriptor.",
);
format_err!(
"ConsumerControlDescriptor does not have a ConsumerControlInputDescriptor"
)
})?;
let device_descriptor: ConsumerControlsDeviceDescriptor =
ConsumerControlsDeviceDescriptor {
buttons: consumer_controls_input_descriptor.buttons.unwrap_or_default(),
device_id,
};
Ok((
ConsumerControlsBinding { event_sender: input_event_sender, device_descriptor },
input_device_status,
))
}
fn process_reports(
report: InputReport,
previous_report: Option<InputReport>,
device_descriptor: &input_device::InputDeviceDescriptor,
input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
inspect_status: &InputDeviceStatus,
metrics_logger: &metrics::MetricsLogger,
) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
inspect_status.count_received_report(&report);
let pressed_buttons: Vec<ConsumerControlButton> = match report.consumer_control {
Some(ref consumer_control_report) => consumer_control_report
.pressed_buttons
.as_ref()
.map(|buttons| buttons.iter().cloned().collect())
.unwrap_or_default(),
None => {
inspect_status.count_filtered_report();
return (previous_report, None);
}
};
send_consumer_controls_event(
pressed_buttons,
device_descriptor,
input_event_sender,
inspect_status,
metrics_logger,
);
(Some(report), None)
}
}
fn send_consumer_controls_event(
pressed_buttons: Vec<ConsumerControlButton>,
device_descriptor: &input_device::InputDeviceDescriptor,
sender: &mut UnboundedSender<input_device::InputEvent>,
inspect_status: &InputDeviceStatus,
metrics_logger: &metrics::MetricsLogger,
) {
let event = input_device::InputEvent {
device_event: input_device::InputDeviceEvent::ConsumerControls(ConsumerControlsEvent::new(
pressed_buttons,
)),
device_descriptor: device_descriptor.clone(),
event_time: zx::MonotonicInstant::get(),
handled: Handled::No,
trace_id: None,
};
match sender.unbounded_send(event.clone()) {
Err(e) => metrics_logger.log_error(
InputPipelineErrorMetricDimensionEvent::ConsumerControlsSendEventFailed,
std::format!("Failed to send ConsumerControlsEvent with error: {:?}", e),
),
_ => inspect_status.count_generated_event(event),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing_utilities;
use fuchsia_async as fasync;
use futures::StreamExt;
#[fasync::run_singlethreaded(test)]
async fn volume_up_only() {
let (event_time_i64, event_time_u64) = testing_utilities::event_times();
let pressed_buttons = vec![ConsumerControlButton::VolumeUp];
let first_report = testing_utilities::create_consumer_control_input_report(
pressed_buttons.clone(),
event_time_i64,
);
let descriptor = testing_utilities::consumer_controls_device_descriptor();
let input_reports = vec![first_report];
let expected_events = vec![testing_utilities::create_consumer_controls_event(
pressed_buttons,
event_time_u64,
&descriptor,
)];
assert_input_report_sequence_generates_events!(
input_reports: input_reports,
expected_events: expected_events,
device_descriptor: descriptor,
device_type: ConsumerControlsBinding,
);
}
#[fasync::run_singlethreaded(test)]
async fn volume_up_and_down() {
let (event_time_i64, event_time_u64) = testing_utilities::event_times();
let pressed_buttons =
vec![ConsumerControlButton::VolumeUp, ConsumerControlButton::VolumeDown];
let first_report = testing_utilities::create_consumer_control_input_report(
pressed_buttons.clone(),
event_time_i64,
);
let descriptor = testing_utilities::consumer_controls_device_descriptor();
let input_reports = vec![first_report];
let expected_events = vec![testing_utilities::create_consumer_controls_event(
pressed_buttons,
event_time_u64,
&descriptor,
)];
assert_input_report_sequence_generates_events!(
input_reports: input_reports,
expected_events: expected_events,
device_descriptor: descriptor,
device_type: ConsumerControlsBinding,
);
}
#[fasync::run_singlethreaded(test)]
async fn sequence_of_buttons() {
let (event_time_i64, event_time_u64) = testing_utilities::event_times();
let first_report = testing_utilities::create_consumer_control_input_report(
vec![ConsumerControlButton::VolumeUp],
event_time_i64,
);
let second_report = testing_utilities::create_consumer_control_input_report(
vec![ConsumerControlButton::VolumeDown],
event_time_i64,
);
let third_report = testing_utilities::create_consumer_control_input_report(
vec![ConsumerControlButton::CameraDisable],
event_time_i64,
);
let descriptor = testing_utilities::consumer_controls_device_descriptor();
let input_reports = vec![first_report, second_report, third_report];
let expected_events = vec![
testing_utilities::create_consumer_controls_event(
vec![ConsumerControlButton::VolumeUp],
event_time_u64,
&descriptor,
),
testing_utilities::create_consumer_controls_event(
vec![ConsumerControlButton::VolumeDown],
event_time_u64,
&descriptor,
),
testing_utilities::create_consumer_controls_event(
vec![ConsumerControlButton::CameraDisable],
event_time_u64,
&descriptor,
),
];
assert_input_report_sequence_generates_events!(
input_reports: input_reports,
expected_events: expected_events,
device_descriptor: descriptor,
device_type: ConsumerControlsBinding,
);
}
}