Add bounce keys input filter

DD: go/pk_accessibility
Bug: 294546335
Test: TEST=libinputflinger_rs_test; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
Change-Id: Id38b3d0fc830d16327b36d1b4376c0a66b6551aa
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 1b8fad3..9c4a3eb 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -22,16 +22,19 @@
 
 using aidl::com::android::server::inputflinger::IInputFilter;
 using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+using aidl::com::android::server::inputflinger::KeyEventAction;
+using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo;
+using aidl::android::hardware::input::common::Source;
 
 AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
     AidlKeyEvent event;
     event.id = args.id;
     event.eventTime = args.eventTime;
     event.deviceId = args.deviceId;
-    event.source = args.source;
+    event.source = static_cast<Source>(args.source);
     event.displayId = args.displayId;
     event.policyFlags = args.policyFlags;
-    event.action = args.action;
+    event.action = static_cast<KeyEventAction>(args.action);
     event.flags = args.flags;
     event.keyCode = args.keyCode;
     event.scanCode = args.scanCode;
@@ -42,9 +45,10 @@
 }
 
 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);
+    return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
+                         static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
+                         static_cast<int32_t>(event.action), event.flags, event.keyCode,
+                         event.scanCode, event.metaState, event.downTime);
 }
 
 namespace {
@@ -71,11 +75,14 @@
 
 void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
     if (isFilterEnabled()) {
-        std::vector<int32_t> deviceIds;
+        std::vector<AidlDeviceInfo> deviceInfos;
         for (auto info : args.inputDeviceInfos) {
-            deviceIds.push_back(info.getId());
+            AidlDeviceInfo aidlInfo;
+            aidlInfo.deviceId = info.getId();
+            aidlInfo.external = info.isExternal();
+            deviceInfos.push_back(aidlInfo);
         }
-        LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceIds).isOk());
+        LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceInfos).isOk());
     }
     mNextListener.notify(args);
 }
@@ -122,6 +129,15 @@
     return result;
 }
 
+void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) {
+    std::scoped_lock _l(mLock);
+
+    if (mConfig.bounceKeysThresholdNs != threshold) {
+        mConfig.bounceKeysThresholdNs = threshold;
+        LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+    }
+}
+
 void InputFilter::dump(std::string& dump) {
     dump += "InputFilter:\n";
 }
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 699f3a0..06f7d0e 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <utils/Mutex.h>
 #include "InputListener.h"
 #include "NotifyArgs.h"
 
@@ -31,6 +32,7 @@
      * This method may be called on any thread (usually by the input manager on a binder thread).
      */
     virtual void dump(std::string& dump) = 0;
+    virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
 };
 
 class InputFilter : public InputFilterInterface {
@@ -39,6 +41,8 @@
     using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
     using IInputFilterCallbacks =
             aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
+    using InputFilterConfiguration =
+            aidl::com::android::server::inputflinger::InputFilterConfiguration;
 
     explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&);
     ~InputFilter() override = default;
@@ -51,12 +55,15 @@
     void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
     void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+    void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
     void dump(std::string& dump) override;
 
 private:
     InputListenerInterface& mNextListener;
     std::shared_ptr<IInputFilterCallbacks> mCallbacks;
     std::shared_ptr<IInputFilter> mInputFilterRust;
+    mutable std::mutex mLock;
+    InputFilterConfiguration mConfig GUARDED_BY(mLock);
 
     bool isFilterEnabled();
 };
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 8cf61f9..02997f0 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -224,6 +224,10 @@
     return *mDispatcher;
 }
 
