Create basic data store to store keyboard classification
Implements a basic file system backed data store (using a json
file), to store keyboard classification information persisted
across reboots.
Test: atest --host libinput_rust_test
Bug: 263559234
Flag: com.android.input.flags.enable_keyboard_classifier
Change-Id: I521444342ae4e98191262273b881775fb2157ef2
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
index 8721ef7..3c789b4 100644
--- a/libs/input/rust/keyboard_classifier.rs
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -31,9 +31,8 @@
//! across multiple device connections in a time period, then change type to
//! KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic
//! (i.e. verified = false).
-//!
-//! TODO(b/263559234): Data store implementation to store information about past classification
+use crate::data_store::DataStore;
use crate::input::{DeviceId, InputDevice, KeyboardType};
use crate::keyboard_classification_config::CLASSIFIED_DEVICES;
use crate::{DeviceClass, ModifierState};
@@ -41,30 +40,28 @@
/// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic
/// keyboard or non-alphabetic keyboard
-#[derive(Default)]
pub struct KeyboardClassifier {
device_map: HashMap<DeviceId, KeyboardInfo>,
+ data_store: DataStore,
}
struct KeyboardInfo {
- _device: InputDevice,
+ device: InputDevice,
keyboard_type: KeyboardType,
is_finalized: bool,
}
impl KeyboardClassifier {
/// Create a new KeyboardClassifier
- pub fn new() -> Self {
- Default::default()
+ pub fn new(data_store: DataStore) -> Self {
+ Self { device_map: HashMap::new(), data_store }
}
/// Adds keyboard to KeyboardClassifier
pub fn notify_keyboard_changed(&mut self, device: InputDevice) {
let (keyboard_type, is_finalized) = self.classify_keyboard(&device);
- self.device_map.insert(
- device.device_id,
- KeyboardInfo { _device: device, keyboard_type, is_finalized },
- );
+ self.device_map
+ .insert(device.device_id, KeyboardInfo { device, keyboard_type, is_finalized });
}
/// Get keyboard type for a tracked keyboard in KeyboardClassifier
@@ -107,11 +104,16 @@
if Self::is_alphabetic_key(&evdev_code) {
keyboard.keyboard_type = KeyboardType::Alphabetic;
keyboard.is_finalized = true;
+ self.data_store.set_keyboard_type(
+ &keyboard.device.identifier.descriptor,
+ keyboard.keyboard_type,
+ keyboard.is_finalized,
+ );
}
}
}
- fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) {
+ fn classify_keyboard(&mut self, device: &InputDevice) -> (KeyboardType, bool) {
// This should never happen but having keyboard device class is necessary to be classified
// as any type of keyboard.
if !device.classes.contains(DeviceClass::Keyboard) {
@@ -128,10 +130,17 @@
};
}
+ // Check in data store
+ if let Some((keyboard_type, is_finalized)) =
+ self.data_store.get_keyboard_type(&device.identifier.descriptor)
+ {
+ return (keyboard_type, is_finalized);
+ }
+
// Check in known device list for classification
- for data in CLASSIFIED_DEVICES.iter() {
- if device.identifier.vendor == data.0 && device.identifier.product == data.1 {
- return (data.2, data.3);
+ for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() {
+ if device.identifier.vendor == *vendor && device.identifier.product == *product {
+ return (*keyboard_type, *is_finalized);
}
}
@@ -177,18 +186,20 @@
#[cfg(test)]
mod tests {
+ use crate::data_store::{test_file_reader_writer::TestFileReaderWriter, DataStore};
use crate::input::{DeviceId, InputDevice, KeyboardType};
use crate::keyboard_classification_config::CLASSIFIED_DEVICES;
use crate::keyboard_classifier::KeyboardClassifier;
use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier};
static DEVICE_ID: DeviceId = DeviceId(1);
+ static SECOND_DEVICE_ID: DeviceId = DeviceId(2);
static KEY_A: i32 = 30;
static KEY_1: i32 = 2;
#[test]
fn classify_external_alphabetic_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
));
@@ -198,7 +209,7 @@
#[test]
fn classify_external_non_alphabetic_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier
.notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External));
assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
@@ -207,7 +218,7 @@
#[test]
fn classify_mouse_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Cursor
@@ -220,7 +231,7 @@
#[test]
fn classify_touchpad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Touchpad
@@ -233,7 +244,7 @@
#[test]
fn classify_stylus_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::ExternalStylus
@@ -246,7 +257,7 @@
#[test]
fn classify_dpad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -259,7 +270,7 @@
#[test]
fn classify_joystick_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Joystick
@@ -272,7 +283,7 @@
#[test]
fn classify_gamepad_pretending_as_keyboard() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Gamepad
@@ -285,7 +296,7 @@
#[test]
fn reclassify_keyboard_on_alphabetic_key_event() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -303,7 +314,7 @@
#[test]
fn dont_reclassify_keyboard_on_non_alphabetic_key_event() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -321,7 +332,7 @@
#[test]
fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() {
- let mut classifier = KeyboardClassifier::new();
+ let mut classifier = create_classifier();
classifier.notify_keyboard_changed(create_device(
DeviceClass::Keyboard
| DeviceClass::Dpad
@@ -338,28 +349,71 @@
#[test]
fn classify_known_devices() {
- let mut classifier = KeyboardClassifier::new();
- for device in CLASSIFIED_DEVICES.iter() {
+ let mut classifier = create_classifier();
+ for (vendor, product, keyboard_type, is_finalized) in CLASSIFIED_DEVICES.iter() {
classifier
- .notify_keyboard_changed(create_device_with_vendor_product_ids(device.0, device.1));
- assert_eq!(classifier.get_keyboard_type(DEVICE_ID), device.2);
- assert_eq!(classifier.is_finalized(DEVICE_ID), device.3);
+ .notify_keyboard_changed(create_device_with_vendor_product_ids(*vendor, *product));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), *keyboard_type);
+ assert_eq!(classifier.is_finalized(DEVICE_ID), *is_finalized);
+ }
+ }
+
+ #[test]
+ fn classify_previously_reclassified_devices() {
+ let test_reader_writer = TestFileReaderWriter::new();
+ {
+ let mut classifier =
+ KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone())));
+ let device = create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ );
+ classifier.notify_keyboard_changed(device);
+ classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None);
+ }
+
+ // Re-create classifier and data store to mimic a reboot (but use the same file system
+ // reader writer)
+ {
+ let mut classifier =
+ KeyboardClassifier::new(DataStore::new(Box::new(test_reader_writer.clone())));
+ let device = InputDevice {
+ device_id: SECOND_DEVICE_ID,
+ identifier: create_identifier(/* vendor= */ 234, /* product= */ 345),
+ classes: DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ };
+ classifier.notify_keyboard_changed(device);
+ assert_eq!(classifier.get_keyboard_type(SECOND_DEVICE_ID), KeyboardType::Alphabetic);
+ assert!(classifier.is_finalized(SECOND_DEVICE_ID));
+ }
+ }
+
+ fn create_classifier() -> KeyboardClassifier {
+ KeyboardClassifier::new(DataStore::new(Box::new(TestFileReaderWriter::new())))
+ }
+
+ fn create_identifier(vendor: u16, product: u16) -> RustInputDeviceIdentifier {
+ RustInputDeviceIdentifier {
+ name: "test_device".to_string(),
+ location: "location".to_string(),
+ unique_id: "unique_id".to_string(),
+ bus: 123,
+ vendor,
+ product,
+ version: 567,
+ descriptor: "descriptor".to_string(),
}
}
fn create_device(classes: DeviceClass) -> InputDevice {
InputDevice {
device_id: DEVICE_ID,
- identifier: RustInputDeviceIdentifier {
- name: "test_device".to_string(),
- location: "location".to_string(),
- unique_id: "unique_id".to_string(),
- bus: 123,
- vendor: 234,
- product: 345,
- version: 567,
- descriptor: "descriptor".to_string(),
- },
+ identifier: create_identifier(/* vendor= */ 234, /* product= */ 345),
classes,
}
}
@@ -367,16 +421,7 @@
fn create_device_with_vendor_product_ids(vendor: u16, product: u16) -> InputDevice {
InputDevice {
device_id: DEVICE_ID,
- identifier: RustInputDeviceIdentifier {
- name: "test_device".to_string(),
- location: "location".to_string(),
- unique_id: "unique_id".to_string(),
- bus: 123,
- vendor,
- product,
- version: 567,
- descriptor: "descriptor".to_string(),
- },
+ identifier: create_identifier(vendor, product),
classes: DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
}
}