Merge "Log fatal if transaction callbacks are called more than once" into main
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index a4d9e77..a590c86 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/native/libs/nativewindow"
+ },
+ {
+ "path": "frameworks/native/services/surfaceflinger"
}
],
"presubmit": [
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index ca00e7d..c2da5ba 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -1,10 +1,10 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -150,10 +150,10 @@
],
"hwasan-postsubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a93a2ea..25e1d21 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -437,7 +437,8 @@
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
@@ -447,6 +448,9 @@
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/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2aaddf5..bacc720 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -225,23 +225,6 @@
mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
}
- if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
- if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
- std::shared_ptr<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- bool shouldBumpGeneration = false;
- for_each_subdevice(
- [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
- if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
- shouldBumpGeneration = true;
- }
- });
- if (shouldBumpGeneration) {
- bumpGeneration();
- }
- }
- }
-
if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ea95f78..08600b2 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1040,6 +1040,16 @@
return mReader->getLedMetaStateLocked();
}
+void InputReader::ContextImpl::setPreventingTouchpadTaps(bool prevent) {
+ // lock is already held by the input loop
+ mReader->mPreventingTouchpadTaps = prevent;
+}
+
+bool InputReader::ContextImpl::isPreventingTouchpadTaps() {
+ // lock is already held by the input loop
+ return mReader->mPreventingTouchpadTaps;
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9112913..01ec7c1 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -155,6 +155,9 @@
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
+ REQUIRES(mLock) override;
+ bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
@@ -185,6 +188,9 @@
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
+ // true if tap-to-click on touchpad currently disabled
+ bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 0beace1..aed7563 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -62,6 +62,9 @@
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
+
+ virtual void setPreventingTouchpadTaps(bool prevent) = 0;
+ virtual bool isPreventingTouchpadTaps() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e03a773..58b29b8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -86,20 +86,26 @@
return ADISPLAY_ID_NONE;
}
+std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
+ if (mKeyboardLayoutInfo) {
+ return mKeyboardLayoutInfo;
+ }
+ std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
+ if (!layoutInfo) {
+ return std::nullopt;
+ }
+ return KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType);
+}
+
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
info.setKeyboardType(mKeyboardType);
info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
- if (mKeyboardLayoutInfo) {
- info.setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
- } else {
- std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
- if (layoutInfo) {
- info.setKeyboardLayoutInfo(
- KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
- }
+ std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
+ if (keyboardLayoutInfo) {
+ info.setKeyboardLayoutInfo(*keyboardLayoutInfo);
}
}
@@ -152,13 +158,31 @@
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+ // Also update keyboard layout overlay as soon as we find the new layout info
+ updateKeyboardLayoutOverlay();
bumpGeneration();
}
}
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUTS)) {
+ if (!getDeviceContext().getDeviceClasses().test(InputDeviceClass::VIRTUAL) &&
+ updateKeyboardLayoutOverlay()) {
+ bumpGeneration();
+ }
+ }
return out;
}
+bool KeyboardInputMapper::updateKeyboardLayoutOverlay() {
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
+ getDeviceContext()
+ .getContext()
+ ->getPolicy()
+ ->getKeyboardLayoutOverlay(getDeviceContext().getDeviceIdentifier(),
+ getKeyboardLayoutInfo());
+ return getDeviceContext().setKeyboardLayoutOverlay(keyboardLayout);
+}
+
void KeyboardInputMapper::configureParameters() {
const PropertyMap& config = getDeviceContext().getConfiguration();
mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
@@ -246,6 +270,7 @@
keyDown.flags = flags;
mKeyDowns.push_back(keyDown);
}
+ onKeyDownProcessed();
} else {
// Remove key down.
if (keyDownIndex) {
@@ -423,4 +448,19 @@
return out;
}
+void KeyboardInputMapper::onKeyDownProcessed() {
+ InputReaderContext& context = *getContext();
+ if (context.isPreventingTouchpadTaps()) {
+ // avoid pinging java service unnecessarily
+ return;
+ }
+ // 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);
+ if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
+ context.fadePointer();
+ context.setPreventingTouchpadTaps(true);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 45fd68b..09808df 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -99,12 +99,15 @@
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
std::optional<size_t> findKeyDownIndex(int32_t scanCode);
+ std::optional<KeyboardLayoutInfo> getKeyboardLayoutInfo() const;
+ bool updateKeyboardLayoutOverlay();
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
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 onKeyDownProcessed();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index e826341..3abf2bd 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -153,6 +153,9 @@
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
+ enableTapToClick();
+ }
rotateDelta(mOrientation, &deltaX, &deltaY);
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -191,6 +194,15 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+
+ if (mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ if (gesture.details.buttons.is_tap) {
+ // return early to prevent this tap
+ return out;
+ }
+ }
+
const uint32_t buttonsPressed = gesture.details.buttons.down;
bool pointerDown = isPointerDown(mButtonState) ||
buttonsPressed &
@@ -337,6 +349,9 @@
// magnitude, which will also result in the pointer icon being updated.
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
// initiated with a touchpad.
+ if (!mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ }
return {handleMove(when, readTime,
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
/*dx=*/0.f,
@@ -545,7 +560,7 @@
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ 0,
+ /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
@@ -561,4 +576,8 @@
/* videoFrames= */ {}};
}
+void GestureConverter::enableTapToClick() {
+ mReaderContext.setPreventingTouchpadTaps(false);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index b613b88..3ea3790 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -78,6 +78,8 @@
const PointerCoords* pointerCoords, float xCursorPosition,
float yCursorPosition);
+ void enableTapToClick();
+
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 38640b3..892be6c 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -63,6 +63,7 @@
"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 3486d0f..78420c0 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -209,6 +209,14 @@
mConfig.stylusPointerIconEnabled = enabled;
}
+void FakeInputReaderPolicy::setIsInputMethodConnectionActive(bool active) {
+ mIsInputMethodConnectionActive = active;
+}
+
+bool FakeInputReaderPolicy::isInputMethodConnectionActive() {
+ return mIsInputMethodConnectionActive;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
@@ -227,7 +235,7 @@
}
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) {
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 85ff01a..e03d28d 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -77,6 +77,8 @@
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;
@@ -84,7 +86,7 @@
int32_t /*deviceId*/) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override;
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
@@ -99,6 +101,7 @@
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/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 482a266..4df0f69 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -983,4 +983,226 @@
ASSERT_TRUE(mFakePointerController->isPointerShown());
}
+TEST_F(GestureConverterTest, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ ASSERT_EQ(5u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+}
+
+TEST_F(GestureConverterTest, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+}
+
+TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index 7f8d556..fef58ec 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -103,12 +103,16 @@
mExternalStylusDevices = devices;
}
+ void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
+ bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
+
private:
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
std::optional<nsecs_t> mRequestedTimeout;
std::vector<InputDeviceInfo> mExternalStylusDevices;
+ bool mPreventingTouchpadTaps{false};
} mFakeContext;
friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d720a90..b6720c5 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -49,6 +49,9 @@
MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override));
MOCK_METHOD(int32_t, getLedMetaState, (), (override));
+
+ MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
+ MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
};
class MockEventHubInterface : public EventHubInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
new file mode 100644
index 0000000..08a5559
--- /dev/null
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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);
+ }
+ }
+
+ void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
+ const bool expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).Times(keyCodes.size());
+ if (expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
+ .Times(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);
+}
+
+/**
+ * Touchpad tap should not be disabled if there is no active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
+ testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
+}
+
+/**
+ * Touchpad tap should be disabled if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
+ 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};
+ testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index e88731a..bdedfdf 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -295,7 +295,8 @@
}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) override {
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> layoutInfo) override {
return nullptr;
}
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
@@ -307,6 +308,7 @@
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+ bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -355,6 +357,9 @@
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+
+ void setPreventingTouchpadTaps(bool prevent) {}
+ bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
};
template <class Fdp>
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 5c00260..16088ca 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -42,17 +42,20 @@
if (count > 0) {
for (size_t i=0 ; i<size_t(count) ; i++) {
- if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
- mAcc = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
- mMag = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
- mGyro = Sensor(list + i);
- }
- if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
- uncalibratedGyro = Sensor(list + i);
+ // Only use non-wakeup sensors
+ if ((list[i].flags & SENSOR_FLAG_WAKE_UP) == 0) {
+ if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+ mAcc = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
+ mMag = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
+ mGyro = Sensor(list + i);
+ }
+ if (list[i].type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
+ uncalibratedGyro = Sensor(list + i);
+ }
}
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index d607c75..9f6141a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -66,7 +66,7 @@
TexturePool(renderengine::RenderEngine& renderEngine)
: mRenderEngine(renderEngine), mEnabled(false) {}
- virtual ~TexturePool() = default;
+ virtual ~TexturePool();
// Sets the display size for the texture pool.
// This will trigger a reallocation for all remaining textures in the pool.
@@ -83,11 +83,10 @@
// be held by the pool. This is useful when the active display changes.
void setEnabled(bool enable);
- void dump(std::string& out) const;
+ void dump(std::string& out) const EXCLUDES(mMutex);
protected:
// Proteted visibility so that they can be used for testing
- const static constexpr size_t kMinPoolSize = 3;
const static constexpr size_t kMaxPoolSize = 4;
struct Entry {
@@ -96,16 +95,20 @@
};
std::deque<Entry> mPool;
+ std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture;
private:
- std::shared_ptr<renderengine::ExternalTexture> genTexture();
+ std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size);
// Returns a previously borrowed texture to the pool.
void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
const sp<Fence>& fence);
- void allocatePool();
- renderengine::RenderEngine& mRenderEngine;
- ui::Size mSize;
+ void genTextureAsyncIfNeeded() REQUIRES(mMutex);
+ void resetPool() REQUIRES(mMutex);
+ renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex);
+ ui::Size mSize GUARDED_BY(mMutex);
bool mEnabled;
+ mutable std::mutex mMutex;
+ mutable std::mutex mRenderEngineMutex;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 54ecb56..10f58ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -25,31 +25,61 @@
namespace android::compositionengine::impl::planner {
-void TexturePool::allocatePool() {
+TexturePool::~TexturePool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
+}
+
+void TexturePool::resetPool() {
+ if (mGenTextureFuture.valid()) {
+ mGenTextureFuture.get();
+ }
mPool.clear();
- if (mEnabled && mSize.isValid()) {
- mPool.resize(kMinPoolSize);
- std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
- return Entry{genTexture(), nullptr};
- });
+ genTextureAsyncIfNeeded();
+}
+
+// Generate a new texture asynchronously so it will not require allocation on the main
+// thread.
+void TexturePool::genTextureAsyncIfNeeded() {
+ if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) {
+ mGenTextureFuture = std::async(
+ std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize);
}
}
void TexturePool::setDisplaySize(ui::Size size) {
+ std::lock_guard lock(mMutex);
if (mSize == size) {
return;
}
mSize = size;
- allocatePool();
+ resetPool();
}
std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
if (mPool.empty()) {
- return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
+ std::lock_guard lock(mMutex);
+ std::shared_ptr<TexturePool::AutoTexture> tex;
+ if (mGenTextureFuture.valid()) {
+ tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr);
+ } else {
+ tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr);
+ }
+ // Speculatively generate a new texture, so that the next call does not need
+ // to wait for allocation.
+ genTextureAsyncIfNeeded();
+ return tex;
}
const auto entry = mPool.front();
mPool.pop_front();
+ if (mPool.empty()) {
+ std::lock_guard lock(mMutex);
+ // Similiarly generate a new texture when lending out the last entry, so that
+ // the next call does not need to wait for allocation.
+ genTextureAsyncIfNeeded();
+ }
return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
}
@@ -60,6 +90,8 @@
return;
}
+ std::lock_guard lock(mMutex);
+
// Or the texture on the floor if the pool is no longer tracking textures of the same size.
if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
@@ -80,13 +112,14 @@
mPool.push_back({std::move(texture), fence});
}
-std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
- LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) {
+ std::lock_guard lock(mRenderEngineMutex);
+ LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size");
return std::make_shared<
renderengine::impl::
ExternalTexture>(sp<GraphicBuffer>::
- make(static_cast<uint32_t>(mSize.getWidth()),
- static_cast<uint32_t>(mSize.getHeight()),
+ make(static_cast<uint32_t>(size.getWidth()),
+ static_cast<uint32_t>(size.getHeight()),
HAL_PIXEL_FORMAT_RGBA_8888, 1U,
static_cast<uint64_t>(
GraphicBuffer::USAGE_HW_RENDER |
@@ -100,13 +133,16 @@
void TexturePool::setEnabled(bool enabled) {
mEnabled = enabled;
- allocatePool();
+
+ std::lock_guard lock(mMutex);
+ resetPool();
}
void TexturePool::dump(std::string& out) const {
+ std::lock_guard lock(mMutex);
base::StringAppendF(&out,
"TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
}
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index 6fc90fe..494a9f4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -32,9 +32,9 @@
public:
TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
- size_t getMinPoolSize() const { return kMinPoolSize; }
size_t getMaxPoolSize() const { return kMaxPoolSize; }
size_t getPoolSize() const { return mPool.size(); }
+ size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); }
};
struct TexturePoolTest : public testing::Test {
@@ -56,16 +56,8 @@
TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
};
-TEST_F(TexturePoolTest, preallocatesMinPool) {
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
-}
-
-TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
- auto texture = mTexturePool.borrowTexture();
- }
-
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+TEST_F(TexturePoolTest, preallocatesZeroSizePool) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
@@ -119,10 +111,10 @@
static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
mTexturePool.setDisplaySize(kDisplaySizeTwo);
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture.reset();
// When the texture is returned to the pool, the pool now destroys it.
- EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
texture = mTexturePool.borrowTexture();
EXPECT_EQ(kDisplaySizeTwo.getWidth(),
@@ -132,14 +124,11 @@
}
TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
-
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
- EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
@@ -148,12 +137,11 @@
}
TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+ for (size_t i = 0; i < 2; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
@@ -162,12 +150,13 @@
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
-TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+TEST_F(TexturePoolTest, genFutureWhenReEnabled) {
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_FALSE(mTexturePool.isGenTextureFutureValid());
mTexturePool.setEnabled(true);
- EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+ EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_TRUE(mTexturePool.isGenTextureFutureValid());
}
} // namespace