1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //! Contains the DataStore, used to store input related data in a persistent way. 18 19 use crate::input::KeyboardType; 20 use log::{debug, error, info}; 21 use serde::{Deserialize, Serialize}; 22 use std::fs::File; 23 use std::io::{Read, Write}; 24 use std::path::Path; 25 use std::sync::{Arc, RwLock}; 26 27 /// Data store to be used to store information that persistent across device reboots. 28 pub struct DataStore { 29 file_reader_writer: Box<dyn FileReaderWriter>, 30 inner: Arc<RwLock<DataStoreInner>>, 31 } 32 33 #[derive(Default)] 34 struct DataStoreInner { 35 is_loaded: bool, 36 data: Data, 37 } 38 39 #[derive(Default, Serialize, Deserialize)] 40 struct Data { 41 // Map storing data for keyboard classification for specific devices. 42 #[serde(default)] 43 keyboard_classifications: Vec<KeyboardClassification>, 44 // NOTE: Important things to consider: 45 // - Add any data that needs to be persisted here in this struct. 46 // - Mark all new fields with "#[serde(default)]" for backward compatibility. 47 // - Also, you can't modify the already added fields. 48 // - Can add new nested fields to existing structs. e.g. Add another field to the struct 49 // KeyboardClassification and mark it "#[serde(default)]". 50 } 51 52 #[derive(Default, Serialize, Deserialize)] 53 struct KeyboardClassification { 54 descriptor: String, 55 keyboard_type: KeyboardType, 56 is_finalized: bool, 57 } 58 59 impl DataStore { 60 /// Creates a new instance of Data store new(file_reader_writer: Box<dyn FileReaderWriter>) -> Self61 pub fn new(file_reader_writer: Box<dyn FileReaderWriter>) -> Self { 62 Self { file_reader_writer, inner: Default::default() } 63 } 64 load(&mut self)65 fn load(&mut self) { 66 if self.inner.read().unwrap().is_loaded { 67 return; 68 } 69 self.load_internal(); 70 } 71 load_internal(&mut self)72 fn load_internal(&mut self) { 73 let s = self.file_reader_writer.read(); 74 let data: Data = if !s.is_empty() { 75 let deserialize: Data = match serde_json::from_str(&s) { 76 Ok(deserialize) => deserialize, 77 Err(msg) => { 78 error!("Unable to deserialize JSON data into struct: {:?} -> {:?}", msg, s); 79 Default::default() 80 } 81 }; 82 deserialize 83 } else { 84 Default::default() 85 }; 86 87 let mut inner = self.inner.write().unwrap(); 88 inner.data = data; 89 inner.is_loaded = true; 90 } 91 save(&mut self)92 fn save(&mut self) { 93 let string_to_save; 94 { 95 let inner = self.inner.read().unwrap(); 96 string_to_save = serde_json::to_string(&inner.data).unwrap(); 97 } 98 self.file_reader_writer.write(string_to_save); 99 } 100 101 /// Get keyboard type of the device (as stored in the data store) get_keyboard_type(&mut self, descriptor: &String) -> Option<(KeyboardType, bool)>102 pub fn get_keyboard_type(&mut self, descriptor: &String) -> Option<(KeyboardType, bool)> { 103 self.load(); 104 let data = &self.inner.read().unwrap().data; 105 for keyboard_classification in data.keyboard_classifications.iter() { 106 if keyboard_classification.descriptor == *descriptor { 107 return Some(( 108 keyboard_classification.keyboard_type, 109 keyboard_classification.is_finalized, 110 )); 111 } 112 } 113 None 114 } 115 116 /// Save keyboard type of the device in the data store set_keyboard_type( &mut self, descriptor: &String, keyboard_type: KeyboardType, is_finalized: bool, )117 pub fn set_keyboard_type( 118 &mut self, 119 descriptor: &String, 120 keyboard_type: KeyboardType, 121 is_finalized: bool, 122 ) { 123 { 124 let data = &mut self.inner.write().unwrap().data; 125 data.keyboard_classifications 126 .retain(|classification| classification.descriptor != *descriptor); 127 data.keyboard_classifications.push(KeyboardClassification { 128 descriptor: descriptor.to_string(), 129 keyboard_type, 130 is_finalized, 131 }) 132 } 133 self.save(); 134 } 135 } 136 137 pub trait FileReaderWriter { read(&self) -> String138 fn read(&self) -> String; write(&self, to_write: String)139 fn write(&self, to_write: String); 140 } 141 142 /// Default file reader writer implementation 143 pub struct DefaultFileReaderWriter { 144 filepath: String, 145 } 146 147 impl DefaultFileReaderWriter { 148 /// Creates a new instance of Default file reader writer that can read and write string to a 149 /// particular file in the filesystem new(filepath: String) -> Self150 pub fn new(filepath: String) -> Self { 151 Self { filepath } 152 } 153 } 154 155 impl FileReaderWriter for DefaultFileReaderWriter { read(&self) -> String156 fn read(&self) -> String { 157 let path = Path::new(&self.filepath); 158 let mut fs_string = String::new(); 159 match File::open(path) { 160 Err(e) => info!("couldn't open {:?}: {}", path, e), 161 Ok(mut file) => match file.read_to_string(&mut fs_string) { 162 Err(e) => error!("Couldn't read from {:?}: {}", path, e), 163 Ok(_) => debug!("Successfully read from file {:?}", path), 164 }, 165 }; 166 fs_string 167 } 168 write(&self, to_write: String)169 fn write(&self, to_write: String) { 170 let path = Path::new(&self.filepath); 171 match File::create(path) { 172 Err(e) => error!("couldn't create {:?}: {}", path, e), 173 Ok(mut file) => match file.write_all(to_write.as_bytes()) { 174 Err(e) => error!("Couldn't write to {:?}: {}", path, e), 175 Ok(_) => debug!("Successfully saved to file {:?}", path), 176 }, 177 }; 178 } 179 } 180 181 #[cfg(test)] 182 mod tests { 183 use crate::data_store::{ 184 test_file_reader_writer::TestFileReaderWriter, DataStore, FileReaderWriter, 185 }; 186 use crate::input::KeyboardType; 187 188 #[test] test_backward_compatibility_version_1()189 fn test_backward_compatibility_version_1() { 190 // This test tests JSON string that will be created by the first version of data store 191 // This test SHOULD NOT be modified 192 let test_reader_writer = TestFileReaderWriter::new(); 193 test_reader_writer.write(r#"{"keyboard_classifications":[{"descriptor":"descriptor","keyboard_type":{"type":"Alphabetic"},"is_finalized":true}]}"#.to_string()); 194 195 let mut data_store = DataStore::new(Box::new(test_reader_writer)); 196 let (keyboard_type, is_finalized) = 197 data_store.get_keyboard_type(&"descriptor".to_string()).unwrap(); 198 assert_eq!(keyboard_type, KeyboardType::Alphabetic); 199 assert!(is_finalized); 200 } 201 } 202 203 #[cfg(test)] 204 pub mod test_file_reader_writer { 205 206 use crate::data_store::FileReaderWriter; 207 use std::sync::{Arc, RwLock}; 208 209 #[derive(Default)] 210 struct TestFileReaderWriterInner { 211 fs_string: String, 212 } 213 214 #[derive(Default, Clone)] 215 pub struct TestFileReaderWriter(Arc<RwLock<TestFileReaderWriterInner>>); 216 217 impl TestFileReaderWriter { new() -> Self218 pub fn new() -> Self { 219 Default::default() 220 } 221 } 222 223 impl FileReaderWriter for TestFileReaderWriter { read(&self) -> String224 fn read(&self) -> String { 225 self.0.read().unwrap().fs_string.clone() 226 } 227 write(&self, fs_string: String)228 fn write(&self, fs_string: String) { 229 self.0.write().unwrap().fs_string = fs_string; 230 } 231 } 232 } 233