Merge "Reland "EGL: Refactor multifile blobcache""
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index d03326e..53852d8 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -388,7 +388,8 @@
{
if (isRpcBinder()) {
if (rpcSession()->getMaxIncomingThreads() < 1) {
- ALOGE("Cannot register a DeathRecipient without any incoming connections.");
+ ALOGE("Cannot register a DeathRecipient without any incoming threads. Need to set max "
+ "incoming threads to a value greater than 0 before calling linkToDeath.");
return INVALID_OPERATION;
}
} else if constexpr (!kEnableKernelIpc) {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 985c549..ffe79a3 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -39,6 +39,12 @@
} // Anonymous namespace
+namespace { // Anonymous
+
+constexpr int32_t kSerializedCallbackTypeOnCompelteWithJankData = 2;
+
+} // Anonymous namespace
+
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
if (err != NO_ERROR) return err;
@@ -349,7 +355,11 @@
status_t CallbackId::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt64, id);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+ if (type == Type::ON_COMPLETE && includeJankData) {
+ SAFE_PARCEL(output->writeInt32, kSerializedCallbackTypeOnCompelteWithJankData);
+ } else {
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+ }
return NO_ERROR;
}
@@ -357,7 +367,13 @@
SAFE_PARCEL(input->readInt64, &id);
int32_t typeAsInt;
SAFE_PARCEL(input->readInt32, &typeAsInt);
- type = static_cast<CallbackId::Type>(typeAsInt);
+ if (typeAsInt == kSerializedCallbackTypeOnCompelteWithJankData) {
+ type = Type::ON_COMPLETE;
+ includeJankData = true;
+ } else {
+ type = static_cast<CallbackId::Type>(typeAsInt);
+ includeJankData = false;
+ }
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7fc6788..a345938 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -82,6 +82,8 @@
int64_t generateId() {
return (((int64_t)getpid()) << 32) | ++idCounter;
}
+
+void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {}
} // namespace
ComposerService::ComposerService()
@@ -249,6 +251,14 @@
surfaceControls,
CallbackId::Type callbackType) {
std::lock_guard<std::mutex> lock(mMutex);
+ return addCallbackFunctionLocked(callbackFunction, surfaceControls, callbackType);
+}
+
+CallbackId TransactionCompletedListener::addCallbackFunctionLocked(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls,
+ CallbackId::Type callbackType) {
startListeningLocked();
CallbackId callbackId(getNextIdLocked(), callbackType);
@@ -257,6 +267,11 @@
for (const auto& surfaceControl : surfaceControls) {
callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
+
+ if (callbackType == CallbackId::Type::ON_COMPLETE &&
+ mJankListeners.count(surfaceControl->getLayerId()) != 0) {
+ callbackId.includeJankData = true;
+ }
}
return callbackId;
@@ -305,15 +320,26 @@
}
void TransactionCompletedListener::addSurfaceControlToCallbacks(
- const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
+ SurfaceComposerClient::CallbackInfo& callbackInfo,
+ const sp<SurfaceControl>& surfaceControl) {
std::lock_guard<std::mutex> lock(mMutex);
- for (auto callbackId : callbackIds) {
+ bool includingJankData = false;
+ for (auto callbackId : callbackInfo.callbackIds) {
mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
std::forward_as_tuple(
surfaceControl->getHandle()),
std::forward_as_tuple(surfaceControl));
+ includingJankData = includingJankData || callbackId.includeJankData;
+ }
+
+ // If no registered callback is requesting jank data, but there is a jank listener registered
+ // on the new surface control, add a synthetic callback that requests the jank data.
+ if (!includingJankData && mJankListeners.count(surfaceControl->getLayerId()) != 0) {
+ CallbackId callbackId =
+ addCallbackFunctionLocked(&emptyCallback, callbackInfo.surfaceControls,
+ CallbackId::Type::ON_COMPLETE);
+ callbackInfo.callbackIds.emplace(callbackId);
}
}
@@ -930,8 +956,7 @@
// register all surface controls for all callbackIds for this listener that is merging
for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
TransactionCompletedListener::getInstance()
- ->addSurfaceControlToCallbacks(surfaceControl,
- currentProcessCallbackInfo.callbackIds);
+ ->addSurfaceControlToCallbacks(currentProcessCallbackInfo, surfaceControl);
}
}
@@ -1267,8 +1292,7 @@
auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
callbackInfo.surfaceControls.insert(sc);
- TransactionCompletedListener::getInstance()
- ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
+ TransactionCompletedListener::getInstance()->addSurfaceControlToCallbacks(callbackInfo, sc);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index d593f56..39bcb4a 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -40,10 +40,15 @@
class CallbackId : public Parcelable {
public:
int64_t id;
- enum class Type : int32_t { ON_COMPLETE, ON_COMMIT } type;
+ enum class Type : int32_t {
+ ON_COMPLETE = 0,
+ ON_COMMIT = 1,
+ /*reserved for serialization = 2*/
+ } type;
+ bool includeJankData; // Only respected for ON_COMPLETE callbacks.
CallbackId() {}
- CallbackId(int64_t id, Type type) : id(id), type(type) {}
+ CallbackId(int64_t id, Type type) : id(id), type(type), includeJankData(false) {}
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 49196d6..809ea5a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -878,10 +878,14 @@
const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
surfaceControls,
CallbackId::Type callbackType);
+ CallbackId addCallbackFunctionLocked(
+ const TransactionCompletedCallback& callbackFunction,
+ const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+ surfaceControls,
+ CallbackId::Type callbackType) REQUIRES(mMutex);
- void addSurfaceControlToCallbacks(
- const sp<SurfaceControl>& surfaceControl,
- const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
+ void addSurfaceControlToCallbacks(SurfaceComposerClient::CallbackInfo& callbackInfo,
+ const sp<SurfaceControl>& surfaceControl);
void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
void removeQueueStallListener(void *id);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 079b80d..d14a781 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4185,6 +4185,17 @@
bool needWake = false;
{ // acquire lock
mLock.lock();
+ if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
+ // Set the flag anyway if we already have an ongoing gesture. That would allow us to
+ // complete the processing of the current stroke.
+ const auto touchStateIt = mTouchStatesByDisplay.find(args->displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end()) {
+ const TouchState& touchState = touchStateIt->second;
+ if (touchState.deviceId == args->deviceId && touchState.isDown()) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
+ }
+ }
if (shouldSendMotionToInputFilterLocked(args)) {
ui::Transform displayTransform;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 6d63991..4258471 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -33,8 +33,7 @@
void TouchState::removeTouchedPointer(int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
- touchedWindow.pointerIds.reset(pointerId);
- touchedWindow.pilferedPointerIds.reset(pointerId);
+ touchedWindow.removeTouchingPointer(pointerId);
}
}
@@ -42,8 +41,7 @@
int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.pointerIds.reset(pointerId);
- touchedWindow.pilferedPointerIds.reset(pointerId);
+ touchedWindow.removeTouchingPointer(pointerId);
return;
}
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 92f62b5..99c4769 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -50,6 +50,14 @@
it->second.set(pointerId);
}
+void TouchedWindow::removeTouchingPointer(int32_t pointerId) {
+ pointerIds.reset(pointerId);
+ pilferedPointerIds.reset(pointerId);
+ if (pointerIds.none()) {
+ firstDownTimeInTarget.reset();
+ }
+}
+
void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
const auto it = mHoveringPointerIdsByDevice.find(deviceId);
if (it == mHoveringPointerIdsByDevice.end()) {
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index e59e781..aa2e9dd 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -43,6 +43,7 @@
bool hasHoveringPointer(int32_t deviceId, int32_t pointerId) const;
void addHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
+ void removeTouchingPointer(int32_t pointerId);
void clearHoveringPointers();
std::string dump() const;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b1b6e05..3605be2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1567,6 +1567,113 @@
std::vector<PointerBuilder> mPointers;
};
+class MotionArgsBuilder {
+public:
+ MotionArgsBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDownTime = mEventTime;
+ }
+
+ MotionArgsBuilder& deviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ MotionArgsBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ MotionArgsBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionArgsBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionArgsBuilder& policyFlags(int32_t policyFlags) {
+ mPolicyFlags = policyFlags;
+ return *this;
+ }
+
+ MotionArgsBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionArgsBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
+ return *this;
+ }
+
+ MotionArgsBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionArgsBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionArgsBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionArgsBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
+ NotifyMotionArgs build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ mPointers.size() == 1) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ NotifyMotionArgs args(InputEvent::nextId(), mEventTime, /*readTime=*/mEventTime, mDeviceId,
+ mSource, mDisplayId, mPolicyFlags, mAction, mActionButton, mFlags,
+ AMETA_NONE, mButtonState, MotionClassification::NONE, /*edgeFlags=*/0,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data(),
+ /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
+ mRawYCursorPosition, mDownTime, /*videoFrames=*/{});
+
+ return args;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mDeviceId = DEVICE_ID;
+ int32_t mSource;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ int32_t mFlags{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
static InputEventInjectionResult injectMotionEvent(
const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -2055,6 +2162,92 @@
}
/**
+ * The policy typically sets POLICY_FLAG_PASS_TO_USER to the events. But when the display is not
+ * interactive, it might stop sending this flag.
+ * In this test, we check that if the policy stops sending this flag mid-gesture, we still ensure
+ * to have a consistent input stream.
+ *
+ * Test procedure:
+ * DOWN -> POINTER_DOWN -> (stop sending POLICY_FLAG_PASS_TO_USER) -> CANCEL.
+ * DOWN (new gesture).
+ *
+ * In the bad implementation, we could potentially drop the CANCEL event, and get an inconsistent
+ * state in the dispatcher. This would cause the final DOWN event to not be delivered to the app.
+ *
+ * We technically just need a single window here, but we are using two windows (spy on top and a
+ * regular window below) to emulate the actual situation where it happens on the device.
+ */
+TEST_F(InputDispatcherTest, TwoPointerCancelInconsistentPolicy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+ const int32_t touchDeviceId = 4;
+ NotifyMotionArgs args;
+
+ // Two pointers down
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(120).y(120))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ spyWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ // Cancel the current gesture. Send the cancel without the default policy flags.
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .policyFlags(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(120).y(120))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL));
+
+ // We don't need to reset the device to reproduce the issue, but the reset event typically
+ // follows, so we keep it here to model the actual listener behaviour more closely.
+ NotifyDeviceResetArgs resetArgs;
+ resetArgs.id = 1; // arbitrary id
+ resetArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ resetArgs.deviceId = touchDeviceId;
+ mDispatcher->notifyDeviceReset(&resetArgs);
+
+ // Start new gesture
+ mDispatcher->notifyMotion(&(
+ args = MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .policyFlags(DEFAULT_POLICY_FLAGS)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
+ .build()));
+ spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ // No more events
+ spyWindow->assertNoEvents();
+ window->assertNoEvents();
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is hovered from the right window into the left window.
* Next, we tap on the left window, where the cursor was last seen.
@@ -2172,6 +2365,102 @@
}
/**
+ * This test is similar to the test above, but the sequence of injected events is different.
+ *
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is hovered over the left window.
+ * Next, we tap on the left window, where the cursor was last seen.
+ *
+ * After that, we inject one finger down onto the right window, and then a second finger down onto
+ * the left window.
+ * The touch is split, so this last gesture should cause 2 ACTION_DOWN events, one in the right
+ * window (first), and then another on the left window (second).
+ * This test reproduces a crash where there is a mismatch between the downTime and eventTime.
+ * In the buggy implementation, second finger down on the left window would cause a crash.
+ */
+TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}});
+
+ const int32_t mouseDeviceId = 6;
+ const int32_t touchDeviceId = 4;
+ // Hover over the left window. Keep the cursor there.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+
+ // Tap on left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .build()));
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
+
+ // First finger down on right window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(300)
+ .y(100))
+ .build()));
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ // Second finger down on the left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(300)
+ .y(100))
+ .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .build()));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+
+ // No more events
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
* Make sure that the window receives the second pointer, and first pointer is simply ignored.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 31ee91e..66c2fb6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -734,6 +734,40 @@
return (p != nullptr) ? p->isSecure() : false;
}
+void Layer::transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
+ std::vector<JankData>& jankData) {
+ if (mPendingJankClassifications.empty() ||
+ !mPendingJankClassifications.front()->getJankType()) {
+ return;
+ }
+
+ bool includeJankData = false;
+ for (const auto& handle : handles) {
+ for (const auto& cb : handle->callbackIds) {
+ if (cb.includeJankData) {
+ includeJankData = true;
+ break;
+ }
+ }
+
+ if (includeJankData) {
+ jankData.reserve(mPendingJankClassifications.size());
+ break;
+ }
+ }
+
+ while (!mPendingJankClassifications.empty() &&
+ mPendingJankClassifications.front()->getJankType()) {
+ if (includeJankData) {
+ std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+ mPendingJankClassifications.front();
+ jankData.emplace_back(
+ JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+ }
+ mPendingJankClassifications.pop_front();
+ }
+}
+
// ----------------------------------------------------------------------------
// transaction
// ----------------------------------------------------------------------------
@@ -2798,16 +2832,7 @@
}
std::vector<JankData> jankData;
- jankData.reserve(mPendingJankClassifications.size());
- while (!mPendingJankClassifications.empty() &&
- mPendingJankClassifications.front()->getJankType()) {
- std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
- mPendingJankClassifications.front();
- mPendingJankClassifications.pop_front();
- jankData.emplace_back(
- JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
- }
-
+ transferAvailableJankData(mDrawingState.callbackHandles, jankData);
mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
jankData);
mDrawingState.callbackHandles = {};
@@ -3106,6 +3131,7 @@
return false;
}
+ std::deque<sp<CallbackHandle>> remainingHandles;
for (const auto& handle : handles) {
// If this transaction set a buffer on this layer, release its previous buffer
handle->releasePreviousBuffer = mReleasePreviousBuffer;
@@ -3120,11 +3146,19 @@
mDrawingState.callbackHandles.push_back(handle);
} else { // If this layer will NOT need to be relatched and presented this frame
- // Notify the transaction completed thread this handle is done
- mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+ // Queue this handle to be notified below.
+ remainingHandles.push_back(handle);
}
}
+ if (!remainingHandles.empty()) {
+ // Notify the transaction completed threads these handles are done. These are only the
+ // handles that were not added to the mDrawingState, which will be notified later.
+ std::vector<JankData> jankData;
+ transferAvailableJankData(remainingHandles, jankData);
+ mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles, jankData);
+ }
+
mReleasePreviousBuffer = false;
mCallbackHandleAcquireTimeOrFence = -1;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3d4f03f..07c01d8 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1064,6 +1064,11 @@
void updateChildrenSnapshots(bool updateGeometry);
+ // Fills the provided vector with the currently available JankData and removes the processed
+ // JankData from the pending list.
+ void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
+ std::vector<JankData>& jankData);
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f232780..68ab776 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4552,8 +4552,10 @@
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
- sp<CallbackHandle>::make(listener, callbackIds, s.surface));
+ mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
+ callbackIds,
+ s.surface),
+ std::vector<JankData>());
}
return 0;
}
@@ -4891,8 +4893,10 @@
}
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
- sp<CallbackHandle>::make(listener, callbackIds, s.surface));
+ mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
+ callbackIds,
+ s.surface),
+ std::vector<JankData>());
}
return 0;
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3da98d4..3587a72 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -92,11 +92,6 @@
return NO_ERROR;
}
-status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
- const sp<CallbackHandle>& handle) {
- return addCallbackHandle(handle, std::vector<JankData>());
-}
-
status_t TransactionCallbackInvoker::findOrCreateTransactionStats(
const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 61ff9bc..3074795 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -66,9 +66,6 @@
status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
std::deque<sp<CallbackHandle>>& outRemainingHandles);
- // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
- // presented this frame.
- status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
void addPresentFence(sp<Fence>);