diff options
8 files changed, 138 insertions, 0 deletions
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index a93a2ea615..88e1d7db06 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -447,6 +447,9 @@ public: const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0; /* Notifies the input reader policy that a stylus gesture has started. */ virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0; + + /* Returns true if any InputConnection is currently active. */ + virtual bool isInputMethodConnectionActive() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 7388752805..d51ec454cf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -242,6 +242,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read keyDown.downTime = when; mKeyDowns.push_back(keyDown); } + tryHideCursorOnKeyDown(); } else { // Remove key down. if (keyDownIndex) { @@ -419,4 +420,13 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { return out; } +void KeyboardInputMapper::tryHideCursorOnKeyDown() { + // Hide the cursor while user is inputting text, ignoring meta keys or multiple simultaneous + // down keys as they are likely to be shortcuts + const bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode); + if (shouldHideCursor && getContext()->getPolicy()->isInputMethodConnectionActive()) { + getContext()->fadePointer(); + } +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index cd3d3c49e9..361abe0bcf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -104,6 +104,7 @@ private: void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig); [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); + void tryHideCursorOnKeyDown(); }; } // namespace android diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 300bb85630..1585fddfb3 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -62,6 +62,7 @@ cc_test { "SyncQueue_test.cpp", "TestInputListener.cpp", "TouchpadInputMapper_test.cpp", + "KeyboardInputMapper_test.cpp", "UinputDevice.cpp", "UnwantedInteractionBlocker_test.cpp", ], diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 3486d0f2a4..30222bf407 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -209,6 +209,14 @@ void FakeInputReaderPolicy::setStylusPointerIconEnabled(bool enabled) { mConfig.stylusPointerIconEnabled = enabled; } +void FakeInputReaderPolicy::setIsInputMethodConnectionActive(bool active) { + mIsInputMethodConnectionActive = active; +} + +bool FakeInputReaderPolicy::isInputMethodConnectionActive() { + return mIsInputMethodConnectionActive; +} + void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) { *outConfig = mConfig; } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 85ff01a071..78bb2c3894 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -77,6 +77,8 @@ public: void setVelocityControlParams(const VelocityControlParameters& params); void setStylusButtonMotionEventsEnabled(bool enabled); void setStylusPointerIconEnabled(bool enabled); + void setIsInputMethodConnectionActive(bool active); + bool isInputMethodConnectionActive() override; private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; @@ -99,6 +101,7 @@ private: std::vector<DisplayViewport> mViewports; TouchAffineTransformation transform; std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){}; + bool mIsInputMethodConnectionActive{false}; uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp new file mode 100644 index 0000000000..81060f6ee0 --- /dev/null +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include "KeyboardInputMapper.h" + +#include <gtest/gtest.h> + +#include "InputMapperTest.h" +#include "InterfaceMocks.h" + +#define TAG "KeyboardInputMapper_test" + +namespace android { + +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SetArgPointee; + +/** + * Unit tests for KeyboardInputMapper. + */ +class KeyboardInputMapperUnitTest : public InputMapperUnitTest { +protected: + sp<FakeInputReaderPolicy> mFakePolicy; + const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0}, + {KEY_A, AKEYCODE_A}, + {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT}, + {KEY_LEFTALT, AKEYCODE_ALT_LEFT}, + {KEY_RIGHTALT, AKEYCODE_ALT_RIGHT}, + {KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT}, + {KEY_RIGHTSHIFT, AKEYCODE_SHIFT_RIGHT}, + {KEY_FN, AKEYCODE_FUNCTION}, + {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT}, + {KEY_RIGHTCTRL, AKEYCODE_CTRL_RIGHT}, + {KEY_LEFTMETA, AKEYCODE_META_LEFT}, + {KEY_RIGHTMETA, AKEYCODE_META_RIGHT}, + {KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK}, + {KEY_NUMLOCK, AKEYCODE_NUM_LOCK}, + {KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK}}; + + void SetUp() override { + InputMapperUnitTest::SetUp(); + + // set key-codes expected in tests + for (const auto& [scanCode, outKeycode] : mKeyCodeMap) { + EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR))); + } + + mFakePolicy = sp<FakeInputReaderPolicy>::make(); + EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get())); + + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + } + + void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) { + EXPECT_CALL(mMockInputReaderContext, fadePointer) + .Times(expectVisible ? 0 : keyCodes.size()); + for (int32_t keyCode : keyCodes) { + process(EV_KEY, keyCode, 1); + process(EV_SYN, SYN_REPORT, 0); + process(EV_KEY, keyCode, 0); + process(EV_SYN, SYN_REPORT, 0); + } + } +}; + +/** + * Pointer visibility should remain unaffected if there is no active Input Method Connection + */ +TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) { + testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true); +} + +/** + * Pointer should hide if there is a active Input Method Connection + */ +TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) { + mFakePolicy->setIsInputMethodConnectionActive(true); + testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false); +} + +/** + * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is + * active + */ +TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) { + mFakePolicy->setIsInputMethodConnectionActive(true); + std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, + KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, + KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK}; + testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true); +} + +} // namespace android diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 1e44e0fba0..ec98f814a8 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -289,6 +289,7 @@ public: } void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; } void notifyStylusGestureStarted(int32_t, nsecs_t) {} + bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); } }; class FuzzInputListener : public virtual InputListenerInterface { |