Add InputFilter rust component as InputListener stage.
Test: TEST=libinputflinger_rs_test; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
Bug: 294546335
Change-Id: I2731d45f141c12c1e61b794ee614690b0a121e28
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 23c1691..2775bcc 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -31,8 +31,8 @@
out: ["inputflinger_bootstrap.rs.h"],
}
-rust_ffi_static {
- name: "libinputflinger_rs",
+rust_defaults {
+ name: "libinputflinger_rs_defaults",
crate_name: "inputflinger",
srcs: ["lib.rs"],
rustlibs: [
@@ -45,6 +45,24 @@
host_supported: true,
}
+rust_ffi_static {
+ name: "libinputflinger_rs",
+ defaults: ["libinputflinger_rs_defaults"],
+}
+
+rust_test {
+ name: "libinputflinger_rs_test",
+ defaults: ["libinputflinger_rs_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ address: true,
+ hwaddress: true,
+ },
+}
+
cc_library_headers {
name: "inputflinger_rs_bootstrap_cxx_headers",
host_supported: true,
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
new file mode 100644
index 0000000..5851877
--- /dev/null
+++ b/services/inputflinger/rust/input_filter.rs
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+//! InputFilter manages all the filtering components that can intercept events, modify the events,
+//! block events, etc depending on the situation. This will be used support Accessibility features
+//! like Slow keys, Bounce keys, etc.
+
+use binder::{Interface, Strong};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ KeyEvent::KeyEvent,
+};
+
+/// The rust implementation of InputFilter
+pub struct InputFilter {
+ callbacks: Strong<dyn IInputFilterCallbacks>,
+}
+
+impl Interface for InputFilter {}
+
+impl InputFilter {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Strong<dyn IInputFilterCallbacks>) -> InputFilter {
+ Self { callbacks }
+ }
+}
+
+impl IInputFilter for InputFilter {
+ fn isEnabled(&self) -> binder::Result<bool> {
+ // TODO(b/294546335): Return true if any filters are to be applied, false otherwise
+ Result::Ok(false)
+ }
+ fn notifyKey(&self, event: &KeyEvent) -> binder::Result<()> {
+ // TODO(b/294546335): Handle key event and modify key events here
+ // Just send back the event without processing for now.
+ let _ = self.callbacks.sendKeyEvent(event);
+ Result::Ok(())
+ }
+ fn notifyInputDevicesChanged(&self, _device_ids: &[i32]) -> binder::Result<()> {
+ // TODO(b/294546335): Update data based on device changes here
+ Result::Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::InputFilter;
+ use binder::{Interface, Strong};
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::IInputFilter, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ KeyEvent::KeyEvent,
+ };
+
+ struct FakeCallbacks {}
+
+ impl Interface for FakeCallbacks {}
+
+ impl IInputFilterCallbacks for FakeCallbacks {
+ fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> {
+ Result::Ok(())
+ }
+ }
+
+ #[test]
+ fn test_is_enabled() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let result = filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_notify_key() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let event = create_key_event();
+ assert!(filter.notifyKey(&event).is_ok());
+ }
+
+ #[test]
+ fn test_notify_devices_changed() {
+ let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+ Strong::new(Box::new(FakeCallbacks {}));
+ let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+ let result = filter.notifyInputDevicesChanged(&[0]);
+ assert!(result.is_ok());
+ }
+
+ fn create_key_event() -> KeyEvent {
+ KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: 0,
+ displayId: 0,
+ policyFlags: 0,
+ action: 0,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index 501e435..a4049d5 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -19,13 +19,19 @@
//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
//! pass it back to C++ as a local AIDL interface.
+mod input_filter;
+
+use crate::input_filter::InputFilter;
use binder::{
- unstable_api::{AIBinder, new_spibinder,},
+ unstable_api::{new_spibinder, AIBinder},
BinderFeatures, Interface, StatusCode, Strong,
};
-use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{
- BnInputFlingerRust, IInputFlingerRust,
- IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::{BnInputFilter, IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ IInputFlingerRust::{
+ BnInputFlingerRust, IInputFlingerRust,
+ IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+ },
};
use log::debug;
@@ -71,8 +77,8 @@
// SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its
// reference count has been incremented..
let Some(callback) = (unsafe { new_spibinder(callback) }) else {
- panic!("Failed to get SpAIBinder from raw callback pointer");
- };
+ panic!("Failed to get SpAIBinder from raw callback pointer");
+ };
let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> =
callback.into_interface();
@@ -93,7 +99,19 @@
impl Interface for InputFlingerRust {}
-impl IInputFlingerRust for InputFlingerRust {}
+impl IInputFlingerRust for InputFlingerRust {
+ fn createInputFilter(
+ &self,
+ callbacks: &Strong<dyn IInputFilterCallbacks>,
+ ) -> binder::Result<Strong<dyn IInputFilter>> {
+ debug!("Creating InputFilter");
+ let filter = BnInputFilter::new_binder(
+ InputFilter::new(callbacks.clone()),
+ BinderFeatures::default(),
+ );
+ Result::Ok(filter)
+ }
+}
impl Drop for InputFlingerRust {
fn drop(&mut self) {