Support bounce and slow keys for internal Alphabetic keyboards
Test: atest --host libinputflinger_rs_test
Bug: 294546335
Flag: EXEMPT bugfix
Change-Id: I590e9494f1bab562ac2b3c085781f05f2c250314
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 008f675..9b6fe3c 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -24,7 +24,8 @@
pub use data_store::{DataStore, DefaultFileReaderWriter};
pub use input::{
- DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source,
+ DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags,
+ Source,
};
pub use input_verifier::InputVerifier;
pub use keyboard_classifier::KeyboardClassifier;
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index e4d73fc..2d5803b 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -60,6 +60,7 @@
AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
aidlInfo.deviceId = info.getId();
aidlInfo.external = info.isExternal();
+ aidlInfo.keyboardType = info.getKeyboardType();
}
if (isFilterEnabled()) {
LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
index b9e6a03..5b0b9ac 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
@@ -23,4 +23,5 @@
parcelable DeviceInfo {
int deviceId;
boolean external;
+ int keyboardType;
}
\ No newline at end of file
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
index 2d5039a..dc4f1df 100644
--- a/services/inputflinger/rust/bounce_keys_filter.rs
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -17,12 +17,13 @@
//! 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;
+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::collections::{HashMap, HashSet};
@@ -42,7 +43,7 @@
next: Box<dyn Filter + Send + Sync>,
key_event_map: HashMap<i32, LastUpKeyEvent>,
blocked_events: Vec<BlockedEvent>,
- external_devices: HashSet<i32>,
+ supported_devices: HashSet<i32>,
bounce_key_threshold_ns: i64,
}
@@ -56,7 +57,7 @@
next,
key_event_map: HashMap::new(),
blocked_events: Vec::new(),
- external_devices: HashSet::new(),
+ supported_devices: HashSet::new(),
bounce_key_threshold_ns,
}
}
@@ -64,7 +65,7 @@
impl Filter for BounceKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
- if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+ if !(self.supported_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
self.next.notify_key(event);
return;
}
@@ -110,10 +111,17 @@
self.blocked_events.retain(|blocked_event| {
device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
});
- self.external_devices.clear();
+ self.supported_devices.clear();
for device_info in device_infos {
- if device_info.external {
- self.external_devices.insert(device_info.deviceId);
+ 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);
@@ -127,11 +135,12 @@
#[cfg(test)]
mod tests {
use crate::bounce_keys_filter::BounceKeysFilter;
- use crate::input_filter::{test_filter::TestFilter, Filter};
+ 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,
@@ -156,6 +165,7 @@
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
+ KeyboardType::Alphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
@@ -181,12 +191,68 @@
}
#[test]
- fn test_is_notify_key_doesnt_block_for_internal_keyboard() {
+ 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 };
@@ -209,6 +275,7 @@
Box::new(next.clone()),
1, /* device_id */
100, /* threshold */
+ KeyboardType::NonAlphabetic,
);
let event =
@@ -233,12 +300,60 @@
let mut filter = setup_filter_with_devices(
Box::new(next.clone()),
&[
- DeviceInfo { deviceId: 1, external: true },
- DeviceInfo { deviceId: 2, external: true },
+ 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);
@@ -261,10 +376,15 @@
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 }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: true,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
@@ -273,10 +393,15 @@
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 }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: false,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 8b44af3..f166723 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -35,6 +35,9 @@
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
+/// Virtual keyboard device ID
+pub const VIRTUAL_KEYBOARD_DEVICE_ID: i32 = -1;
+
/// Interface for all the sub input filters
pub trait Filter {
fn notify_key(&mut self, event: &KeyEvent);
@@ -214,6 +217,7 @@
InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use std::sync::{Arc, RwLock};
#[test]
@@ -256,7 +260,11 @@
Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
);
assert!(input_filter
- .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
+ .notifyInputDevicesChanged(&[DeviceInfo {
+ deviceId: 0,
+ external: true,
+ keyboardType: KeyboardType::None as i32
+ }])
.is_ok());
assert!(test_filter.is_device_changed_called());
}
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 0f18a2f..3e242d8 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -18,12 +18,13 @@
//! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows
//! the user to specify the duration for which one must press-and-hold a key before the system
//! accepts the keypress.
-use crate::input_filter::Filter;
+use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID};
use crate::input_filter_thread::{InputFilterThread, ThreadCallback};
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::collections::HashSet;
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
@@ -41,7 +42,7 @@
struct SlowKeysFilterInner {
next: Box<dyn Filter + Send + Sync>,
slow_key_threshold_ns: i64,
- external_devices: HashSet<i32>,
+ supported_devices: HashSet<i32>,
// This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the
// press duration exceeds the slow keys threshold.
pending_down_events: Vec<KeyEvent>,
@@ -65,7 +66,7 @@
let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner {
next,
slow_key_threshold_ns,
- external_devices: HashSet::new(),
+ supported_devices: HashSet::new(),
pending_down_events: Vec::new(),
ongoing_down_events: Vec::new(),
input_filter_thread: input_filter_thread.clone(),
@@ -98,7 +99,7 @@
{
// acquire write lock
let mut slow_filter = self.write_inner();
- if !(slow_filter.external_devices.contains(&event.deviceId)
+ if !(slow_filter.supported_devices.contains(&event.deviceId)
&& event.source == Source::KEYBOARD)
{
slow_filter.next.notify_key(event);
@@ -164,10 +165,17 @@
slow_filter
.ongoing_down_events
.retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId));
- slow_filter.external_devices.clear();
+ slow_filter.supported_devices.clear();
for device_info in device_infos {
- if device_info.external {
- slow_filter.external_devices.insert(device_info.deviceId);
+ 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 {
+ slow_filter.supported_devices.insert(device_info.deviceId);
}
}
slow_filter.next.notify_devices_changed(device_infos);
@@ -217,6 +225,7 @@
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
use std::sync::{Arc, RwLock};
use std::time::Duration;
@@ -240,7 +249,7 @@
static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms
#[test]
- fn test_is_notify_key_for_internal_keyboard_not_blocked() {
+ fn test_is_notify_key_for_internal_non_alphabetic_keyboard_not_blocked() {
let test_callbacks = TestCallbacks::new();
let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
@@ -249,6 +258,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
);
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
@@ -266,6 +276,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
);
let event =
@@ -275,6 +286,58 @@
}
#[test]
+ fn test_notify_key_for_internal_alphabetic_keyboard_when_key_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = get_thread(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
+ );
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ ..BASE_KEY_EVENT
+ });
+ assert!(next.last_event().is_none());
+
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
+ ..BASE_KEY_EVENT
+ }
+ );
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ }
+ );
+ }
+
+ #[test]
fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() {
let test_callbacks = TestCallbacks::new();
let test_thread = get_thread(test_callbacks.clone());
@@ -284,6 +347,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
filter.notify_key(&KeyEvent {
@@ -335,6 +399,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
filter.notify_key(&KeyEvent {
@@ -367,6 +432,7 @@
test_thread.clone(),
1, /* device_id */
SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::Alphabetic,
);
let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
@@ -388,11 +454,16 @@
test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> SlowKeysFilter {
setup_filter_with_devices(
next,
test_thread,
- &[DeviceInfo { deviceId: device_id, external: true }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: true,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
@@ -402,11 +473,16 @@
test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
+ keyboard_type: KeyboardType,
) -> SlowKeysFilter {
setup_filter_with_devices(
next,
test_thread,
- &[DeviceInfo { deviceId: device_id, external: false }],
+ &[DeviceInfo {
+ deviceId: device_id,
+ external: false,
+ keyboardType: keyboard_type as i32,
+ }],
threshold,
)
}
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 6c7c7fb..dfcea79 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -235,6 +235,7 @@
DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use input::KeyboardType;
use input::ModifierState;
use std::sync::{Arc, RwLock};
@@ -496,7 +497,11 @@
..BASE_KEY_UP
});
- sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
+ sticky_keys_filter.notify_devices_changed(&[DeviceInfo {
+ deviceId: 2,
+ external: true,
+ keyboardType: KeyboardType::Alphabetic as i32,
+ }]);
assert_eq!(
test_callbacks.get_last_modifier_state(),
ModifierState::CtrlLeftOn | ModifierState::CtrlOn