blob: a8959d90413a64b5f3cd7f3bccee4ccd70c8f60e [file]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Bounce keys input filter implementation.
//! Bounce keys is an accessibility feature to aid users who have physical disabilities, that
//! allows the user to configure the device to ignore rapid, repeated key presses of the same key.
use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
use input::KeyboardType;
use log::debug;
use std::any::Any;
use std::collections::{HashMap, HashSet};
#[derive(Debug)]
struct LastUpKeyEvent {
keycode: i32,
event_time: i64,
}
#[derive(Debug)]
struct BlockedEvent {
device_id: i32,
keycode: i32,
}
pub struct BounceKeysFilter {
next: Box<dyn Filter + Send + Sync>,
key_event_map: HashMap<i32, LastUpKeyEvent>,
blocked_events: Vec<BlockedEvent>,
supported_devices: HashSet<i32>,
bounce_key_threshold_ns: i64,
}
impl BounceKeysFilter {
/// Create a new BounceKeysFilter instance.
pub fn new(
next: Box<dyn Filter + Send + Sync>,
bounce_key_threshold_ns: i64,
) -> BounceKeysFilter {
Self {
next,
key_event_map: HashMap::new(),
blocked_events: Vec::new(),
supported_devices: HashSet::new(),
bounce_key_threshold_ns,
}
}
}
impl Filter for BounceKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
// Check if it is a supported device and event source contains Source::KEYBOARD
if !(self.supported_devices.contains(&event.deviceId)
&& event.source.0 & Source::KEYBOARD.0 != 0)
{
self.next.notify_key(event);
return;
}
match event.action {
KeyEventAction::DOWN => match self.key_event_map.get(&event.deviceId) {
None => self.next.notify_key(event),
Some(last_up_event) => {
if event.keyCode == last_up_event.keycode
&& event.eventTime < last_up_event.event_time + self.bounce_key_threshold_ns
{
self.blocked_events.push(BlockedEvent {
device_id: event.deviceId,
keycode: event.keyCode,
});
debug!("Event dropped because last up was too recent");
} else {
self.key_event_map.remove(&event.deviceId);
self.next.notify_key(event);
}
}
},
KeyEventAction::UP => {
self.key_event_map.insert(
event.deviceId,
LastUpKeyEvent { keycode: event.keyCode, event_time: event.eventTime },
);
if let Some(index) = self.blocked_events.iter().position(|blocked_event| {
blocked_event.device_id == event.deviceId
&& blocked_event.keycode == event.keyCode
}) {
self.blocked_events.remove(index);
debug!("Event dropped because key down was already dropped");
} else {
self.next.notify_key(event);
}
}
_ => (),
}
}
fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
self.key_event_map.retain(|id, _| device_infos.iter().any(|x| *id == x.deviceId));
self.blocked_events.retain(|blocked_event| {
device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
});
self.supported_devices.clear();
for device_info in device_infos {
if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID {
continue;
}
if device_info.keyboardType == KeyboardType::None as i32 {
continue;
}
// Support Alphabetic keyboards and Non-alphabetic external keyboards
if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 {
self.supported_devices.insert(device_info.deviceId);
}
}
self.next.notify_devices_changed(device_infos);
}
fn destroy(&mut self) {
self.next.destroy();
}
fn save(
&mut self,
state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
self.next.save(state)
}
fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
self.next.restore(state);
}
fn dump(&mut self, dump_str: String) -> String {
let mut result = "Bounce Keys filter: \n".to_string();
result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns);
result += &format!("\tkey_event_map = {:?}\n", self.key_event_map);
result += &format!("\tblocked_events = {:?}\n", self.blocked_events);
result += &format!("\tsupported_devices = {:?}\n", self.supported_devices);
self.next.dump(dump_str + &result)
}
}
#[cfg(test)]
mod tests {
use crate::bounce_keys_filter::BounceKeysFilter;
use crate::input_filter::{test_filter::TestFilter, Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
use input::KeyboardType;
static BASE_KEY_EVENT: KeyEvent = KeyEvent {
id: 1,
deviceId: 1,
downTime: 0,
readTime: 0,
eventTime: 0,
source: Source::KEYBOARD,
displayId: 0,
policyFlags: 0,
action: KeyEventAction::DOWN,
flags: 0,
keyCode: 1,
scanCode: 0,
metaState: 0,
};
#[test]
fn test_is_notify_key_for_external_keyboard() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
next.clear();
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_for_tv_remote() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
KeyboardType::NonAlphabetic,
);
let source = Source(Source::KEYBOARD.0 | Source::DPAD.0);
let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
next.clear();
let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event =
KeyEvent { eventTime: 100, action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event =
KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_blocks_for_internal_keyboard() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
next.clear();
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_doesnt_block_for_internal_non_alphabetic_keyboard() {
let next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
KeyboardType::NonAlphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_doesnt_block_for_virtual_keyboard() {
let next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
Box::new(next.clone()),
VIRTUAL_KEYBOARD_DEVICE_ID, /* device_id */
100, /* threshold */
KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_doesnt_block_for_external_stylus() {
let next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
KeyboardType::NonAlphabetic,
);
let event =
KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event =
KeyEvent { action: KeyEventAction::UP, source: Source::STYLUS, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event =
KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_for_multiple_external_keyboards() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_devices(
Box::new(next.clone()),
&[
DeviceInfo {
deviceId: 1,
external: true,
keyboardType: KeyboardType::Alphabetic as i32,
},
DeviceInfo {
deviceId: 2,
external: true,
keyboardType: KeyboardType::Alphabetic as i32,
},
],
100, /* threshold */
);
// Bounce key scenario on the external keyboard
let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
next.clear();
let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
#[test]
fn test_is_notify_key_for_external_and_internal_alphabetic_keyboards() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_devices(
Box::new(next.clone()),
&[
DeviceInfo {
deviceId: 1,
external: false,
keyboardType: KeyboardType::Alphabetic as i32,
},
DeviceInfo {
deviceId: 2,
external: true,
keyboardType: KeyboardType::Alphabetic as i32,
},
],
100, /* threshold */
);
// Bounce key scenario on the internal keyboard
let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
next.clear();
let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert!(next.last_event().is_none());
let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
assert_eq!(next.last_event().unwrap(), event);
}
fn setup_filter_with_external_device(
next: Box<dyn Filter + Send + Sync>,
device_id: i32,
threshold: i64,
keyboard_type: KeyboardType,
) -> BounceKeysFilter {
setup_filter_with_devices(
next,
&[DeviceInfo {
deviceId: device_id,
external: true,
keyboardType: keyboard_type as i32,
}],
threshold,
)
}
fn setup_filter_with_internal_device(
next: Box<dyn Filter + Send + Sync>,
device_id: i32,
threshold: i64,
keyboard_type: KeyboardType,
) -> BounceKeysFilter {
setup_filter_with_devices(
next,
&[DeviceInfo {
deviceId: device_id,
external: false,
keyboardType: keyboard_type as i32,
}],
threshold,
)
}
fn setup_filter_with_devices(
next: Box<dyn Filter + Send + Sync>,
devices: &[DeviceInfo],
threshold: i64,
) -> BounceKeysFilter {
let mut filter = BounceKeysFilter::new(next, threshold);
filter.notify_devices_changed(devices);
filter
}
}