blob: d69dc47760c89995876ffedbb80323908104c9dd [file] [log] [blame]
// Copyright 2021 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_handler::UnhandledInputHandler,
crate::{consumer_controls_binding, input_device},
anyhow::{Context, Error},
async_trait::async_trait,
fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
fidl_fuchsia_ui_policy as fidl_ui_policy,
fuchsia_syslog::fx_log_err,
futures::lock::Mutex,
futures::TryStreamExt,
std::rc::Rc,
};
/// A [`MediaButtonsHandler`] tracks MediaButtonListeners and sends media button events to them.
#[derive(Debug)]
pub struct MediaButtonsHandler {
/// The mutable fields of this handler.
inner: Mutex<MediaButtonsHandlerInner>,
}
#[derive(Debug)]
struct MediaButtonsHandlerInner {
/// The media button listeners.
pub listeners: Vec<fidl_ui_policy::MediaButtonsListenerProxy>,
/// The last MediaButtonsEvent sent to all listeners.
/// This is used to send new listeners the state of the media buttons.
pub last_event: Option<fidl_ui_input::MediaButtonsEvent>,
}
#[async_trait(?Send)]
impl UnhandledInputHandler for MediaButtonsHandler {
async fn handle_unhandled_input_event(
self: Rc<Self>,
unhandled_input_event: input_device::UnhandledInputEvent,
) -> Vec<input_device::InputEvent> {
match unhandled_input_event {
input_device::UnhandledInputEvent {
device_event:
input_device::InputDeviceEvent::ConsumerControls(ref media_buttons_event),
device_descriptor: input_device::InputDeviceDescriptor::ConsumerControls(_),
event_time: _,
trace_id: _,
} => {
let media_buttons_event = Self::create_media_buttons_event(media_buttons_event);
// Send the event if the media buttons are supported.
self.send_event_to_listeners(&media_buttons_event).await;
// Store the sent event.
let mut inner = self.inner.lock().await;
inner.last_event = Some(media_buttons_event);
// Consume the input event.
vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
}
_ => vec![input_device::InputEvent::from(unhandled_input_event)],
}
}
}
impl MediaButtonsHandler {
/// Creates a new [`MediaButtonsHandler`] that sends media button events to listeners.
pub fn new() -> Rc<Self> {
let media_buttons_handler = Self {
inner: Mutex::new(MediaButtonsHandlerInner { listeners: Vec::new(), last_event: None }),
};
Rc::new(media_buttons_handler)
}
/// Handles the incoming DeviceListenerRegistryRequestStream.
///
/// This method will end when the request stream is closed. If the stream closes with an
/// error the error will be returned in the Result.
///
/// # Parameters
/// - `stream`: The stream of DeviceListenerRegistryRequestStream.
pub async fn handle_device_listener_registry_request_stream(
self: &Rc<Self>,
mut stream: fidl_ui_policy::DeviceListenerRegistryRequestStream,
) -> Result<(), Error> {
while let Some(request) = stream
.try_next()
.await
.context("Error handling device listener registry request stream")?
{
match request {
fidl_ui_policy::DeviceListenerRegistryRequest::RegisterListener {
listener,
responder,
} => {
if let Ok(proxy) = listener.into_proxy() {
// Add the listener to the registry.
let mut inner = self.inner.lock().await;
inner.listeners.push(proxy.clone());
// Send the listener the last media button event.
if let Some(event) = &inner.last_event {
proxy
.on_event(event.clone())
.await
.context("Failed to send media buttons event to listener")?;
}
}
let _ = responder.send();
}
_ => {}
}
}
Ok(())
}
/// Creates a fidl_ui_input::MediaButtonsEvent from a media_buttons::MediaButtonEvent.
///
/// # Parameters
/// - `event`: The MediaButtonEvent to create a MediaButtonsEvent from.
fn create_media_buttons_event(
event: &consumer_controls_binding::ConsumerControlsEvent,
) -> fidl_ui_input::MediaButtonsEvent {
let mut new_event = fidl_ui_input::MediaButtonsEvent {
volume: Some(0),
mic_mute: Some(false),
pause: Some(false),
camera_disable: Some(false),
..fidl_ui_input::MediaButtonsEvent::EMPTY
};
for button in &event.pressed_buttons {
match button {
fidl_input_report::ConsumerControlButton::VolumeUp => {
new_event.volume = Some(new_event.volume.unwrap().saturating_add(1));
}
fidl_input_report::ConsumerControlButton::VolumeDown => {
new_event.volume = Some(new_event.volume.unwrap().saturating_sub(1));
}
fidl_input_report::ConsumerControlButton::MicMute => {
new_event.mic_mute = Some(true);
}
fidl_input_report::ConsumerControlButton::Pause => {
new_event.pause = Some(true);
}
fidl_input_report::ConsumerControlButton::CameraDisable => {
new_event.camera_disable = Some(true);
}
_ => {}
}
}
new_event
}
/// Sends media button events to media button listeners.
///
/// # Parameters
/// - `event`: The event to send to the listeners.
async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::MediaButtonsEvent) {
let inner = self.inner.lock().await;
for listener in inner.listeners.iter() {
if let Err(e) = listener.on_event(event.clone()).await {
fx_log_err!("Error sending MediaButtonsEvent to listener: {:?}", e);
}
}
}
}
#[cfg(test)]
mod tests {
use {
super::*, crate::testing_utilities, assert_matches::assert_matches,
fidl::endpoints::create_proxy_and_stream, fidl_fuchsia_input_report as fidl_input_report,
fuchsia_async as fasync, fuchsia_zircon as zx, futures::StreamExt,
pretty_assertions::assert_eq,
};
fn spawn_device_listener_registry_server(
handler: Rc<MediaButtonsHandler>,
) -> fidl_ui_policy::DeviceListenerRegistryProxy {
let (device_listener_proxy, device_listener_stream) =
create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>()
.expect("Failed to create DeviceListenerRegistry proxy and stream.");
fasync::Task::local(async move {
let _ = handler
.handle_device_listener_registry_request_stream(device_listener_stream)
.await;
})
.detach();
device_listener_proxy
}
fn create_ui_input_media_buttons_event(
volume: Option<i8>,
mic_mute: Option<bool>,
pause: Option<bool>,
camera_disable: Option<bool>,
) -> fidl_ui_input::MediaButtonsEvent {
fidl_ui_input::MediaButtonsEvent {
volume,
mic_mute,
pause,
camera_disable,
..fidl_ui_input::MediaButtonsEvent::EMPTY
}
}
/// Tests that a media button listener can be registered and is sent the latest event upon
/// registration.
#[fasync::run_singlethreaded(test)]
async fn register_media_buttons_listener() {
// Set up DeviceListenerRegistry.
let media_buttons_handler = Rc::new(MediaButtonsHandler {
inner: Mutex::new(MediaButtonsHandlerInner {
listeners: vec![],
last_event: Some(create_ui_input_media_buttons_event(Some(1), None, None, None)),
}),
});
let device_listener_proxy =
spawn_device_listener_registry_server(media_buttons_handler.clone());
// Register a listener.
let (listener, mut listener_stream) =
fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>()
.unwrap();
let register_listener_fut = async {
let res = device_listener_proxy.register_listener(listener).await;
assert!(res.is_ok());
};
// Assert listener was registered and received last event.
let expected_event = create_ui_input_media_buttons_event(Some(1), None, None, None);
let assert_fut = async {
match listener_stream.next().await {
Some(Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent {
event,
responder,
})) => {
assert_eq!(event, expected_event);
responder.send().expect("responder failed.");
}
_ => assert!(false),
}
};
futures::join!(register_listener_fut, assert_fut);
assert_eq!(media_buttons_handler.inner.lock().await.listeners.len(), 1);
}
/// Tests that all supported buttons are sent.
#[fasync::run_singlethreaded(test)]
async fn listener_receives_all_buttons() {
let media_buttons_handler = MediaButtonsHandler::new();
let device_listener_proxy =
spawn_device_listener_registry_server(media_buttons_handler.clone());
// Register a listener.
let (listener, listener_stream) =
fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>()
.unwrap();
let _ = device_listener_proxy.register_listener(listener).await;
// Setup events and expectations.
let descriptor = testing_utilities::consumer_controls_device_descriptor();
let event_time = zx::Time::get_monotonic();
let input_events = vec![testing_utilities::create_consumer_controls_event(
vec![
fidl_input_report::ConsumerControlButton::VolumeUp,
fidl_input_report::ConsumerControlButton::VolumeDown,
fidl_input_report::ConsumerControlButton::Pause,
fidl_input_report::ConsumerControlButton::MicMute,
fidl_input_report::ConsumerControlButton::CameraDisable,
],
event_time,
&descriptor,
)];
let expected_events =
vec![create_ui_input_media_buttons_event(Some(0), Some(true), Some(true), Some(true))];
// Assert registered listener receives event.
use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
assert_input_event_sequence_generates_media_buttons_events!(
input_handler: media_buttons_handler,
input_events: input_events,
expected_events: expected_events,
media_buttons_listener_request_stream: vec![listener_stream],
);
}
/// Tests that multiple listeners are supported.
#[fasync::run_singlethreaded(test)]
async fn multiple_listeners_receive_event() {
let media_buttons_handler = MediaButtonsHandler::new();
let device_listener_proxy =
spawn_device_listener_registry_server(media_buttons_handler.clone());
// Register two listeners.
let (first_listener, first_listener_stream) =
fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>()
.unwrap();
let (second_listener, second_listener_stream) =
fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>()
.unwrap();
let _ = device_listener_proxy.register_listener(first_listener).await;
let _ = device_listener_proxy.register_listener(second_listener).await;
// Setup events and expectations.
let descriptor = testing_utilities::consumer_controls_device_descriptor();
let event_time = zx::Time::get_monotonic();
let input_events = vec![testing_utilities::create_consumer_controls_event(
vec![fidl_input_report::ConsumerControlButton::VolumeUp],
event_time,
&descriptor,
)];
let expected_events = vec![create_ui_input_media_buttons_event(
Some(1),
Some(false),
Some(false),
Some(false),
)];
// Assert registered listeners receives event.
use crate::input_handler::InputHandler as _; // Adapt UnhandledInputHandler to InputHandler
assert_input_event_sequence_generates_media_buttons_events!(
input_handler: media_buttons_handler,
input_events: input_events,
expected_events: expected_events,
media_buttons_listener_request_stream:
vec![first_listener_stream, second_listener_stream],
);
}
}