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/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 3672387..a807d82 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -62,3 +62,10 @@
description: "Disable touch rejection when the stylus hovers the screen"
bug: "301216095"
}
+
+flag {
+ name: "enable_input_filter_rust_impl"
+ namespace: "input"
+ description: "Enable input filter rust implementation"
+ bug: "294546335"
+}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 76729ef..10f386e 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -76,6 +76,7 @@
srcs: [
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
+ "InputFilter.cpp",
"InputProcessor.cpp",
"PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
new file mode 100644
index 0000000..1b8fad3
--- /dev/null
+++ b/services/inputflinger/InputFilter.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputFilter"
+
+#include "InputFilter.h"
+
+namespace android {
+
+using aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
+ AidlKeyEvent event;
+ event.id = args.id;
+ event.eventTime = args.eventTime;
+ event.deviceId = args.deviceId;
+ event.source = args.source;
+ event.displayId = args.displayId;
+ event.policyFlags = args.policyFlags;
+ event.action = args.action;
+ event.flags = args.flags;
+ event.keyCode = args.keyCode;
+ event.scanCode = args.scanCode;
+ event.metaState = args.metaState;
+ event.downTime = args.downTime;
+ event.readTime = args.readTime;
+ return event;
+}
+
+NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
+ return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId, event.source,
+ event.displayId, event.policyFlags, event.action, event.flags,
+ event.keyCode, event.scanCode, event.metaState, event.downTime);
+}
+
+namespace {
+
+class RustCallbacks : public IInputFilter::BnInputFilterCallbacks {
+public:
+ RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {}
+ ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override {
+ mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
+ return ndk::ScopedAStatus::ok();
+ }
+
+private:
+ InputListenerInterface& mNextListener;
+};
+
+} // namespace
+
+InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust)
+ : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) {
+ LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
+}
+
+void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ if (isFilterEnabled()) {
+ std::vector<int32_t> deviceIds;
+ for (auto info : args.inputDeviceInfos) {
+ deviceIds.push_back(info.getId());
+ }
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceIds).isOk());
+ }
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyKey(const NotifyKeyArgs& args) {
+ if (!isFilterEnabled()) {
+ mNextListener.notifyKey(args);
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
+}
+
+void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySwitch(const NotifySwitchArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySensor(const NotifySensorArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+bool InputFilter::isFilterEnabled() {
+ bool result;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->isEnabled(&result).isOk());
+ return result;
+}
+
+void InputFilter::dump(std::string& dump) {
+ dump += "InputFilter:\n";
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
new file mode 100644
index 0000000..699f3a0
--- /dev/null
+++ b/services/inputflinger/InputFilter.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+namespace android {
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust implementation.
+ */
+class InputFilterInterface : public InputListenerInterface {
+public:
+ /**
+ * This method may be called on any thread (usually by the input manager on a binder thread).
+ */
+ virtual void dump(std::string& dump) = 0;
+};
+
+class InputFilter : public InputFilterInterface {
+public:
+ using IInputFlingerRust = aidl::com::android::server::inputflinger::IInputFlingerRust;
+ using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+ using IInputFilterCallbacks =
+ aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
+
+ explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&);
+ ~InputFilter() override = default;
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+ void notifyKey(const NotifyKeyArgs& args) override;
+ void notifyMotion(const NotifyMotionArgs& args) override;
+ void notifySwitch(const NotifySwitchArgs& args) override;
+ void notifySensor(const NotifySensorArgs& args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ void dump(std::string& dump) override;
+
+private:
+ InputListenerInterface& mNextListener;
+ std::shared_ptr<IInputFilterCallbacks> mCallbacks;
+ std::shared_ptr<IInputFilter> mInputFilterRust;
+
+ bool isFilterEnabled();
+};
+
+} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 92c65e1..8cf61f9 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -42,6 +42,7 @@
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
@@ -118,6 +119,7 @@
* The event flow is via the "InputListener" interface, as follows:
* InputReader
* -> UnwantedInteractionBlocker
+ * -> InputFilter
* -> PointerChoreographer
* -> InputProcessor
* -> InputDeviceMetricsCollector
@@ -132,6 +134,12 @@
mTracingStages.emplace_back(
std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
+ if (ENABLE_INPUT_FILTER_RUST) {
+ mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
+ }
+
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
mTracingStages.emplace_back(
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 20b9fd5..aea7bd5 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -21,6 +21,7 @@
*/
#include "InputDeviceMetricsCollector.h"
+#include "InputFilter.h"
#include "InputProcessor.h"
#include "InputReaderBase.h"
#include "PointerChoreographer.h"
@@ -40,6 +41,7 @@
using android::os::BnInputFlinger;
+using aidl::com::android::server::inputflinger::IInputFilter;
using aidl::com::android::server::inputflinger::IInputFlingerRust;
namespace android {
@@ -137,6 +139,8 @@
std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;
+ std::unique_ptr<InputFilterInterface> mInputFilter;
+
std::unique_ptr<PointerChoreographerInterface> mChoreographer;
std::unique_ptr<InputProcessorInterface> mProcessor;
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
new file mode 100644
index 0000000..44f959e
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+import com.android.server.inputflinger.KeyEvent;
+
+/**
+ * A local AIDL interface used as a foreign function interface (ffi) to
+ * filter input events.
+ *
+ * NOTE: Since we use this as a local interface, all processing happens on the
+ * calling thread.
+ */
+interface IInputFilter {
+
+ /** Callbacks for the rust InputFilter to call into C++. */
+ interface IInputFilterCallbacks {
+ /** Sends back a filtered key event */
+ void sendKeyEvent(in KeyEvent event);
+ }
+
+ /** Returns if InputFilter is enabled */
+ boolean isEnabled();
+
+ /** Notifies if a key event occurred */
+ void notifyKey(in KeyEvent event);
+
+ /** Notifies if any InputDevice list changed and provides the list of connected peripherals */
+ void notifyInputDevicesChanged(in int[] deviceIds);
+}
+
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
index 8e826fd..de852c0 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
@@ -16,6 +16,9 @@
package com.android.server.inputflinger;
+import com.android.server.inputflinger.IInputFilter;
+import com.android.server.inputflinger.IInputFilter.IInputFilterCallbacks;
+
/**
* A local AIDL interface used as a foreign function interface (ffi) to
* communicate with the Rust component of inputflinger.
@@ -31,4 +34,7 @@
interface IInputFlingerRustBootstrapCallback {
void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust);
}
+
+ /** Create the rust implementation of InputFilter. */
+ IInputFilter createInputFilter(IInputFilterCallbacks callbacks);
}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
new file mode 100644
index 0000000..e213221
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.server.inputflinger;
+
+/**
+ * Analogous to Android's native KeyEvent / NotifyKeyArgs.
+ * Stores the basic information about Key events.
+ */
+parcelable KeyEvent {
+ int id;
+ int deviceId;
+ long downTime;
+ long readTime;
+ long eventTime;
+ int source;
+ int displayId;
+ int policyFlags;
+ int action;
+ int flags;
+ int keyCode;
+ int scanCode;
+ int metaState;
+}
\ No newline at end of file
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) {