+InputFilterInterface& InputManager::getInputFilter() {
+    return *mInputFilter;
+}
+
 void InputManager::monitor() {
     mReader->monitor();
     mBlocker->monitor();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index aea7bd5..fa7db37 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -102,6 +102,9 @@
     /* Gets the input dispatcher. */
     virtual InputDispatcherInterface& getDispatcher() = 0;
 
+    /* Gets the input filter */
+    virtual InputFilterInterface& getInputFilter() = 0;
+
     /* Check that the input stages have not deadlocked. */
     virtual void monitor() = 0;
 
@@ -126,6 +129,7 @@
     InputProcessorInterface& getProcessor() override;
     InputDeviceMetricsCollectorInterface& getMetricsCollector() override;
     InputDispatcherInterface& getDispatcher() override;
+    InputFilterInterface& getInputFilter() override;
     void monitor() override;
     void dump(std::string& dump) override;
 
diff --git a/services/inputflinger/aidl/Android.bp b/services/inputflinger/aidl/Android.bp
index 314c433..d068129 100644
--- a/services/inputflinger/aidl/Android.bp
+++ b/services/inputflinger/aidl/Android.bp
@@ -17,6 +17,9 @@
     srcs: ["**/*.aidl"],
     unstable: true,
     host_supported: true,
+    imports: [
+        "android.hardware.input.common-V1",
+    ],
     backend: {
         cpp: {
             enabled: false,
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
new file mode 100644
index 0000000..b9e6a03
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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 InputDeviceInfo
+ * Stores the basic information connected input devices.
+ */
+parcelable DeviceInfo {
+    int deviceId;
+    boolean external;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 44f959e..14b41cd 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -16,6 +16,8 @@
 
 package com.android.server.inputflinger;
 
+import com.android.server.inputflinger.DeviceInfo;
+import com.android.server.inputflinger.InputFilterConfiguration;
 import com.android.server.inputflinger.KeyEvent;
 
 /**
@@ -40,6 +42,9 @@
     void notifyKey(in KeyEvent event);
 
     /** Notifies if any InputDevice list changed and provides the list of connected peripherals */
-    void notifyInputDevicesChanged(in int[] deviceIds);
+    void notifyInputDevicesChanged(in DeviceInfo[] deviceInfos);
+
+    /** Notifies when configuration changes */
+    void notifyConfigurationChanged(in InputFilterConfiguration config);
 }
 
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
new file mode 100644
index 0000000..3b2e88b
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Contains data for the current Input filter configuration
+ */
+parcelable InputFilterConfiguration {
+    // Threshold value for Bounce keys filter (check bounce_keys_filter.rs)
+    long bounceKeysThresholdNs;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
index e213221..2cae6e1 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
@@ -16,20 +16,24 @@
 
 package com.android.server.inputflinger;
 
+import android.hardware.input.common.Source;
+import com.android.server.inputflinger.KeyEventAction;
+
 /**
  * Analogous to Android's native KeyEvent / NotifyKeyArgs.
  * Stores the basic information about Key events.
  */
+@RustDerive(Copy=true, Clone=true, Eq=true, PartialEq=true)
 parcelable KeyEvent {
     int id;
     int deviceId;
     long downTime;
     long readTime;
     long eventTime;
-    int source;
+    Source source;
     int displayId;
     int policyFlags;
-    int action;
+    KeyEventAction action;
     int flags;
     int keyCode;
     int scanCode;
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl
new file mode 100644
index 0000000..43ee5fe
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.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;
+
+/** Different Key event actions */
+enum KeyEventAction {
+    /** The key has been pressed down. */
+    DOWN = 0,
+
+    /** The key has been released. */
+    UP = 1,
+
+    /**
+     * Multiple duplicate key events have occurred in a row, or a
+     * complex string is being delivered.  The repeat_count property
+     * of the key event contains the number of times the given key
+     * code should be executed.
+     *
+     * NOTE: This is deprecated and should never be used. This just
+     * for consistency with KeyEvent actions defined in NotifyKeyArgs.
+     */
+    MULTIPLE = 2
+}
\ No newline at end of file
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 2775bcc..2803805 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -38,6 +38,7 @@
     rustlibs: [
         "libcxx",
         "com.android.server.inputflinger-rust",
+        "android.hardware.input.common-V1-rust",
         "libbinder_rs",
         "liblog_rust",
         "liblogger",
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
new file mode 100644
index 0000000..894b881
--- /dev/null
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+//! Bounce keys input filter implementation.
+//! Bounce keys is an accessibility feature to aid users who have physical disabilities, that
+//! allows the user to configure the device to ignore rapid, repeated key presses of the same key.
+use crate::input_filter::Filter;
+
+use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+    DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use log::debug;
+use std::collections::{HashMap, HashSet};
+
+#[derive(Debug)]
+struct LastUpKeyEvent {
+    keycode: i32,
+    event_time: i64,
+}
+
+#[derive(Debug)]
+struct BlockedEvent {
+    device_id: i32,
+    keycode: i32,
+}
+
+pub struct BounceKeysFilter {
+    next: Box<dyn Filter + Send + Sync>,
+    key_event_map: HashMap<i32, LastUpKeyEvent>,
+    blocked_events: Vec<BlockedEvent>,
+    external_devices: HashSet<i32>,
+    bounce_key_threshold_ns: i64,
+}
+
+impl BounceKeysFilter {
+    /// Create a new BounceKeysFilter instance.
+    pub fn new(
+        next: Box<dyn Filter + Send + Sync>,
+        bounce_key_threshold_ns: i64,
+    ) -> BounceKeysFilter {
+        Self {
+            next,
+            key_event_map: HashMap::new(),
+            blocked_events: Vec::new(),
+            external_devices: HashSet::new(),
+            bounce_key_threshold_ns,
+        }
+    }
+}
+
+impl Filter for BounceKeysFilter {
+    fn notify_key(&mut self, event: &KeyEvent) {
+        if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+            self.next.notify_key(event);
+            return;
+        }
+        match event.action {
+            KeyEventAction::DOWN => match self.key_event_map.get(&event.deviceId) {
+                None => self.next.notify_key(event),
+                Some(last_up_event) => {
+                    if event.keyCode == last_up_event.keycode
+                        && event.eventTime < last_up_event.event_time + self.bounce_key_threshold_ns
+                    {
+                        self.blocked_events.push(BlockedEvent {
+                            device_id: event.deviceId,
+                            keycode: event.keyCode,
+                        });
+                        debug!("Event dropped because last up was too recent");
+                    } else {
+                        self.key_event_map.remove(&event.deviceId);
+                        self.next.notify_key(event);
+                    }
+                }
+            },
+            KeyEventAction::UP => {
+                self.key_event_map.insert(
+                    event.deviceId,
+                    LastUpKeyEvent { keycode: event.keyCode, event_time: event.eventTime },
+                );
+                if let Some(index) = self.blocked_events.iter().position(|blocked_event| {
+                    blocked_event.device_id == event.deviceId
+                        && blocked_event.keycode == event.keyCode
+                }) {
+                    self.blocked_events.remove(index);
+                    debug!("Event dropped because key down was already dropped");
+                } else {
+                    self.next.notify_key(event);
+                }
+            }
+            _ => (),
+        }
+    }
+
+    fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+        self.key_event_map.retain(|id, _| device_infos.iter().any(|x| *id == x.deviceId));
+        self.blocked_events.retain(|blocked_event| {
+            device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
+        });
+        self.external_devices.clear();
+        for device_info in device_infos {
+            if device_info.external {
+                self.external_devices.insert(device_info.deviceId);
+            }
+        }
+        self.next.notify_devices_changed(device_infos);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::bounce_keys_filter::BounceKeysFilter;
+    use crate::input_filter::{test_filter::TestFilter, Filter};
+    use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+    use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+        DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+    };
+
+    static BASE_KEY_EVENT: KeyEvent = KeyEvent {
+        id: 1,
+        deviceId: 1,
+        downTime: 0,
+        readTime: 0,
+        eventTime: 0,
+        source: Source::KEYBOARD,
+        displayId: 0,
+        policyFlags: 0,
+        action: KeyEventAction::DOWN,
+        flags: 0,
+        keyCode: 1,
+        scanCode: 0,
+        metaState: 0,
+    };
+
+    #[test]
+    fn test_is_notify_key_for_external_keyboard() {
+        let mut next = TestFilter::new();
+        let mut filter = setup_filter_with_external_device(
+            Box::new(next.clone()),
+            1,   /* device_id */
+            100, /* threshold */
+        );
+
+        let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        next.clear();
+        let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert!(next.last_event().is_none());
+
+        let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert!(next.last_event().is_none());
+
+        let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+    }
+
+    #[test]
+    fn test_is_notify_key_doesnt_block_for_internal_keyboard() {
+        let next = TestFilter::new();
+        let mut filter = setup_filter_with_internal_device(
+            Box::new(next.clone()),
+            1,   /* device_id */
+            100, /* threshold */
+        );
+
+        let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+    }
+
+    #[test]
+    fn test_is_notify_key_doesnt_block_for_external_stylus() {
+        let next = TestFilter::new();
+        let mut filter = setup_filter_with_external_device(
+            Box::new(next.clone()),
+            1,   /* device_id */
+            100, /* threshold */
+        );
+
+        let event =
+            KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event =
+            KeyEvent { action: KeyEventAction::UP, source: Source::STYLUS, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event =
+            KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+    }
+
+    #[test]
+    fn test_is_notify_key_for_multiple_external_keyboards() {
+        let mut next = TestFilter::new();
+        let mut filter = setup_filter_with_devices(
+            Box::new(next.clone()),
+            &[
+                DeviceInfo { deviceId: 1, external: true },
+                DeviceInfo { deviceId: 2, external: true },
+            ],
+            100, /* threshold */
+        );
+
+        let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+
+        next.clear();
+        let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert!(next.last_event().is_none());
+
+        let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+        filter.notify_key(&event);
+        assert_eq!(next.last_event().unwrap(), event);
+    }
+
+    fn setup_filter_with_external_device(
+        next: Box<dyn Filter + Send + Sync>,
+        device_id: i32,
+        threshold: i64,
+    ) -> BounceKeysFilter {
+        setup_filter_with_devices(
+            next,
+            &[DeviceInfo { deviceId: device_id, external: true }],
+            threshold,
+        )
+    }
+
+    fn setup_filter_with_internal_device(
+        next: Box<dyn Filter + Send + Sync>,
+        device_id: i32,
+        threshold: i64,
+    ) -> BounceKeysFilter {
+        setup_filter_with_devices(
+            next,
+            &[DeviceInfo { deviceId: device_id, external: false }],
+            threshold,
+        )
+    }
+
+    fn setup_filter_with_devices(
+        next: Box<dyn Filter + Send + Sync>,
+        devices: &[DeviceInfo],
+        threshold: i64,
+    ) -> BounceKeysFilter {
+        let mut filter = BounceKeysFilter::new(next, threshold);
+        filter.notify_devices_changed(devices);
+        filter
+    }
+}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 5851877..340ff8e 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -16,17 +16,39 @@
 
 //! 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.
+//! like Sticky keys, Slow keys, Bounce keys, etc.
 
 use binder::{Interface, Strong};
 use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+    DeviceInfo::DeviceInfo,
     IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+    InputFilterConfiguration::InputFilterConfiguration,
     KeyEvent::KeyEvent,
 };
 
+use crate::bounce_keys_filter::BounceKeysFilter;
+use log::{error, info};
+use std::sync::{Arc, Mutex, RwLock};
+
+/// Interface for all the sub input filters
+pub trait Filter {
+    fn notify_key(&mut self, event: &KeyEvent);
+    fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
+}
+
+struct InputFilterState {
+    first_filter: Box<dyn Filter + Send + Sync>,
+    enabled: bool,
+}
+
 /// The rust implementation of InputFilter
 pub struct InputFilter {
-    callbacks: Strong<dyn IInputFilterCallbacks>,
+    // In order to have multiple immutable references to the callbacks that is thread safe need to
+    // wrap the callbacks in Arc<RwLock<...>>
+    callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+    // Access to mutable references to mutable state (includes access to filters, enabled, etc.) is
+    // guarded by Mutex for thread safety
+    state: Mutex<InputFilterState>,
 }
 
 impl Interface for InputFilter {}
@@ -34,35 +56,87 @@
 impl InputFilter {
     /// Create a new InputFilter instance.
     pub fn new(callbacks: Strong<dyn IInputFilterCallbacks>) -> InputFilter {
-        Self { callbacks }
+        let ref_callbacks = Arc::new(RwLock::new(callbacks));
+        let base_filter = Box::new(BaseFilter::new(ref_callbacks.clone()));
+        Self::create_input_filter(base_filter, ref_callbacks)
+    }
+
+    /// Create test instance of InputFilter
+    fn create_input_filter(
+        first_filter: Box<dyn Filter + Send + Sync>,
+        callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+    ) -> InputFilter {
+        Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) }
     }
 }
 
 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)
+        Result::Ok(self.state.lock().unwrap().enabled)
     }
