diff options
18 files changed, 329 insertions, 74 deletions
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index ba06101059..5f2f8dc013 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -278,9 +278,9 @@ struct layer_state_t { layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFixedTransformHintChanged; // Changes affecting data sent to input. - static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged | - layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged | - layer_state_t::eLayerStackChanged; + static constexpr uint64_t INPUT_CHANGES = layer_state_t::eAlphaChanged | + layer_state_t::eInputInfoChanged | layer_state_t::eDropInputModeChanged | + layer_state_t::eTrustedOverlayChanged | layer_state_t::eLayerStackChanged; // Changes that affect the visible region on a display. static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES | diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 7d3a2df550..00dd6ba62b 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -21,6 +21,7 @@ #if defined(__ANDROID__) #include <gui/SurfaceComposerClient.h> #endif +#include <input/Keyboard.h> #include <input/PrintTools.h> #include <unordered_set> @@ -137,6 +138,7 @@ PointerChoreographer::PointerChoreographer( mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID), mShowTouchesEnabled(false), mStylusPointerIconEnabled(false), + mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT), mRegisterListener(registerListener), mUnregisterListener(unregisterListener) {} @@ -168,6 +170,7 @@ void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationC } void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) { + fadeMouseCursorOnKeyPress(args); mNextListener.notify(args); } @@ -177,6 +180,32 @@ void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) { mNextListener.notify(newArgs); } +void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) { + if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) { + return; + } + // Meta state for these keys is ignored for dismissing cursor while typing + constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | + AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON; + if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) { + // Do not fade if any other meta state is active + return; + } + if (!mPolicy.isInputMethodConnectionActive()) { + return; + } + + std::scoped_lock _l(mLock); + ui::LogicalDisplayId targetDisplay = args.displayId; + if (targetDisplay == ui::LogicalDisplayId::INVALID) { + targetDisplay = mCurrentFocusedDisplay; + } + auto it = mMousePointersByDisplay.find(targetDisplay); + if (it != mMousePointersByDisplay.end()) { + it->second->fade(PointerControllerInterface::Transition::GRADUAL); + } +} + NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) { std::scoped_lock _l(mLock); @@ -806,6 +835,11 @@ void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId display } } +void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) { + std::scoped_lock lock(mLock); + mCurrentFocusedDisplay = displayId; +} + PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( ui::LogicalDisplayId displayId) { std::function<std::shared_ptr<PointerControllerInterface>()> ctor = diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index d9b075f3ee..aaf1e3e962 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -76,6 +76,11 @@ public: virtual void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) = 0; /** + * Used by Dispatcher to notify changes in the current focused display. + */ + virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0; + + /** * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; @@ -97,6 +102,7 @@ public: bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, ui::LogicalDisplayId displayId, DeviceId deviceId) override; void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override; + void setFocusedDisplay(ui::LogicalDisplayId displayId) override; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -124,6 +130,7 @@ private: InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock); + void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); @@ -192,6 +199,7 @@ private: bool mShowTouchesEnabled GUARDED_BY(mLock); bool mStylusPointerIconEnabled GUARDED_BY(mLock); std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden; + ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock); protected: using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>( diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5ed5eb8e33..47c288931d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -5526,6 +5526,13 @@ void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) { synthesizeCancelationEventsForWindowLocked(windowHandle, options); } mFocusedDisplayId = displayId; + // Enqueue a command to run outside the lock to tell the policy that the focused display + // changed. + auto command = [this]() REQUIRES(mLock) { + scoped_unlock unlock(mLock); + mPolicy.notifyFocusedDisplayChanged(mFocusedDisplayId); + }; + postCommandLocked(std::move(command)); // Only a window on the focused display can have Pointer Capture, so disable the active // Pointer Capture session if there is one, since the focused display changed. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 0f03620f05..65fb76d274 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -76,6 +76,11 @@ public: InputDeviceSensorAccuracy accuracy) = 0; virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0; + /* + * Notifies the system that focused display has changed. + */ + virtual void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) = 0; + /* Filters an input event. * Return true to dispatch the event unmodified, false to consume the event. * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h index cae638f7bf..5b94d57b8e 100644 --- a/services/inputflinger/include/NotifyArgsBuilders.h +++ b/services/inputflinger/include/NotifyArgsBuilders.h @@ -21,6 +21,7 @@ #include <attestation/HmacKeyManager.h> #include <input/Input.h> #include <input/InputEventBuilders.h> +#include <input/Keyboard.h> #include <utils/Timers.h> // for nsecs_t, systemTime #include <vector> @@ -206,6 +207,12 @@ public: return *this; } + KeyArgsBuilder& metaState(int32_t metaState) { + mMetaState |= metaState; + mMetaState = normalizeMetaState(/*oldMetaState=*/mMetaState); + return *this; + } + NotifyKeyArgs build() const { return {mEventId, mEventTime, diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index f6dc10997a..7a85c12559 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -55,6 +55,9 @@ public: */ virtual void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId, const FloatPoint& position) = 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 91ec62d3d4..25f4893baf 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -493,7 +493,6 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) { InputReaderContext& context = *getContext(); context.setLastKeyDownTimestamp(downTime); - // TODO(b/338652288): Move cursor fading logic into PointerChoreographer. // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard // shortcuts bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode); diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index e17ee3a5d9..3df05f4bae 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -219,6 +219,24 @@ void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBe mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; } +void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + if (!mFocusedDisplayNotifiedCondition.wait_for(lock, 100ms, + [this, expectedDisplay]() REQUIRES(mLock) { + if (!mNotifiedFocusedDisplay.has_value() || + mNotifiedFocusedDisplay.value() != + expectedDisplay) { + return false; + } + return true; + })) { + ADD_FAILURE() << "Timed out waiting for notifyFocusedDisplayChanged(" << expectedDisplay + << ") to be called."; + } +} + void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -473,4 +491,10 @@ void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal( mFilteredEvent = nullptr; } +void FakeInputDispatcherPolicy::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) { + std::scoped_lock lock(mLock); + mNotifiedFocusedDisplay = displayId; + mFocusedDisplayNotifiedCondition.notify_all(); +} + } // namespace android diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index 62ff10f8c8..a0f3ea9008 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -116,6 +116,7 @@ public: void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching); + void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay); private: std::mutex mLock; @@ -126,6 +127,9 @@ private: std::condition_variable mPointerCaptureChangedCondition; + std::optional<ui::LogicalDisplayId> mNotifiedFocusedDisplay GUARDED_BY(mLock); + std::condition_variable mFocusedDisplayNotifiedCondition; + std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock); // ANR handling std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); @@ -201,6 +205,7 @@ private: void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, const std::set<gui::Uid>& uids) override; + void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) override; void assertFilterInputEventWasCalledInternal( const std::function<void(const InputEvent&)>& verify); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 56a05a3a3c..aa1462a2ff 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -8577,6 +8577,8 @@ public: // Set focus to second display window. // Set focus display to second one. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); + // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); windowInSecondary->setFocusable(true); @@ -11066,6 +11068,7 @@ TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) { // Make the second display the focused display. mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); // This causes the first window to lose pointer capture, and it's unable to request capture. mWindow->consumeCaptureEvent(false); @@ -13769,4 +13772,9 @@ TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse) { /*pointerId=*/0)); } +TEST_F(InputDispatcherTest, FocusedDisplayChangeIsNotified) { + mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 44417246d2..16d3193908 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -186,6 +186,7 @@ public: (PointerControllerInterface::ControllerType), (override)); MOCK_METHOD(void, notifyPointerDisplayIdChanged, (ui::LogicalDisplayId displayId, const FloatPoint& position), (override)); + MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override)); }; } // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index ada841d28d..ab47cc67b1 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -70,15 +70,6 @@ protected: AINPUT_SOURCE_KEYBOARD); } - void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) { - 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); - } - } - void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes, const bool expectPrevent) { if (expectPrevent) { @@ -95,42 +86,6 @@ protected: }; /** - * 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 should still hide if touchpad taps are already disabled - */ -TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithTouchpadTapDisabledHidePointer) { - mFakePolicy->setIsInputMethodConnectionActive(true); - EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).WillRepeatedly(Return(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); -} - -/** * Touchpad tap should not be disabled if there is no active Input Method Connection */ TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) { diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 3f2d6ec45c..9a5b6a73f5 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -2294,6 +2294,185 @@ TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) { assertPointerControllerRemoved(pc); } +class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest { +protected: + const std::unordered_map<int32_t, int32_t> + mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON}, + {AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON}, + {AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON}, + {AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON}, + {AKEYCODE_SYM, AMETA_SYM_ON}, + {AKEYCODE_FUNCTION, AMETA_FUNCTION_ON}, + {AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON}, + {AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON}, + {AKEYCODE_META_LEFT, AMETA_META_LEFT_ON}, + {AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON}, + {AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON}, + {AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON}, + {AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON}}; + + void notifyKey(ui::LogicalDisplayId targetDisplay, int32_t keyCode, + int32_t metaState = AMETA_NONE) { + if (metaState == AMETA_NONE && mMetaKeyStates.contains(keyCode)) { + // For simplicity, we always set the corresponding meta state when sending a meta + // keycode. This does not take into consideration when the meta state is updated in + // reality. + metaState = mMetaKeyStates.at(keyCode); + } + mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(targetDisplay) + .keyCode(keyCode) + .metaState(metaState) + .build()); + mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .displayId(targetDisplay) + .keyCode(keyCode) + .metaState(metaState) + .build()); + } + + void metaKeyCombinationHidesPointer(FakePointerController& pc, int32_t keyCode, + int32_t metaKeyCode) { + ASSERT_TRUE(pc.isPointerShown()); + notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode)); + ASSERT_FALSE(pc.isPointerShown()); + + unfadePointer(); + } + + void metaKeyCombinationDoesNotHidePointer(FakePointerController& pc, int32_t keyCode, + int32_t metaKeyCode) { + ASSERT_TRUE(pc.isPointerShown()); + notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode)); + ASSERT_TRUE(pc.isPointerShown()); + } + + void unfadePointer() { + // unfade pointer by injecting mose hover event + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + } +}; + +TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A); + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT); + + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + notifyKey(DISPLAY_ID, AKEYCODE_0); + ASSERT_FALSE(pc->isPointerShown()); + + unfadePointer(); + + notifyKey(DISPLAY_ID, AKEYCODE_A); + ASSERT_FALSE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT, + AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT, + AKEYCODE_SYM, AKEYCODE_FUNCTION, + AKEYCODE_CTRL_LEFT, AKEYCODE_CTRL_RIGHT, + AKEYCODE_META_LEFT, AKEYCODE_META_RIGHT, + AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK, + AKEYCODE_SCROLL_LOCK}; + for (int32_t keyCode : metaKeyCodes) { + notifyKey(ui::LogicalDisplayId::INVALID, keyCode); + } + + ASSERT_TRUE(pc->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setFocusedDisplay(DISPLAY_ID); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}}); + auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE); + auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_TRUE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); + + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0); + ASSERT_FALSE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); + unfadePointer(); + + notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A); + ASSERT_FALSE(pc1->isPointerShown()); + ASSERT_TRUE(pc2->isPointerShown()); +} + +TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + + // Mouse connected + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true)); + + // meta key combinations that should hide pointer + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_0, AKEYCODE_NUM_LOCK); + metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK); + + // meta key combinations that should not hide pointer + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_RIGHT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_SYM); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_FUNCTION); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_LEFT); + metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT); +} + class PointerChoreographerWindowInfoListenerTest : public testing::Test {}; TEST_F_WITH_FLAGS( diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 84f668d9df..8c0f81e051 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -79,6 +79,9 @@ Display::Display(android::Hwc2::Composer& composer, DisplayType type) : mComposer(composer), mCapabilities(capabilities), mId(id), mType(type) { ALOGV("Created display %" PRIu64, id); + if (mType == hal::DisplayType::VIRTUAL) { + loadDisplayCapabilities(); + } } Display::~Display() { @@ -499,29 +502,7 @@ Error Display::setPowerMode(PowerMode mode) auto intError = mComposer.setPowerMode(mId, intMode); if (mode == PowerMode::ON) { - std::call_once(mDisplayCapabilityQueryFlag, [this]() { - std::vector<DisplayCapability> tmpCapabilities; - auto error = - static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities)); - if (error == Error::NONE) { - std::scoped_lock lock(mDisplayCapabilitiesMutex); - mDisplayCapabilities.emplace(); - for (auto capability : tmpCapabilities) { - mDisplayCapabilities->emplace(capability); - } - } else if (error == Error::UNSUPPORTED) { - std::scoped_lock lock(mDisplayCapabilitiesMutex); - mDisplayCapabilities.emplace(); - if (mCapabilities.count(AidlCapability::SKIP_CLIENT_COLOR_TRANSFORM)) { - mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); - } - bool dozeSupport = false; - error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport)); - if (error == Error::NONE && dozeSupport) { - mDisplayCapabilities->emplace(DisplayCapability::DOZE); - } - } - }); + loadDisplayCapabilities(); } return static_cast<Error>(intError); @@ -653,6 +634,32 @@ std::shared_ptr<HWC2::Layer> Display::getLayerById(HWLayerId id) const { auto it = mLayers.find(id); return it != mLayers.end() ? it->second.lock() : nullptr; } + +void Display::loadDisplayCapabilities() { + std::call_once(mDisplayCapabilityQueryFlag, [this]() { + std::vector<DisplayCapability> tmpCapabilities; + auto error = + static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities)); + if (error == Error::NONE) { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + mDisplayCapabilities.emplace(); + for (auto capability : tmpCapabilities) { + mDisplayCapabilities->emplace(capability); + } + } else if (error == Error::UNSUPPORTED) { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + mDisplayCapabilities.emplace(); + if (mCapabilities.count(AidlCapability::SKIP_CLIENT_COLOR_TRANSFORM)) { + mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); + } + bool dozeSupport = false; + error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport)); + if (error == Error::NONE && dozeSupport) { + mDisplayCapabilities->emplace(DisplayCapability::DOZE); + } + } + }); +} } // namespace impl // Layer methods diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index de044e0b76..5b948318ae 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -278,6 +278,7 @@ public: hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override; private: + void loadDisplayCapabilities(); // This may fail (and return a null pointer) if no layer with this ID exists // on this display diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index 97bd79fcee..bc15dec493 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -538,7 +538,8 @@ TEST_F(LayerLifecycleManagerTest, alphaChangesAlwaysSetsVisibleRegionFlag) { ftl::Flags<RequestedLayerState::Changes>( RequestedLayerState::Changes::Content | RequestedLayerState::Changes::AffectsChildren | - RequestedLayerState::Changes::VisibleRegion) + RequestedLayerState::Changes::VisibleRegion | + RequestedLayerState::Changes::Input) .string()); EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(startingAlpha)); mLifecycleManager.commitChanges(); @@ -551,7 +552,8 @@ TEST_F(LayerLifecycleManagerTest, alphaChangesAlwaysSetsVisibleRegionFlag) { ftl::Flags<RequestedLayerState::Changes>( RequestedLayerState::Changes::Content | RequestedLayerState::Changes::AffectsChildren | - RequestedLayerState::Changes::VisibleRegion) + RequestedLayerState::Changes::VisibleRegion | + RequestedLayerState::Changes::Input) .string()); EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(endingAlpha)); mLifecycleManager.commitChanges(); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index dedb292b34..8b9ac93310 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1172,6 +1172,16 @@ TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) { gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)); } +TEST_F(LayerSnapshotTest, alphaChangesPropagateToInput) { + Region touch{Rect{0, 0, 1000, 1000}}; + setTouchableRegion(1, touch); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + setAlpha(1, 0.5f); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot(1)->inputInfo.alpha, 0.5f); +} + TEST_F(LayerSnapshotTest, isFrontBuffered) { setBuffer(1, std::make_shared<renderengine::mock::FakeExternalTexture>( |