Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93144689 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Add sticky keys input filter

DD: go/pk_accessibility
'Sticky keys' is an accessibility feature that assists users
who have physical disabilities or help users reduce repetitive
strain injury. It serializes keystrokes instead of pressing
multiple keys at a time, allowing the user to press and
release a modifier key, such as Shift, Ctrl, Alt, or any other
modifier key, and have it remain active until any other key
is pressed.

Bug: 294546335
Test: TEST=libinputflinger_rs_test; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
Change-Id: Id26ed4a949e929cb6af8b9ecf1cd95c48fe20486
parent b507b71c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ filegroup {
        "InputCommonConverter.cpp",
        "InputDeviceMetricsCollector.cpp",
        "InputFilter.cpp",
        "InputFilterCallbacks.cpp",
        "InputProcessor.cpp",
        "PointerChoreographer.cpp",
        "PreferStylusOverTouchBlocker.cpp",
+14 −27
Original line number Diff line number Diff line
@@ -44,31 +44,9 @@ AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
    return event;
}

NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
    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 {

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)) {
      : mNextListener(listener),
        mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) {
    LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
    LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
}
@@ -92,11 +70,11 @@ void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArg
}

void InputFilter::notifyKey(const NotifyKeyArgs& args) {
    if (!isFilterEnabled()) {
        mNextListener.notifyKey(args);
    if (isFilterEnabled()) {
        LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
        return;
    }
    LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
    mNextListener.notifyKey(args);
}

void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
@@ -138,6 +116,15 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) {
    }
}

void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) {
    std::scoped_lock _l(mLock);

    if (mConfig.stickyKeysEnabled != enabled) {
        mConfig.stickyKeysEnabled = enabled;
        LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
    }
}

void InputFilter::dump(std::string& dump) {
    dump += "InputFilter:\n";
}
+4 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <utils/Mutex.h>
#include "InputFilterCallbacks.h"
#include "InputListener.h"
#include "NotifyArgs.h"

@@ -33,6 +34,7 @@ public:
     */
    virtual void dump(std::string& dump) = 0;
    virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
    virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
};

class InputFilter : public InputFilterInterface {
@@ -56,11 +58,12 @@ public:
    void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
    void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
    void setAccessibilityStickyKeysEnabled(bool enabled) override;
    void dump(std::string& dump) override;

private:
    InputListenerInterface& mNextListener;
    std::shared_ptr<IInputFilterCallbacks> mCallbacks;
    std::shared_ptr<InputFilterCallbacks> mCallbacks;
    std::shared_ptr<IInputFilter> mInputFilterRust;
    mutable std::mutex mLock;
    InputFilterConfiguration mConfig GUARDED_BY(mLock);
+60 −0
Original line number Diff line number Diff line
/*
 * 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 "InputFilterCallbacks"

#include "InputFilterCallbacks.h"

namespace android {

using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;

NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
    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);
}

InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener)
      : mNextListener(listener) {}

ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) {
    mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState,
                                                                int32_t lockedModifierState) {
    std::scoped_lock _l(mLock);
    mStickyModifierState.modifierState = modifierState;
    mStickyModifierState.lockedModifierState = lockedModifierState;
    ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d",
          modifierState, lockedModifierState);
    return ndk::ScopedAStatus::ok();
}

uint32_t InputFilterCallbacks::getModifierState() {
    std::scoped_lock _l(mLock);
    return mStickyModifierState.modifierState;
}

uint32_t InputFilterCallbacks::getLockedModifierState() {
    std::scoped_lock _l(mLock);
    return mStickyModifierState.lockedModifierState;
}

} // namespace android
+55 −0
Original line number Diff line number Diff line
/*
 * 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 <android/binder_auto_utils.h>
#include <utils/Mutex.h>
#include <mutex>
#include "InputListener.h"
#include "NotifyArgs.h"

/**
 * The C++ component of InputFilter designed as a wrapper around the rust callback implementation.
 */
namespace android {

using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;

class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks {
public:
    explicit InputFilterCallbacks(InputListenerInterface& listener);
    ~InputFilterCallbacks() override = default;

    uint32_t getModifierState();
    uint32_t getLockedModifierState();

private:
    InputListenerInterface& mNextListener;
    mutable std::mutex mLock;
    struct StickyModifierState {
        uint32_t modifierState;
        uint32_t lockedModifierState;
    } mStickyModifierState GUARDED_BY(mLock);

    ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override;
    ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState,
                                              int32_t lockedModifierState) override;
};

} // namespace android
 No newline at end of file
Loading