blob: 52e3a5509bb018635ad218259323eef033d9d51e [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::InputHandler,
crate::{input_device, media_buttons},
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::sync::Arc,
};
/// A [`MediaButtonsHandler`] tracks MediaButtonListeners and sends media button events to them.
pub struct MediaButtonsHandler {
/// The media button listeners.
listeners: Arc<Mutex<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.
last_event: Arc<Mutex<Option<fidl_ui_input::MediaButtonsEvent>>>,
}
#[async_trait]
impl InputHandler for MediaButtonsHandler {
async fn handle_input_event(
&mut self,
input_event: input_device::InputEvent,
) -> Vec<input_device::InputEvent> {
match input_event {
input_device::InputEvent {
device_event: input_device::InputDeviceEvent::MediaButtons(media_buttons_event),
device_descriptor: input_device::InputDeviceDescriptor::MediaButtons(_),
event_time: _,
} => {
let media_buttons_event = Self::create_media_buttons_event(media_buttons_event);
// Send the event if the media buttons are supported.
if !is_empty_media_buttons_event(&media_buttons_event) {
self.send_event_to_listeners(&media_buttons_event).await;
*self.last_event.lock().await = Some(media_buttons_event);
}
vec![]
}
_ => vec![input_event],
}
}
}
impl MediaButtonsHandler {
/// Creates a new [`MediaButtonsHandler`] that sends media button events to listeners.
pub fn new() -> Self {
let media_buttons_handler = Self {
listeners: Arc::new(Mutex::new(Vec::new())),
last_event: Arc::new(Mutex::new(None)),
};
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
/// - `listeners`: The media button listeners to send events to.
/// - `last_event`: The last event sent to the listeners.
/// - `stream`: The stream of DeviceListenerRegistryRequestStream.
pub async fn handle_device_listener_registry_request_stream(
mut stream: fidl_ui_policy::DeviceListenerRegistryRequestStream,
listeners: Arc<Mutex<Vec<fidl_ui_policy::MediaButtonsListenerProxy>>>,
last_event: Arc<Mutex<Option<fidl_ui_input::MediaButtonsEvent>>>,
) -> 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 listeners_locked = listeners.lock().await;
listeners_locked.push(proxy.clone());
// Send the listener the last media button event.
if let Some(event) = last_event.lock().await.clone() {
if !is_empty_media_buttons_event(&event) {
let _ = proxy.on_event(event).await;
}
}
}
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: media_buttons::MediaButtonsEvent,
) -> fidl_ui_input::MediaButtonsEvent {
let mut new_event = fidl_ui_input::MediaButtonsEvent {
volume: None,
mic_mute: None,
pause: None,
camera_disable: None,
..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_or_default() + 1);
}
fidl_input_report::ConsumerControlButton::VolumeDown => {
new_event.volume = Some(new_event.volume.unwrap_or_default() - 1);
}
fidl_input_report::ConsumerControlButton::MicMute => {
new_event.mic_mute = Some(!new_event.mic_mute.unwrap_or_default());
}
fidl_input_report::ConsumerControlButton::Pause => {
new_event.pause = Some(!new_event.pause.unwrap_or_default());
}
fidl_input_report::ConsumerControlButton::CameraDisable => {
new_event.camera_disable = Some(!new_event.camera_disable.unwrap_or_default());
}
_ => {}
}
}
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, event: &fidl_ui_input::MediaButtonsEvent) {
let listeners = self.listeners.lock().await;
for listener in listeners.iter() {
if let Err(e) = listener.on_event(event.clone()).await {
fx_log_err!("Error sending MediaButtonsEvent to listener: {:?}", e);
}
}
}
}
/// Checks if the event contains any media button changes.
///
/// # Parameters
/// `event`: The media button event to check.
fn is_empty_media_buttons_event(event: &fidl_ui_input::MediaButtonsEvent) -> bool {
let empty_event = fidl_ui_input::MediaButtonsEvent {
volume: None,
mic_mute: None,
pause: None,
camera_disable: None,
..fidl_ui_input::MediaButtonsEvent::EMPTY
};
empty_event == *event
}
#[cfg(test)]
mod tests {
use {
super::*, crate::testing_utilities, fidl::endpoints::create_proxy_and_stream,
fidl_fuchsia_input_report as fidl_input_report, fuchsia_async as fasync,
fuchsia_zircon as zx, futures::StreamExt,
};
fn spawn_device_listener_registry_server(
listeners: Arc<Mutex<Vec<fidl_ui_policy::MediaButtonsListenerProxy>>>,
last_event: Arc<Mutex<Option<fidl_ui_input::MediaButtonsEvent>>>,
) -> 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::spawn(async move {
let _ = MediaButtonsHandler::handle_device_listener_registry_request_stream(
device_listener_stream,
listeners,
last_event,
)
.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 listeners: Arc<Mutex<Vec<fidl_ui_policy::MediaButtonsListenerProxy>>> =
Arc::new(Mutex::new(vec![]));
let last_event: Arc<Mutex<Option<fidl_ui_input::MediaButtonsEvent>>> = Arc::new(
Mutex::new(Some(create_ui_input_media_buttons_event(Some(1), None, None, None))),
);
let device_listener_proxy =
spawn_device_listener_registry_server(listeners.clone(), last_event.clone());
// Register a listener.
let (listener, mut listener_stream) =
fidl::endpoints::create_request_stream::<fidl_ui_policy::MediaButtonsListenerMarker>()
.unwrap();
fasync::Task::spawn(async move {
let _ = device_listener_proxy.register_listener(listener).await;
})
.detach();
let expected_event = create_ui_input_media_buttons_event(Some(1), None, None, None);
// Assert listener was registered and received last event.
if let Some(request) = listener_stream.next().await {
match request {
Ok(fidl_ui_policy::MediaButtonsListenerRequest::OnEvent { event, responder }) => {
assert_eq!(event, expected_event);
let _ = responder.send();
}
_ => assert!(false),
}
}
let unlocked_listeners = listeners.lock().await;
assert_eq!(unlocked_listeners.len(), 1);
}
/// Tests that all supported buttons are sent.
#[fasync::run_singlethreaded(test)]
async fn listener_receives_all_buttons() {
let mut media_buttons_handler = MediaButtonsHandler::new();
let device_listener_proxy = spawn_device_listener_registry_server(
media_buttons_handler.listeners.clone(),
media_buttons_handler.last_event.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::media_buttons_device_descriptor();
let event_time = zx::Time::get_monotonic().into_nanos() as input_device::EventTime;
let input_events = vec![testing_utilities::create_media_buttons_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.
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 mut media_buttons_handler = MediaButtonsHandler::new();
let device_listener_proxy = spawn_device_listener_registry_server(
media_buttons_handler.listeners.clone(),
media_buttons_handler.last_event.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::media_buttons_device_descriptor();
let event_time = zx::Time::get_monotonic().into_nanos() as input_device::EventTime;
let input_events = vec![testing_utilities::create_media_buttons_event(
vec![fidl_input_report::ConsumerControlButton::VolumeUp],
event_time,
&descriptor,
)];
let expected_events = vec![create_ui_input_media_buttons_event(Some(1), None, None, None)];
// Assert registered listeners receives event.
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],
);
}
}