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/Android.bp b/libs/input/rust/Android.bp
index 018d199..63853f7 100644
--- a/libs/input/rust/Android.bp
+++ b/libs/input/rust/Android.bp
@@ -24,6 +24,8 @@
         "liblogger",
         "liblog_rust",
         "inputconstants-rust",
+        "libserde",
+        "libserde_json",
     ],
     whole_static_libs: [
         "libinput_from_rust_to_cpp",
diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs
new file mode 100644
index 0000000..6bdcefd
--- /dev/null
+++ b/libs/input/rust/data_store.rs
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2024 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.
+ */
+
+//! Contains the DataStore, used to store input related data in a persistent way.
+
+use crate::input::KeyboardType;
+use log::{debug, error};
+use serde::{Deserialize, Serialize};
+use std::fs::File;
+use std::io::{Read, Write};
+use std::path::Path;
+use std::sync::{Arc, RwLock};
+
+/// Data store to be used to store information that persistent across device reboots.
+pub struct DataStore {
+    file_reader_writer: Box<dyn FileReaderWriter>,
+    inner: Arc<RwLock<DataStoreInner>>,
+}
+
+#[derive(Default)]
+struct DataStoreInner {
+    is_loaded: bool,
+    data: Data,
+}
+
+#[derive(Default, Serialize, Deserialize)]
+struct Data {
+    // Map storing data for keyboard classification for specific devices.
+    #[serde(default)]
+    keyboard_classifications: Vec<KeyboardClassification>,
+    // NOTE: Important things to consider:
+    // - Add any data that needs to be persisted here in this struct.
+    // - Mark all new fields with "#[serde(default)]" for backward compatibility.
+    // - Also, you can't modify the already added fields.
+    // - Can add new nested fields to existing structs. e.g. Add another field to the struct
+    //   KeyboardClassification and mark it "#[serde(default)]".
+}
+
+#[derive(Default, Serialize, Deserialize)]
+struct KeyboardClassification {
+    descriptor: String,
+    keyboard_type: KeyboardType,
+    is_finalized: bool,
+}
+
+impl DataStore {
+    /// Creates a new instance of Data store
+    pub fn new(file_reader_writer: Box<dyn FileReaderWriter>) -> Self {
+        Self { file_reader_writer, inner: Default::default() }
+    }
+
+    fn load(&mut self) {
+        if self.inner.read().unwrap().is_loaded {
+            return;
+        }
+        self.load_internal();
+    }
+
+    fn load_internal(&mut self) {
+        let s = self.file_reader_writer.read();
+        let data: Data = if !s.is_empty() {
+            let deserialize: Data = match serde_json::from_str(&s) {
+                Ok(deserialize) => deserialize,
+                Err(msg) => {
+                    error!("Unable to deserialize JSON data into struct: {:?} -> {:?}", msg, s);
+                    Default::default()
+                }
+            };
+            deserialize
+        } else {
+            Default::default()
+        };
+
+        let mut inner = self.inner.write().unwrap();
+        inner.data = data;
+        inner.is_loaded = true;
+    }
+
+    fn save(&mut self) {
+        let string_to_save;
+        {
+            let inner = self.inner.read().unwrap();
+            string_to_save = serde_json::to_string(&inner.data).unwrap();
+        }
+        self.file_reader_writer.write(string_to_save);
+    }
+
+    /// Get keyboard type of the device (as stored in the data store)
+    pub fn get_keyboard_type(&mut self, descriptor: &String) -> Option<(KeyboardType, bool)> {
+        self.load();
+        let data = &self.inner.read().unwrap().data;
+        for keyboard_classification in data.keyboard_classifications.iter() {
+            if keyboard_classification.descriptor == *descriptor {
+                return Some((
+                    keyboard_classification.keyboard_type,
+                    keyboard_classification.is_finalized,
+                ));
+            }
+        }
+        None
+    }
+
+    /// Save keyboard type of the device in the data store
+    pub fn set_keyboard_type(
+        &mut self,
+        descriptor: &String,
+        keyboard_type: KeyboardType,
+        is_finalized: bool,
+    ) {
+        {
+            let data = &mut self.inner.write().unwrap().data;
+            data.keyboard_classifications
+                .retain(|classification| classification.descriptor != *descriptor);
+            data.keyboard_classifications.push(KeyboardClassification {
+                descriptor: descriptor.to_string(),
+                keyboard_type,
+                is_finalized,
+            })
+        }
+        self.save();
+    }
+}
+
+pub trait FileReaderWriter {
+    fn read(&self) -> String;
+    fn write(&self, to_write: String);
+}
+
+/// Default file reader writer implementation
+pub struct DefaultFileReaderWriter {
+    filepath: String,
+}
+
+impl DefaultFileReaderWriter {
+    /// Creates a new instance of Default file reader writer that can read and write string to a
+    /// particular file in the filesystem
+    pub fn new(filepath: String) -> Self {
+        Self { filepath }
+    }
+}
+
+impl FileReaderWriter for DefaultFileReaderWriter {
+    fn read(&self) -> String {
+        let path = Path::new(&self.filepath);
+        let mut fs_string = String::new();
+        match File::open(path) {
+            Err(e) => error!("couldn't open {:?}: {}", path, e),
+            Ok(mut file) => match file.read_to_string(&mut fs_string) {
+                Err(e) => error!("Couldn't read from {:?}: {}", path, e),
+                Ok(_) => debug!("Successfully read from file {:?}", path),
+            },
+        };
+        fs_string
+    }
+
+    fn write(&self, to_write: String) {
+        let path = Path::new(&self.filepath);
+        match File::create(path) {
+            Err(e) => error!("couldn't create {:?}: {}", path, e),
+            Ok(mut file) => match file.write_all(to_write.as_bytes()) {
+                Err(e) => error!("Couldn't write to {:?}: {}", path, e),
+                Ok(_) => debug!("Successfully saved to file {:?}", path),
+            },
+        };
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::data_store::{
+        test_file_reader_writer::TestFileReaderWriter, DataStore, FileReaderWriter,
+    };
+    use crate::input::KeyboardType;
+
+    #[test]
+    fn test_backward_compatibility_version_1() {
+        // This test tests JSON string that will be created by the first version of data store
+        // This test SHOULD NOT be modified
+        let test_reader_writer = TestFileReaderWriter::new();
+        test_reader_writer.write(r#"{"keyboard_classifications":[{"descriptor":"descriptor","keyboard_type":{"type":"Alphabetic"},"is_finalized":true}]}"#.to_string());
+
+        let mut data_store = DataStore::new(Box::new(test_reader_writer));
+        let (keyboard_type, is_finalized) =
+            data_store.get_keyboard_type(&"descriptor".to_string()).unwrap();
+        assert_eq!(keyboard_type, KeyboardType::Alphabetic);
+        assert!(is_finalized);
+    }
+}
+
+#[cfg(test)]
+pub mod test_file_reader_writer {
+
+    use crate::data_store::FileReaderWriter;
+    use std::sync::{Arc, RwLock};
+
+    #[derive(Default)]
+    struct TestFileReaderWriterInner {
+        fs_string: String,
+    }
+
+    #[derive(Default, Clone)]
+    pub struct TestFileReaderWriter(Arc<RwLock<TestFileReaderWriterInner>>);
+
+    impl TestFileReaderWriter {
+        pub fn new() -> Self {
+            Default::default()
+        }
+    }
+
+    impl FileReaderWriter for TestFileReaderWriter {
+        fn read(&self) -> String {
+            self.0.read().unwrap().fs_string.clone()
+        }
+
+        fn write(&self, fs_string: String) {
+            self.0.write().unwrap().fs_string = fs_string;
+        }
+    }
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index c46b7bb..90f509d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -20,6 +20,7 @@
 use bitflags::bitflags;
 use inputconstants::aidl::android::os::IInputConstants;
 use inputconstants::aidl::android::os::MotionEventFlag::MotionEventFlag;
+use serde::{Deserialize, Serialize};
 use std::fmt;
 
 /// The InputDevice ID.
@@ -324,9 +325,11 @@
 
 /// A rust enum representation of a Keyboard type.
 #[repr(u32)]
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
 pub enum KeyboardType {
     /// KEYBOARD_TYPE_NONE
+    #[default]
     None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE,
     /// KEYBOARD_TYPE_NON_ALPHABETIC
     NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
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,
         }
     }
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index af8f889..008f675 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -16,11 +16,13 @@
 
 //! The rust component of libinput.
 
+mod data_store;
 mod input;
 mod input_verifier;
 mod keyboard_classification_config;
 mod keyboard_classifier;
 
+pub use data_store::{DataStore, DefaultFileReaderWriter};
 pub use input::{
     DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source,
 };
@@ -149,7 +151,14 @@
 }
 
 fn create_keyboard_classifier() -> Box<KeyboardClassifier> {
-    Box::new(KeyboardClassifier::new())
+    // Future design: Make this data store singleton by passing it to C++ side and making it global
+    // and pass by reference to components that need to store persistent data.
+    //
+    // Currently only used by rust keyboard classifier so keeping it here.
+    let data_store = DataStore::new(Box::new(DefaultFileReaderWriter::new(
+        "/data/system/inputflinger-data.json".to_string(),
+    )));
+    Box::new(KeyboardClassifier::new(data_store))
 }
 
 fn notify_keyboard_changed(