+
     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);
+        let first_filter = &mut self.state.lock().unwrap().first_filter;
+        first_filter.notify_key(event);
         Result::Ok(())
     }
-    fn notifyInputDevicesChanged(&self, _device_ids: &[i32]) -> binder::Result<()> {
-        // TODO(b/294546335): Update data based on device changes here
+
+    fn notifyInputDevicesChanged(&self, device_infos: &[DeviceInfo]) -> binder::Result<()> {
+        let first_filter = &mut self.state.lock().unwrap().first_filter;
+        first_filter.notify_devices_changed(device_infos);
         Result::Ok(())
     }
+
+    fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> {
+        let mut state = self.state.lock().unwrap();
+        let mut first_filter: Box<dyn Filter + Send + Sync> =
+            Box::new(BaseFilter::new(self.callbacks.clone()));
+        if config.bounceKeysThresholdNs > 0 {
+            first_filter =
+                Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
+            state.enabled = true;
+            info!("Bounce keys filter is installed");
+        }
+        state.first_filter = first_filter;
+        Result::Ok(())
+    }
+}
+
+struct BaseFilter {
+    callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl BaseFilter {
+    fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> BaseFilter {
+        Self { callbacks }
+    }
+}
+
+impl Filter for BaseFilter {
+    fn notify_key(&mut self, event: &KeyEvent) {
+        match self.callbacks.read().unwrap().sendKeyEvent(event) {
+            Ok(_) => (),
+            _ => error!("Failed to send key event back to native C++"),
+        }
+    }
+
+    fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+        // do nothing
+    }
 }
 
 #[cfg(test)]
 mod tests {
-    use crate::input_filter::InputFilter;
+    use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter};
+    use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
     use binder::{Interface, Strong};
     use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
