Merge "Ensure injected touches poke user activity" into udc-qpr-dev
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index eaf3b5e..d50c5f8 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
+#include <stdint.h>
+
__BEGIN_DECLS
/**
@@ -27,7 +29,7 @@
/**
* Hints for the session used to signal upcoming changes in the mode or workload.
*/
-enum SessionHint {
+enum SessionHint: int32_t {
/**
* This hint indicates a sudden increase in CPU workload intensity. It means
* that this hint session needs extra CPU resources immediately to meet the
@@ -61,7 +63,7 @@
* @return 0 on success
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(void* session, int hint);
+int APerformanceHint_sendHint(void* session, SessionHint hint);
/**
* Return the list of thread ids, this API should only be used for testing only.
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d72f65e..b526a6c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -59,15 +59,13 @@
virtual ~BpSurfaceComposer();
- status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& state, const Vector<DisplayState>& displays,
- uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands commands, int64_t desiredPresentTime,
- bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) override {
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
+ const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+ const std::vector<uint64_t>& mergedTransactionIds) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -103,6 +101,11 @@
SAFE_PARCEL(data.writeUint64, transactionId);
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size()));
+ for (auto mergedTransactionId : mergedTransactionIds) {
+ SAFE_PARCEL(data.writeUint64, mergedTransactionId);
+ }
+
if (flags & ISurfaceComposer::eOneWay) {
return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
data, &reply, IBinder::FLAG_ONEWAY);
@@ -187,10 +190,16 @@
uint64_t transactionId = -1;
SAFE_PARCEL(data.readUint64, &transactionId);
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ std::vector<uint64_t> mergedTransactions(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(data.readUint64, &mergedTransactions[i]);
+ }
+
return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
std::move(inputWindowCommands), desiredPresentTime,
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId);
+ listenerCallbacks, transactionId, mergedTransactions);
}
default: {
return BBinder::onTransact(code, data, reply, flags);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c6c7367..0fda358 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -827,6 +827,15 @@
SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
}
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ std::vector<uint64_t> mergedTransactionIds(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]);
+ }
+
// Parsing was successful. Update the object.
mId = transactionId;
mTransactionNestCount = transactionNestCount;
@@ -842,6 +851,7 @@
mInputWindowCommands = inputWindowCommands;
mApplyToken = applyToken;
mUncacheBuffers = std::move(uncacheBuffers);
+ mMergedTransactionIds = std::move(mergedTransactionIds);
return NO_ERROR;
}
@@ -900,6 +910,11 @@
SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
}
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+ for (auto mergedTransactionId : mMergedTransactionIds) {
+ SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+ }
+
return NO_ERROR;
}
@@ -924,6 +939,22 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
+ while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+ MAX_MERGE_HISTORY_LENGTH - 1 &&
+ mMergedTransactionIds.size() > 0) {
+ mMergedTransactionIds.pop_back();
+ }
+ if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end() - 1);
+ } else if (other.mMergedTransactionIds.size() > 0u) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end());
+ }
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
for (auto const& [handle, composerState] : other.mComposerStates) {
if (mComposerStates.count(handle) == 0) {
mComposerStates[handle] = composerState;
@@ -998,12 +1029,17 @@
mIsAutoTimestamp = true;
clearFrameTimelineInfo(mFrameTimelineInfo);
mApplyToken = nullptr;
+ mMergedTransactionIds.clear();
}
uint64_t SurfaceComposerClient::Transaction::getId() {
return mId;
}
+std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
+ return mMergedTransactionIds;
+}
+
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -1014,7 +1050,7 @@
status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
- true, {uncacheBuffer}, false, {}, generateId());
+ true, {uncacheBuffer}, false, {}, generateId(), {});
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -1189,7 +1225,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
- mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId);
+ mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
+ mMergedTransactionIds);
mId = generateId();
// Clear the current states and flags
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index bd21851..7c150d5 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,7 +116,7 @@
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) = 0;
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8d2cdaf..fb57f63 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -419,6 +419,11 @@
mListenerCallbacks;
std::vector<client_cache_t> mUncacheBuffers;
+ // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+ // Ordered most recently merged to least recently merged.
+ static const size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+ std::vector<uint64_t> mMergedTransactionIds;
+
uint64_t mId;
uint32_t mTransactionNestCount = 0;
@@ -482,6 +487,8 @@
// The id is updated every time the transaction is applied.
uint64_t getId();
+ std::vector<uint64_t> getMergedTransactionIds();
+
status_t apply(bool synchronous = false, bool oneWay = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5bc6904..096a43c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -695,16 +695,14 @@
mSupportsPresent = supportsPresent;
}
- status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
- Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
- const sp<IBinder>& /*applyToken*/,
- InputWindowCommands /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const std::vector<client_cache_t>& /*cachedBuffer*/,
- bool /*hasListenerCallbacks*/,
- const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
- uint64_t /*transactionId*/) override {
+ status_t setTransactionState(
+ const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+ const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/,
+ const std::vector<uint64_t>& /*mergedTransactionIds*/) override {
return NO_ERROR;
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0eb4ad2..0354164 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1072,16 +1072,8 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->keyMap.haveKeyLayout()) {
for (size_t codeIndex = 0; codeIndex < keyCodes.size(); codeIndex++) {
- std::vector<int32_t> scanCodes =
- device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]);
-
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (const int32_t scanCode : scanCodes) {
- if (device->keyBitmask.test(scanCode)) {
- outFlags[codeIndex] = 1;
- break;
- }
+ if (device->hasKeycodeLocked(keyCodes[codeIndex])) {
+ outFlags[codeIndex] = 1;
}
}
return true;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 3f6d557..c8c5115 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -503,9 +503,9 @@
classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
- mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH)) {
- mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
}
// Joystick-like devices.
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..f300ee1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,6 +27,8 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
+ explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
~MultiTouchInputMapper() override;
@@ -39,8 +41,6 @@
bool hasStylus() const override;
private:
- explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
// simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
// mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
// It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..dac53cf 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,6 +27,8 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
+ explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
~SingleTouchInputMapper() override;
@@ -40,8 +42,6 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
- explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index fd2be5f..7eca6fa 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -132,7 +132,7 @@
case kGestureTypeScroll:
return handleScroll(when, readTime, gesture);
case kGestureTypeFling:
- return {handleFling(when, readTime, gesture)};
+ return handleFling(when, readTime, gesture);
case kGestureTypeSwipe:
return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
gesture.details.swipe.dy);
@@ -149,7 +149,8 @@
}
}
-NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
rotateDelta(mOrientation, &deltaX, &deltaY);
@@ -312,19 +313,39 @@
return out;
}
-NotifyArgs GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
- // We don't actually want to use the gestures library's fling velocity values (to ensure
- // consistency between touchscreen and touchpad flings), so we're just using the "start fling"
- // gestures as a marker for the end of a two-finger scroll gesture.
- if (gesture.details.fling.fling_state != GESTURES_FLING_START ||
- mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
- return {};
+std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ switch (gesture.details.fling.fling_state) {
+ case GESTURES_FLING_START:
+ if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
+ // We don't actually want to use the gestures library's fling velocity values (to
+ // ensure consistency between touchscreen and touchpad flings), so we're just using
+ // the "start fling" gestures as a marker for the end of a two-finger scroll
+ // gesture.
+ return {endScroll(when, readTime)};
+ }
+ break;
+ case GESTURES_FLING_TAP_DOWN:
+ if (mCurrentClassification == MotionClassification::NONE) {
+ // Use the tap down state of a fling gesture as an indicator that a contact
+ // has been initiated with the touchpad. We treat this as a move event with zero
+ // 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.
+ return {handleMove(when, readTime,
+ Gesture(kGestureMove, gesture.start_time, gesture.end_time,
+ /*dx=*/0.f,
+ /*dy=*/0.f))};
+ }
+ break;
+ default:
+ break;
}
- return endScroll(when, readTime);
+ return {};
}
-NotifyArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
+NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
@@ -507,14 +528,29 @@
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords,
float xCursorPosition, float yCursorPosition) {
- return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
- mPointerController->getDisplayId(), /* policyFlags= */ POLICY_FLAG_WAKE,
- action, /* actionButton= */ actionButton, /* flags= */ 0,
- mReaderContext.getGlobalMetaState(), buttonState,
- mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties, pointerCoords, /* xPrecision= */ 1.0f,
- /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition,
- /* downTime= */ mDownTime, /* videoFrames= */ {});
+ return {mReaderContext.getNextId(),
+ when,
+ readTime,
+ mDeviceId,
+ SOURCE,
+ mPointerController->getDisplayId(),
+ /* policyFlags= */ POLICY_FLAG_WAKE,
+ action,
+ /* actionButton= */ actionButton,
+ /* flags= */ 0,
+ mReaderContext.getGlobalMetaState(),
+ buttonState,
+ mCurrentClassification,
+ AMOTION_EVENT_EDGE_FLAG_NONE,
+ pointerCount,
+ pointerProperties,
+ pointerCoords,
+ /* xPrecision= */ 1.0f,
+ /* yPrecision= */ 1.0f,
+ xCursorPosition,
+ yCursorPosition,
+ /* downTime= */ mDownTime,
+ /* videoFrames= */ {}};
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 70e8fb7..b613b88 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -52,14 +52,16 @@
const Gesture& gesture);
private:
- [[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] NotifyMotionArgs handleMove(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
- [[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
- [[nodiscard]] NotifyArgs endScroll(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+ [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
uint32_t fingerCount, float dx,
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index c6d541e..a723636 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -888,4 +888,21 @@
WithToolType(ToolType::FINGER)));
}
+TEST_F(GestureConverterTest, FlingTapDown) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture);
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
+ ASSERT_TRUE(mFakePointerController->isPointerShown());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 655931c..74be843 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1564,8 +1564,7 @@
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
- mPointers.size() == 1) {
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
mRawXCursorPosition = pointerCoords[0].getX();
mRawYCursorPosition = pointerCoords[0].getY();
}
@@ -1677,8 +1676,7 @@
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
- mPointers.size() == 1) {
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
mRawXCursorPosition = pointerCoords[0].getX();
mRawYCursorPosition = pointerCoords[0].getY();
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index af9acf3..281b0ae 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -612,6 +612,15 @@
preferredExpectedPresentationTime + multiplier * frameInterval;
if (expectedPresentationTime >= preferredExpectedPresentationTime +
scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+ if (currentIndex == 0) {
+ ALOGW("%s: Expected present time is too far in the future but no timelines are "
+ "valid. preferred EPT=%" PRId64 ", Calculated EPT=%" PRId64
+ ", multiplier=%" PRId64 ", frameInterval=%" PRId64 ", threshold=%" PRId64,
+ __func__, preferredExpectedPresentationTime, expectedPresentationTime,
+ multiplier, frameInterval,
+ static_cast<int64_t>(
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+ }
break;
}
@@ -625,6 +634,20 @@
.expectedPresentationTime = expectedPresentationTime};
currentIndex++;
}
+
+ if (currentIndex == 0) {
+ ALOGW("%s: No timelines are valid. preferred EPT=%" PRId64 ", frameInterval=%" PRId64
+ ", threshold=%" PRId64,
+ __func__, preferredExpectedPresentationTime, frameInterval,
+ static_cast<int64_t>(scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+ outVsyncEventData.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(timestamp, preferredDeadlineTimestamp,
+ preferredExpectedPresentationTime),
+ .deadlineTimestamp = preferredDeadlineTimestamp,
+ .expectedPresentationTime = preferredExpectedPresentationTime};
+ currentIndex++;
+ }
+
outVsyncEventData.frameTimelinesLength = currentIndex;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e841954..06d6ef0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4405,7 +4405,8 @@
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+ const std::vector<uint64_t>& mergedTransactionIds) {
ATRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
@@ -4494,7 +4495,8 @@
listenerCallbacks,
originPid,
originUid,
- transactionId};
+ transactionId,
+ mergedTransactionIds};
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
@@ -8090,7 +8092,7 @@
});
}
if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
- mDrawingState.traverseInZOrder([&refreshArgs, cursorOnly, &layers](Layer* layer) {
+ auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) {
if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
if (cursorOnly &&
layer->getLayerSnapshot()->compositionType !=
@@ -8101,7 +8103,22 @@
refreshArgs.layers.push_back(layerFE);
layers.emplace_back(layer, layerFE.get());
}
- });
+ };
+
+ if (cursorOnly || !mVisibleRegionsDirty) {
+ // for hot path avoid traversals by walking though the previous composition list
+ for (sp<Layer> layer : mPreviouslyComposedLayers) {
+ moveSnapshots(layer.get());
+ }
+ } else {
+ mPreviouslyComposedLayers.clear();
+ mDrawingState.traverseInZOrder(
+ [&moveSnapshots](Layer* layer) { moveSnapshots(layer); });
+ mPreviouslyComposedLayers.reserve(layers.size());
+ for (auto [layer, _] : layers) {
+ mPreviouslyComposedLayers.push_back(sp<Layer>::fromExisting(layer));
+ }
+ }
}
return layers;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e886fb..5a6f22c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -515,15 +515,13 @@
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
- status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& state, const Vector<DisplayState>& displays,
- uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) override;
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
void bootFinished();
virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
@@ -1184,6 +1182,11 @@
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
+ // Sorted list of layers that were composed during previous frame. This is used to
+ // avoid an expensive traversal of the layer hierarchy when there are no
+ // visible region changes. Because this is a list of strong pointers, this will
+ // extend the life of the layer but this list is only updated in the main thread.
+ std::vector<sp<Layer>> mPreviouslyComposedLayers;
BootStage mBootStage = BootStage::BOOTLOADER;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 19334e3..dafdc8a 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -69,6 +69,13 @@
for (auto& displayState : t.displays) {
proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
}
+
+ proto.mutable_merged_transaction_ids()->Reserve(
+ static_cast<int32_t>(t.mergedTransactionIds.size()));
+ for (auto& mergedTransactionId : t.mergedTransactionIds) {
+ proto.mutable_merged_transaction_ids()->Add(mergedTransactionId);
+ }
+
return proto;
}
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 62a7dfd..7132a59 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -56,7 +56,8 @@
int64_t desiredPresentTime, bool isAutoTimestamp,
std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId)
+ int originPid, int originUid, uint64_t transactionId,
+ std::vector<uint64_t> mergedTransactionIds)
: frameTimelineInfo(frameTimelineInfo),
states(std::move(composerStates)),
displays(displayStates),
@@ -71,7 +72,8 @@
listenerCallbacks(listenerCallbacks),
originPid(originPid),
originUid(originUid),
- id(transactionId) {}
+ id(transactionId),
+ mergedTransactionIds(std::move(mergedTransactionIds)) {}
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
@@ -131,6 +133,7 @@
int originUid;
uint64_t id;
bool sentFenceTimeoutWarning = false;
+ std::vector<uint64_t> mergedTransactionIds;
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index ce4d18f..80943b5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -185,11 +185,12 @@
bool hasListenerCallbacks = mFdp.ConsumeBool();
std::vector<ListenerCallbacks> listenerCallbacks{};
uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>();
+ std::vector<uint64_t> mergedTransactionIds{};
mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken,
InputWindowCommands{}, desiredPresentTime, isAutoTimestamp,
- {}, hasListenerCallbacks, listenerCallbacks,
- transactionId);
+ {}, hasListenerCallbacks, listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
void SurfaceFlingerFuzzer::setDisplayStateLocked() {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 5c152b5..42b3879 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -737,19 +737,18 @@
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
- auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& states, const Vector<DisplayState>& displays,
- uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks,
- std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) {
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId);
+ listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
auto flushTransactionQueues() {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 2c4eb10..b0cee9b 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -100,6 +100,7 @@
uint64 transaction_id = 6;
repeated LayerState layer_changes = 7;
repeated DisplayState display_changes = 8;
+ repeated uint64 merged_transaction_ids = 9;
}
// Keep insync with layer_state_t
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6ca21bd..e8a9cfe 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -680,6 +680,9 @@
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
test->mFlinger.setLayerSidebandStream(layer, stream);
+ auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+ layerDrawingState.crop =
+ Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
}
static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -814,6 +817,7 @@
Mock::VerifyAndClear(test->mComposer);
test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+ test->mFlinger.mutableVisibleRegionsDirty() = true;
}
static void cleanupInjectedLayers(CompositionTest* test) {
@@ -822,6 +826,7 @@
test->mDisplay->getCompositionDisplay()->clearOutputLayers();
test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+ test->mFlinger.mutablePreviouslyComposedLayers().clear();
// Layer should be unregistered with scheduler.
test->mFlinger.commit();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a8e2ff..833984f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -483,19 +483,18 @@
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
- auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& states, const Vector<DisplayState>& displays,
- uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks,
- std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) {
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId);
+ listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
auto setTransactionStateInternal(TransactionState& transaction) {
@@ -605,6 +604,7 @@
auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
+ auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
auto& mutableTexturePool() { return mFlinger->mTexturePool; }
@@ -616,6 +616,7 @@
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
+ auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; }
auto& mutableActiveDisplayRotationFlags() {
return SurfaceFlinger::sActiveDisplayRotationFlags;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 6a641b3..afb8efb 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -73,6 +73,7 @@
FrameTimelineInfo frameTimelineInfo;
std::vector<client_cache_t> uncacheBuffers;
uint64_t id = static_cast<uint64_t>(-1);
+ std::vector<uint64_t> mergedTransactionIds;
static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
@@ -108,7 +109,7 @@
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id);
+ transaction.id, transaction.mergedTransactionIds);
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
// SF to commit the transaction. If this is animation, it should not time out waiting.
@@ -135,7 +136,7 @@
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id);
+ transaction.id, transaction.mergedTransactionIds);
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
@@ -166,7 +167,7 @@
transactionA.applyToken, transactionA.inputWindowCommands,
transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionA.id);
+ transactionA.id, transactionA.mergedTransactionIds);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -181,7 +182,7 @@
transactionB.applyToken, transactionB.inputWindowCommands,
transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionB.id);
+ transactionB.id, transactionB.mergedTransactionIds);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -218,7 +219,8 @@
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id);
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -238,7 +240,8 @@
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id);
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -251,7 +254,8 @@
mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
empty.applyToken, empty.inputWindowCommands,
empty.desiredPresentTime, empty.isAutoTimestamp,
- empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id);
+ empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
+ empty.mergedTransactionIds);
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -388,7 +392,8 @@
transaction.desiredPresentTime,
transaction.isAutoTimestamp, {}, systemTime(),
mHasListenerCallbacks, mCallbacks, getpid(),
- static_cast<int>(getuid()), transaction.id);
+ static_cast<int>(getuid()), transaction.id,
+ transaction.mergedTransactionIds);
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
@@ -1083,4 +1088,137 @@
EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
}
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfDirectMerges) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+ EXPECT_NE(transaction2Id, transaction3Id);
+
+ transaction1.merge(std::move(transaction2));
+ transaction1.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfIndirectMerges) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+ uint64_t transaction4Id = transaction4.getId();
+ EXPECT_NE(transaction2Id, transaction3Id);
+ EXPECT_NE(transaction2Id, transaction4Id);
+ EXPECT_NE(transaction3Id, transaction4Id);
+
+ transaction4.merge(std::move(transaction2));
+ transaction4.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction4.getMergedTransactionIds().size(), 2u);
+ EXPECT_EQ(transaction4.getMergedTransactionIds()[0], transaction3Id);
+ EXPECT_EQ(transaction4.getMergedTransactionIds()[1], transaction2Id);
+
+ transaction1.merge(std::move(transaction4));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 3u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction4Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction3Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[2], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCleared) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+
+ transaction1.merge(std::move(transaction2));
+ transaction1.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+
+ transaction1.clear();
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().empty(), true);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCapped) {
+ SurfaceComposerClient::Transaction transaction;
+ std::vector<uint64_t> mergedTransactionIds;
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds.push_back(transactionToMerge.getId());
+ transaction.merge(std::move(transactionToMerge));
+ }
+
+ // Keeps latest 10 merges in order of merge recency
+ EXPECT_EQ(transaction.getMergedTransactionIds().size(), 10u);
+ for (uint i = 0; i < 10u; i++) {
+ EXPECT_EQ(transaction.getMergedTransactionIds()[i],
+ mergedTransactionIds[mergedTransactionIds.size() - 1 - i]);
+ }
+}
+
+TEST(TransactionHandlerTest, KeepsMergesFromMoreRecentMerge) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+ std::vector<uint64_t> mergedTransactionIds1, mergedTransactionIds2, mergedTransactionIds3;
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds1.push_back(transactionToMerge.getId());
+ transaction1.merge(std::move(transactionToMerge));
+ }
+
+ for (uint i = 0; i < 5u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds2.push_back(transactionToMerge.getId());
+ transaction2.merge(std::move(transactionToMerge));
+ }
+
+ transaction1.merge(std::move(transaction2));
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction2Id);
+ for (uint i = 0; i < 5u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1u],
+ mergedTransactionIds2[mergedTransactionIds2.size() - 1 - i]);
+ }
+ for (uint i = 0; i < 4u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 6u],
+ mergedTransactionIds1[mergedTransactionIds1.size() - 1 - i]);
+ }
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds3.push_back(transactionToMerge.getId());
+ transaction3.merge(std::move(transactionToMerge));
+ }
+
+ transaction1.merge(std::move(transaction3));
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+ for (uint i = 0; i < 9u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1],
+ mergedTransactionIds3[mergedTransactionIds3.size() - 1 - i]);
+ }
+}
+
+TEST(TransactionHandlerTest, CanAddTransactionWithFullMergedIds) {
+ SurfaceComposerClient::Transaction transaction1, transaction2;
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ transaction1.merge(std::move(transactionToMerge));
+ }
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+
+ auto transaction1Id = transaction1.getId();
+ transaction2.merge(std::move(transaction1));
+ EXPECT_EQ(transaction2.getMergedTransactionIds().size(), 10u);
+ auto mergedTransactionIds = transaction2.getMergedTransactionIds();
+ EXPECT_TRUE(std::count(mergedTransactionIds.begin(), mergedTransactionIds.end(),
+ transaction1Id) > 0);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 82aac7e..92411a7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -67,8 +67,14 @@
ASSERT_EQ(actualProto.transactions().size(),
static_cast<int32_t>(expectedTransactions.size()));
for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
- EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
- expectedTransactions[i].originPid);
+ const auto expectedTransaction = expectedTransactions[i];
+ const auto protoTransaction = actualProto.transactions(static_cast<int32_t>(i));
+ EXPECT_EQ(protoTransaction.transaction_id(), expectedTransaction.id);
+ EXPECT_EQ(protoTransaction.pid(), expectedTransaction.originPid);
+ for (uint32_t i = 0; i < expectedTransaction.mergedTransactionIds.size(); i++) {
+ EXPECT_EQ(protoTransaction.merged_transaction_ids(static_cast<int32_t>(i)),
+ expectedTransaction.mergedTransactionIds[i]);
+ }
}
}
@@ -92,6 +98,7 @@
TransactionState transaction;
transaction.id = i;
transaction.originPid = static_cast<int32_t>(i);
+ transaction.mergedTransactionIds = std::vector<uint64_t>{i + 100, i + 102};
transactions.emplace_back(transaction);
mTracing.addQueuedTransaction(transaction);
}