-        IInputFilter::IInputFilter, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
-        KeyEvent::KeyEvent,
+        DeviceInfo::DeviceInfo, IInputFilter::IInputFilter,
+        IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+        InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
+        KeyEventAction::KeyEventAction,
     };
+    use std::sync::{Arc, RwLock};
 
     struct FakeCallbacks {}
 
@@ -75,31 +149,60 @@
     }
 
     #[test]
-    fn test_is_enabled() {
+    fn test_not_enabled_with_default_filter() {
         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();
+        let input_filter = InputFilter::new(fake_callbacks);
+        let result = input_filter.isEnabled();
         assert!(result.is_ok());
         assert!(!result.unwrap());
     }
 
     #[test]
-    fn test_notify_key() {
+    fn test_notify_key_with_no_filters() {
         let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
             Strong::new(Box::new(FakeCallbacks {}));
-        let filter: Box<dyn IInputFilter> = Box::new(InputFilter::new(fake_callbacks));
+        let input_filter = InputFilter::new(fake_callbacks);
         let event = create_key_event();
-        assert!(filter.notifyKey(&event).is_ok());
+        assert!(input_filter.notifyKey(&event).is_ok());
+    }
+
+    #[test]
+    fn test_notify_key_with_filter() {
+        let test_filter = TestFilter::new();
+        let input_filter = create_input_filter(Box::new(test_filter.clone()));
+        let event = create_key_event();
+        assert!(input_filter.notifyKey(&event).is_ok());
+        assert_eq!(test_filter.last_event().unwrap(), event);
     }
 
     #[test]
     fn test_notify_devices_changed() {
+        let test_filter = TestFilter::new();
+        let input_filter = create_input_filter(Box::new(test_filter.clone()));
+        assert!(input_filter
+            .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
+            .is_ok());
+        assert!(test_filter.is_device_changed_called());
+    }
+
+    #[test]
+    fn test_notify_configuration_changed_enabled_bounce_keys() {
         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]);
+        let input_filter = InputFilter::new(fake_callbacks);
+        let result = input_filter
+            .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 });
         assert!(result.is_ok());
+        let result = input_filter.isEnabled();
+        assert!(result.is_ok());
+        assert!(result.unwrap());
+    }
+
+    fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter {
+        let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
+            Strong::new(Box::new(FakeCallbacks {}));
+        InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks)))
     }
 
     fn create_key_event() -> KeyEvent {
@@ -109,10 +212,10 @@
             downTime: 0,
             readTime: 0,
             eventTime: 0,
-            source: 0,
+            source: Source::KEYBOARD,
             displayId: 0,
             policyFlags: 0,
-            action: 0,
+            action: KeyEventAction::DOWN,
             flags: 0,
             keyCode: 0,
             scanCode: 0,
@@ -120,3 +223,52 @@
         }
     }
 }
+
+#[cfg(test)]
+pub mod test_filter {
+    use crate::input_filter::Filter;
+    use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+        DeviceInfo::DeviceInfo, KeyEvent::KeyEvent,
+    };
+    use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+    #[derive(Default)]
+    struct TestFilterInner {
+        is_device_changed_called: bool,
+        last_event: Option<KeyEvent>,
+    }
+
+    #[derive(Default, Clone)]
+    pub struct TestFilter(Arc<RwLock<TestFilterInner>>);
+
+    impl TestFilter {
+        pub fn new() -> Self {
+            Default::default()
+        }
+
+        fn inner(&mut self) -> RwLockWriteGuard<'_, TestFilterInner> {
+            self.0.write().unwrap()
+        }
+
+        pub fn last_event(&self) -> Option<KeyEvent> {
+            self.0.read().unwrap().last_event
+        }
+
+        pub fn clear(&mut self) {
+            self.inner().last_event = None
+        }
+
+        pub fn is_device_changed_called(&self) -> bool {
+            self.0.read().unwrap().is_device_changed_called
+        }
+    }
+
+    impl Filter for TestFilter {
+        fn notify_key(&mut self, event: &KeyEvent) {
+            self.inner().last_event = Some(*event);
+        }
+        fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+            self.inner().is_device_changed_called = true;
+        }
+    }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index a4049d5..68cd480 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -19,6 +19,7 @@
 //! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
 //! pass it back to C++ as a local AIDL interface.
 
+mod bounce_keys_filter;
 mod input_filter;
 
 use crate::input_filter::InputFilter;