Merge "DO NOT MERGE: SurfaceFlinger: Don't wake for pending transactions." into rvc-dev
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 7f04611..86de394 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -61,6 +61,11 @@
return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
+ inline std::chrono::nanoseconds getDispatchingTimeout(
+ std::chrono::nanoseconds defaultValue) const {
+ return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+ }
+
inline sp<IBinder> getApplicationToken() const {
return mInfo.token;
}
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index c5e56fd..2dac5b6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -222,6 +222,11 @@
return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
+ inline std::chrono::nanoseconds getDispatchingTimeout(
+ std::chrono::nanoseconds defaultValue) const {
+ return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+ }
+
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 6366529..3afbabf 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -33,7 +33,7 @@
DECLARE_META_INTERFACE(SurfaceComposerClient)
// flags for createSurface()
- enum { // (keep in sync with Surface.java)
+ enum { // (keep in sync with SurfaceControl.java)
eHidden = 0x00000004,
eDestroyBackbuffer = 0x00000020,
eSecure = 0x00000080,
@@ -42,6 +42,7 @@
eProtectedByApp = 0x00000800,
eProtectedByDRM = 0x00001000,
eCursorWindow = 0x00002000,
+ eNoColorFill = 0x00004000,
eFXSurfaceBufferQueue = 0x00000000,
eFXSurfaceEffect = 0x00020000,
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 25130e2..36aad2e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -236,7 +236,11 @@
/** Compatibility value for ANativeWindow_setFrameRate. */
enum ANativeWindow_FrameRateCompatibility {
/**
- * There are no inherent restrictions on the frame rate of this window.
+ * There are no inherent restrictions on the frame rate of this window. When
+ * the system selects a frame rate other than what the app requested, the
+ * app will be able to run at the system frame rate without requiring pull
+ * down. This value should be used when displaying game content, UIs, and
+ * anything that isn't video.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
/**
@@ -246,7 +250,7 @@
* to do pull down or use some other technique to adapt to the system's
* frame rate. The user experience is likely to be worse (e.g. more frame
* stuttering) than it would be if the system had chosen the app's requested
- * frame rate.
+ * frame rate. This value should be used for video content.
*/
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
};
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a1eb007..21c8ae1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -102,6 +102,12 @@
releaseInjectionState();
}
+std::string EventEntry::getDescription() const {
+ std::string result;
+ appendDescription(result);
+ return result;
+}
+
void EventEntry::release() {
refCount -= 1;
if (refCount == 0) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ab481bd..a135409 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -83,6 +83,8 @@
virtual void appendDescription(std::string& msg) const = 0;
+ std::string getDescription() const;
+
protected:
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a21d1eb..e6e3347 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -78,7 +78,7 @@
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
-constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
// Amount of time to allow for all pending events to be processed when an app switch
// key is on the way. This is used to preempt input dispatch and drop input events
@@ -1295,11 +1295,9 @@
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- if (DEBUG_FOCUS) {
- ALOGD("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
- }
- nsecs_t timeout;
+ ALOGI("Waiting for application to become ready for input: %s. Reason: %s",
+ getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
+ std::chrono::nanoseconds timeout;
if (windowHandle != nullptr) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != nullptr) {
@@ -1311,7 +1309,7 @@
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout;
+ mInputTargetWaitTimeoutTime = currentTime + timeout.count();
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationToken.clear();
@@ -1353,10 +1351,10 @@
}
void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
- nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
- if (newTimeout > 0) {
+ nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) {
+ if (timeoutExtension > 0) {
// Extend the timeout.
- mInputTargetWaitTimeoutTime = now() + newTimeout;
+ mInputTargetWaitTimeoutTime = now() + timeoutExtension;
} else {
// Give up.
mInputTargetWaitTimeoutExpired = true;
@@ -4048,11 +4046,12 @@
const int32_t displayId = it.first;
const sp<InputApplicationHandle>& applicationHandle = it.second;
dump += StringPrintf(INDENT2 "displayId=%" PRId32
- ", name='%s', dispatchingTimeout=%0.3fms\n",
+ ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
displayId, applicationHandle->getName().c_str(),
- applicationHandle->getDispatchingTimeout(
- DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
- 1000000.0);
+ ns2ms(applicationHandle
+ ->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT)
+ .count()));
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -4132,9 +4131,10 @@
windowInfo->windowXScale, windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
- dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+ "ms\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- windowInfo->dispatchingTimeout / 1000000.0);
+ ns2ms(windowInfo->dispatchingTimeout));
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -4167,7 +4167,7 @@
for (EventEntry* entry : mRecentQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
dump += INDENT "RecentQueue: <empty>\n";
@@ -4178,8 +4178,8 @@
dump += INDENT "PendingEvent:\n";
dump += INDENT2;
mPendingEvent->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n",
- (currentTime - mPendingEvent->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n",
+ ns2ms(currentTime - mPendingEvent->eventTime));
} else {
dump += INDENT "PendingEvent: <none>\n";
}
@@ -4190,7 +4190,7 @@
for (EventEntry* entry : mInboundQueue) {
dump += INDENT2;
entry->appendDescription(dump);
- dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+ dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
dump += INDENT "InboundQueue: <empty>\n";
@@ -4225,9 +4225,10 @@
for (DispatchEntry* entry : connection->outboundQueue) {
dump.append(INDENT4);
entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
+ dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
+ "ms\n",
entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+ ns2ms(currentTime - entry->eventEntry->eventTime));
}
} else {
dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -4240,10 +4241,10 @@
dump += INDENT4;
entry->eventEntry->appendDescription(dump);
dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%0.1fms, wait=%0.1fms\n",
+ "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
entry->targetFlags, entry->resolvedAction,
- (currentTime - entry->eventEntry->eventTime) * 0.000001f,
- (currentTime - entry->deliveryTime) * 0.000001f);
+ ns2ms(currentTime - entry->eventEntry->eventTime),
+ ns2ms(currentTime - entry->deliveryTime));
}
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
@@ -4254,16 +4255,16 @@
}
if (isAppSwitchPendingLocked()) {
- dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
- (mAppSwitchDueTime - now()) / 1000000.0);
+ dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
+ ns2ms(mAppSwitchDueTime - now()));
} else {
dump += INDENT "AppSwitch: not pending\n";
}
dump += INDENT "Configuration:\n";
- dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
- dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
- mConfig.keyRepeatTimeout * 0.000001f);
+ dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
+ dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
+ ns2ms(mConfig.keyRepeatTimeout));
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -4365,8 +4366,7 @@
return BAD_VALUE;
}
- [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
- ALOG_ASSERT(removed);
+ removeConnectionLocked(connection);
mInputChannelsByToken.erase(inputChannel->getConnectionToken());
if (connection->monitor) {
@@ -4468,7 +4468,7 @@
return std::nullopt;
}
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
}
@@ -4483,6 +4483,10 @@
return nullptr;
}
+void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+ removeByValue(mConnectionsByFd, connection);
+}
+
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
const sp<Connection>& connection, uint32_t seq,
bool handled) {
@@ -4587,12 +4591,12 @@
commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
- nsecs_t newTimeout =
+ const nsecs_t timeoutExtension =
mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token);
+ resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token);
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4647,11 +4651,8 @@
const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- std::string msg =
- StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
- connection->getWindowName().c_str(), eventDuration * 0.000001f);
- dispatchEntry->eventEntry->appendDescription(msg);
- ALOGI("%s", msg.c_str());
+ ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+ ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
}
reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 89b5089..ff7be87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -197,6 +197,11 @@
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+ sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+ REQUIRES(mLock);
+
+ void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
return std::hash<IBinder*>{}(b.get());
@@ -209,7 +214,6 @@
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
- sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f33cc65..13e8354 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,12 +17,14 @@
#include "../dispatcher/InputDispatcher.h"
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
#include <cinttypes>
+#include <thread>
#include <unordered_set>
#include <vector>
@@ -119,6 +121,33 @@
<< "Expected onPointerDownOutsideFocus to not have been called";
}
+ // This function must be called soon after the expected ANR timer starts,
+ // because we are also checking how much time has passed.
+ void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
+ const sp<InputApplicationHandle>& expectedApplication,
+ const sp<IBinder>& expectedToken) {
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::unique_lock lock(mLock);
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+
+ // If there is an ANR, Dispatcher won't be idle because there are still events
+ // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+ // before checking if ANR was called.
+ // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
+ // it some time to act. 100ms seems reasonable.
+ mNotifyAnr.wait_for(lock, timeToWait,
+ [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; });
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+ ASSERT_TRUE(mNotifyAnrWasCalled);
+ // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+ // the dispatcher started counting before this function was called
+ ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for
+ mNotifyAnrWasCalled = false;
+ ASSERT_EQ(expectedApplication, mLastAnrApplication);
+ ASSERT_EQ(expectedToken, mLastAnrWindowToken);
+ }
+
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
mConfig.keyRepeatTimeout = timeout;
mConfig.keyRepeatDelay = delay;
@@ -131,14 +160,26 @@
sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
+ // ANR handling
+ bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false;
+ sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock);
+ sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock);
+ std::condition_variable mNotifyAnr;
+ std::chrono::nanoseconds mAnrTimeout = 0ms;
+
virtual void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
- const std::string&) override {
- return 0;
+ virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& windowToken, const std::string&) override {
+ std::scoped_lock lock(mLock);
+ mLastAnrApplication = application;
+ mLastAnrWindowToken = windowToken;
+ mNotifyAnrWasCalled = true;
+ mNotifyAnr.notify_all();
+ return mAnrTimeout.count();
}
virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -309,6 +350,20 @@
mFakePolicy.clear();
mDispatcher.clear();
}
+
+ /**
+ * Used for debugging when writing the test
+ */
+ void dumpDispatcherState() {
+ std::string dump;
+ mDispatcher->dump(dump);
+ std::stringstream ss(dump);
+ std::string to;
+
+ while (std::getline(ss, to, '\n')) {
+ ALOGE("%s", to.c_str());
+ }
+ }
};
@@ -502,13 +557,20 @@
class FakeApplicationHandle : public InputApplicationHandle {
public:
- FakeApplicationHandle() {}
+ FakeApplicationHandle() {
+ mInfo.name = "Fake Application";
+ mInfo.token = new BBinder();
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+ }
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() override {
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
return true;
}
+
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout.count();
+ }
};
class FakeInputReceiver {
@@ -519,6 +581,20 @@
}
InputEvent* consume() {
+ InputEvent* event;
+ std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+ if (!consumeSeq) {
+ return nullptr;
+ }
+ finishEvent(*consumeSeq);
+ return event;
+ }
+
+ /**
+ * Receive an event without acknowledging it.
+ * Return the sequence number that could later be used to send finished signal.
+ */
+ std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
uint32_t consumeSeq;
InputEvent* event;
@@ -535,23 +611,29 @@
if (status == WOULD_BLOCK) {
// Just means there's no event available.
- return nullptr;
+ return std::nullopt;
}
if (status != OK) {
ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
- return nullptr;
+ return std::nullopt;
}
if (event == nullptr) {
ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
- return nullptr;
+ return std::nullopt;
}
+ if (outEvent != nullptr) {
+ *outEvent = event;
+ }
+ return consumeSeq;
+ }
- status = mConsumer->sendFinishedSignal(consumeSeq, true);
- if (status != OK) {
- ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
- }
- return event;
+ /**
+ * To be used together with "receiveEvent" to complete the consumption of an event.
+ */
+ void finishEvent(uint32_t consumeSeq) {
+ const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+ ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
@@ -668,6 +750,10 @@
void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
+ void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout.count();
+ }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
@@ -740,6 +826,19 @@
expectedFlags);
}
+ std::optional<uint32_t> receiveEvent() {
+ if (mInputReceiver == nullptr) {
+ ADD_FAILURE() << "Invalid receive event on window with no receiver";
+ return std::nullopt;
+ }
+ return mInputReceiver->receiveEvent();
+ }
+
+ void finishEvent(uint32_t sequenceNum) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->finishEvent(sequenceNum);
+ }
+
InputEvent* consume() {
if (mInputReceiver == nullptr) {
return nullptr;
@@ -765,16 +864,15 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ int32_t displayId = ADISPLAY_ID_NONE) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key down event.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
- AMETA_NONE,
- /* repeatCount */ 0, currentTime, currentTime);
+ INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+ repeatCount, currentTime, currentTime);
// Inject event until dispatch out.
return dispatcher->injectInputEvent(
@@ -783,10 +881,16 @@
INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action,
- int32_t source, int32_t displayId, int32_t x, int32_t y,
- int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION,
- int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
+}
+
+static int32_t injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position,
+ const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION}) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -796,8 +900,8 @@
pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
@@ -806,7 +910,7 @@
/* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
/* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
- /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+ /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
currentTime, currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -819,14 +923,12 @@
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
int32_t displayId, const PointF& location = {100, 200}) {
- return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x,
- location.y);
+ return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
int32_t displayId, const PointF& location = {100, 200}) {
- return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x,
- location.y);
+ return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
@@ -1051,7 +1153,7 @@
// left window. This event should be dispatched to the left window.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
+ ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
}
@@ -2185,4 +2287,82 @@
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
}
+class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = new FakeApplicationHandle();
+ mApplication->setDispatchingTimeout(20ms);
+ mWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 30, 30));
+ mWindow->setDispatchingTimeout(10ms);
+ mWindow->setFocus(true);
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(true);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+ mWindow.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mWindow;
+ static constexpr PointF WINDOW_LOCATION = {20, 20};
+
+ void tapOnWindow() {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ }
+};
+
+// Send an event to the app and have the app not respond right away.
+// Make sure that ANR is raised
+TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+
+ // Also, overwhelm the socket to make sure ANR starts
+ for (size_t i = 0; i < 100; i++) {
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i});
+ }
+
+ std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(sequenceNum);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// Send a key to the app and have the app not respond right away.
+TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
+ // Inject a key, and don't respond - expect that ANR is called.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(sequenceNum);
+
+ // Start ANR process by sending a 2nd key, which would trigger the check for whether
+ // waitQueue is empty
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1);
+
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken());
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
} // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 45e67f7..8a282e2 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -820,7 +820,7 @@
status_t err(NO_ERROR);
// If the min period or min timeout has changed since the last batch call, call batch.
- if (prevBestBatchParams != info.bestBatchParams) {
+ if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
err = checkReturnAndGetStatus(mSensors->batch(
@@ -890,14 +890,13 @@
Info& info = mActivationCount.editValueAt(i);
if (info.hasBatchParamsForIdent(ident)) {
- if (updateBatchParamsLocked(handle, info) != NO_ERROR) {
- bool enable = info.numActiveClients() == 0 && info.isActive;
- bool disable = info.numActiveClients() > 0 && !info.isActive;
+ updateBatchParamsLocked(handle, info);
+ bool disable = info.numActiveClients() == 0 && info.isActive;
+ bool enable = info.numActiveClients() > 0 && !info.isActive;
- if ((enable || disable) &&
- doActivateHardwareLocked(handle, enable) == NO_ERROR) {
- info.isActive = enable;
- }
+ if ((enable || disable) &&
+ doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+ info.isActive = enable;
}
}
}
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 0dcf8c0..52213cf 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -40,22 +40,12 @@
switch ((SensorTypeV2_1)event->type) {
case SensorTypeV2_1::ACCELEROMETER:
case SensorTypeV2_1::MAGNETIC_FIELD:
- case SensorTypeV2_1::ORIENTATION:
case SensorTypeV2_1::GYROSCOPE:
- case SensorTypeV2_1::GRAVITY:
- case SensorTypeV2_1::LINEAR_ACCELERATION:
case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED:
axes = 3;
break;
- case SensorTypeV2_1::GAME_ROTATION_VECTOR:
- axes = 4;
- break;
- case SensorTypeV2_1::ROTATION_VECTOR:
- case SensorTypeV2_1::GEOMAGNETIC_ROTATION_VECTOR:
- axes = 5;
- break;
case SensorTypeV2_1::DEVICE_ORIENTATION:
case SensorTypeV2_1::LIGHT:
case SensorTypeV2_1::PRESSURE:
@@ -77,11 +67,8 @@
case SensorTypeV2_1::HINGE_ANGLE:
axes = 1;
break;
- case SensorTypeV2_1::POSE_6DOF:
- axes = 15;
- break;
default:
- // No other sensors have data that needs to be rounded.
+ // No other sensors have data that needs to be quantized.
break;
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e5b94e4..07be791 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -31,6 +31,7 @@
#include "SurfaceInterceptor.h"
#include "FrameTracer/FrameTracer.h"
+#include "Scheduler/LayerHistory.h"
#include "TimeStats/TimeStats.h"
namespace android {
@@ -399,7 +400,8 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ LayerHistory::LayerUpdateType::Buffer);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a1ed6d7..4f8fc41 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -283,7 +283,8 @@
desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+ LayerHistory::LayerUpdateType::Buffer);
addFrameEvent(acquireFence, postTime, desiredPresentTime);
return true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 33259a2..9655277 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -119,6 +119,13 @@
mCurrentState.treeHasFrameRateVote = false;
mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+ if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+ // Set an invalid color so there is no color fill.
+ mCurrentState.color.r = -1.0_hf;
+ mCurrentState.color.g = -1.0_hf;
+ mCurrentState.color.b = -1.0_hf;
+ }
+
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -1395,7 +1402,8 @@
}
// Activate the layer in Scheduler's LayerHistory
- mFlinger->mScheduler->recordLayerHistory(this, systemTime());
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+ LayerHistory::LayerUpdateType::SetFrameRate);
mCurrentState.sequence++;
mCurrentState.frameRate = frameRate;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8958d9a..2925109 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -79,7 +79,8 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType /*updateType*/) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index d5bebf6..acd76b0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -52,10 +52,18 @@
// Sets the display size. Client is responsible for synchronization.
virtual void setDisplayArea(uint32_t displayArea) = 0;
+ // Sets whether a config change is pending to be applied
virtual void setConfigChangePending(bool pending) = 0;
+ // Represents which layer activity is recorded
+ enum class LayerUpdateType {
+ Buffer, // a new buffer queued
+ AnimationTX, // a new transaction with eAnimation flag set
+ SetFrameRate, // setFrameRate API was called
+ };
+
// Marks the layer as active, and records the given state to its history.
- virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
+ virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
@@ -83,7 +91,7 @@
void setConfigChangePending(bool /*pending*/) override {}
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
@@ -141,7 +149,7 @@
void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 6570b1a..316000c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -99,7 +99,8 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType updateType) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -107,7 +108,7 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now, mConfigChangePending);
+ info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 25dca39..82da007 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -35,18 +35,24 @@
mLayerVote({defaultVote, 0.0f}) {}
void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
- bool pendingConfigChange) {
+ LayerUpdateType updateType, bool pendingConfigChange) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
-
- FrameTimeData frameTime = {.presetTime = lastPresentTime,
- .queueTime = mLastUpdatedTime,
- .pendingConfigChange = pendingConfigChange};
-
- mFrameTimes.push_back(frameTime);
- if (mFrameTimes.size() > HISTORY_SIZE) {
- mFrameTimes.pop_front();
+ switch (updateType) {
+ case LayerUpdateType::AnimationTX:
+ mLastAnimationTime = std::max(lastPresentTime, now);
+ break;
+ case LayerUpdateType::SetFrameRate:
+ case LayerUpdateType::Buffer:
+ FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingConfigChange = pendingConfigChange};
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+ break;
}
}
@@ -81,6 +87,10 @@
return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}
+bool LayerInfoV2::isAnimating(nsecs_t now) const {
+ return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
// The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
if (mFrameTimes.size() < 2) {
@@ -190,6 +200,11 @@
return {mLayerVote.type, mLayerVote.fps};
}
+ if (isAnimating(now)) {
+ ALOGV("%s is animating", mName.c_str());
+ return {LayerHistory::LayerVoteType::Max, 0};
+ }
+
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
return {LayerHistory::LayerVoteType::Min, 0};
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 5f50d5a..9e6783f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -43,6 +43,8 @@
// Stores history of present times and refresh rates for a layer.
class LayerInfoV2 {
+ using LayerUpdateType = LayerHistory::LayerUpdateType;
+
// Layer is considered frequent if the earliest value in the window of most recent present times
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
@@ -63,7 +65,8 @@
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingConfigChange);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -107,6 +110,7 @@
};
bool isFrequent(nsecs_t now) const;
+ bool isAnimating(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible();
std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
@@ -121,6 +125,8 @@
nsecs_t mLastUpdatedTime = 0;
+ nsecs_t mLastAnimationTime = 0;
+
float mLastReportedRefreshRate = 0.0f;
// Holds information about the layer vote
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7c2af23..e250bbf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -62,7 +62,7 @@
namespace android {
-std::unique_ptr<DispSync> createDispSync() {
+std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
// TODO (140302863) remove this and use the vsync_reactor system.
if (property_get_bool("debug.sf.vsync_reactor", true)) {
// TODO (144707443) tune Predictor tunables.
@@ -90,7 +90,7 @@
static constexpr size_t pendingFenceLimit = 20;
return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
std::move(dispatch), std::move(tracker),
- pendingFenceLimit);
+ pendingFenceLimit, supportKernelTimer);
} else {
return std::make_unique<impl::DispSync>("SchedulerDispSync",
sysprop::running_without_sync_framework(true));
@@ -101,9 +101,9 @@
const scheduler::RefreshRateConfigs& refreshRateConfig,
ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
bool useContentDetection)
- : mPrimaryDispSync(createDispSync()),
+ : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+ mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
mEventControlThread(new impl::EventControlThread(std::move(function))),
- mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(refreshRateConfig),
mUseContentDetection(useContentDetection),
@@ -151,9 +151,9 @@
const scheduler::RefreshRateConfigs& configs,
ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
bool useContentDetection)
- : mPrimaryDispSync(std::move(primaryDispSync)),
+ : mSupportKernelTimer(false),
+ mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
- mSupportKernelTimer(false),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
mUseContentDetection(useContentDetection),
@@ -417,9 +417,10 @@
}
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+ LayerHistory::LayerUpdateType updateType) {
if (mLayerHistory) {
- mLayerHistory->record(layer, presentTime, systemTime());
+ mLayerHistory->record(layer, presentTime, systemTime(), updateType);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 066e9ca..ebee9e3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -39,6 +39,7 @@
namespace android {
using namespace std::chrono_literals;
+using scheduler::LayerHistory;
class DispSync;
class FenceTime;
@@ -118,7 +119,7 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
void setConfigChangePending(bool pending);
// Detects content using layer history, and selects a matching refresh rate.
@@ -207,14 +208,14 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
+ // Whether to use idle timer callbacks that support the kernel timer.
+ const bool mSupportKernelTimer;
+
std::unique_ptr<DispSync> mPrimaryDispSync;
std::unique_ptr<EventControlThread> mEventControlThread;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<scheduler::LayerHistory> mLayerHistory;
-
- // Whether to use idle timer callbacks that support the kernel timer.
- const bool mSupportKernelTimer;
+ std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
@@ -236,7 +237,7 @@
TimerState displayPowerTimer = TimerState::Expired;
std::optional<HwcConfigIndexType> configId;
- scheduler::LayerHistory::Summary contentRequirements;
+ LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 5f0c9ce..16d102c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -56,14 +56,16 @@
};
VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+ std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+ bool supportKernelIdleTimer)
: mClock(std::move(clock)),
mTracker(std::move(tracker)),
mDispatch(std::move(dispatch)),
mPendingLimit(pendingFenceLimit),
mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
? std::make_unique<PredictedVsyncTracer>(*mDispatch)
- : nullptr) {}
+ : nullptr),
+ mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
@@ -249,7 +251,8 @@
ATRACE_INT64("VSR-setPeriod", period);
std::lock_guard lk(mMutex);
mLastHwVsync.reset();
- if (period == getPeriod()) {
+
+ if (!mSupportKernelIdleTimer && period == getPeriod()) {
endPeriodTransition();
} else {
startPeriodTransition(period);
@@ -275,6 +278,11 @@
return false;
}
+ if (mSupportKernelIdleTimer) {
+ // Clear out the Composer-provided period and use the allowance logic below
+ HwcVsyncPeriod = {};
+ }
+
auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
static constexpr int allowancePercent = 10;
static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 31ddf5a..265d89c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -36,7 +36,8 @@
class VSyncReactor : public android::DispSync {
public:
VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
- std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+ std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+ bool supportKernelIdleTimer);
~VSyncReactor();
bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
@@ -89,6 +90,7 @@
GUARDED_BY(mMutex);
const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+ const bool mSupportKernelIdleTimer = false;
};
class SystemClock : public Clock {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 03bbe85..2e22735 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -115,6 +115,7 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
@@ -3405,6 +3406,12 @@
for (const ComposerState& state : states) {
clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
listenerCallbacksWithSurfaces);
+ if ((flags & eAnimation) && state.state.surface) {
+ if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+ mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+ LayerHistory::LayerUpdateType::AnimationTX);
+ }
+ }
}
for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
@@ -3689,7 +3696,7 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eLayerStackChanged) {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 94ebe89..fe2af80 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -22,6 +22,7 @@
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
"DisplayConfigs_test.cpp",
+ "EffectLayer_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
new file mode 100644
index 0000000..3dca391
--- /dev/null
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class EffectLayerTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ASSERT_FALSE(display == nullptr);
+
+ mParentLayer = createColorLayer("Parent layer", Color::RED);
+ asTransaction([&](Transaction& t) {
+ t.setDisplayLayerStack(display, 0);
+ t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+ t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+ });
+ }
+
+ virtual void TearDown() {
+ LayerTransactionTest::TearDown();
+ mParentLayer = 0;
+ }
+
+ sp<SurfaceControl> mParentLayer;
+};
+
+TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Default effect Layer has solid black fill");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK);
+ }
+}
+
+TEST_F(EffectLayerTest, EffectLayerWithNoFill) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Effect layer with nofill option is transparent");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::RED);
+ }
+}
+
+TEST_F(EffectLayerTest, EffectLayerCanSetColor) {
+ sp<SurfaceControl> effectLayer =
+ mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceEffect |
+ ISurfaceComposerClient::eNoColorFill,
+ mParentLayer.get());
+
+ EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+ asTransaction([&](Transaction& t) {
+ t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+ t.setColor(effectLayer,
+ half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
+ Color::GREEN.b / 255.0f});
+ t.show(effectLayer);
+ });
+
+ {
+ SCOPED_TRACE("Effect Layer can set color");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN);
+ }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 71d17a9..cae317b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -96,14 +96,14 @@
// no layers are returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, mTime);
+ history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, mTime);
+ history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(mTime).size());
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
@@ -121,7 +121,7 @@
nsecs_t time = mTime;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -155,7 +155,7 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -166,12 +166,12 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -182,7 +182,7 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -196,10 +196,10 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -209,7 +209,7 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
@@ -228,7 +228,7 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -246,7 +246,7 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index dc705ed..f376b4a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "LayerHistoryTestV2"
+#include <Layer.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -60,6 +61,13 @@
[now](const auto& pair) { return pair.second->isFrequent(now); });
}
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+ [now](const auto& pair) { return pair.second->isAnimating(now); });
+ }
+
void setLayerInfoVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
for (auto& [weak, info] : history().mLayerInfos) {
@@ -109,7 +117,7 @@
// Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -117,7 +125,7 @@
// Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -134,7 +142,7 @@
nsecs_t time = systemTime();
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size());
// Layer is still considered inactive so we expect to get Min
@@ -158,7 +166,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -181,7 +189,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -208,7 +216,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -236,7 +244,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -264,7 +272,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -296,7 +304,7 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -340,7 +348,7 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -351,12 +359,12 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time);
+ history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
@@ -368,7 +376,7 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -383,10 +391,10 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -398,7 +406,7 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
@@ -422,7 +430,7 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time);
+ history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -441,7 +449,7 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, time);
+ history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -470,7 +478,7 @@
// the very first updates makes the layer frequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -481,7 +489,7 @@
}
// the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -495,7 +503,7 @@
// Now event if we post a quick few frame we should stay infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -506,7 +514,7 @@
}
// More quick frames will get us to frequent again
- history().record(layer.get(), time, time);
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -533,8 +541,9 @@
nsecs_t time = systemTime();
// Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer.get(), time, time);
- history().record(explicitInvisiblelayer.get(), time, time);
+ history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, history().summarize(time).size());
@@ -545,6 +554,52 @@
EXPECT_EQ(2, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // an update as animation will immediately vote for Max
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
class LayerHistoryTestV2Parameterized
: public LayerHistoryTestV2,
public testing::WithParamInterface<std::chrono::nanoseconds> {};
@@ -565,8 +620,10 @@
const nsecs_t startTime = systemTime();
const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer.get(), startTime, startTime);
- history().record(infrequentLayer.get(), startTime, startTime);
+ history().record(heuristicLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
nsecs_t time = startTime;
nsecs_t lastInfrequentUpdate = startTime;
@@ -574,13 +631,14 @@
int infrequentLayerUpdates = 0;
while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
time += heuristicUpdateDelta.count();
- history().record(heuristicLayer.get(), time, time);
+ history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
totalInfrequentLayerUpdates);
lastInfrequentUpdate = time;
- history().record(infrequentLayer.get(), time, time);
+ history().record(infrequentLayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
infrequentLayerUpdates++;
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 3f14d65..ccbd17f 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -157,7 +157,8 @@
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+ std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
+ false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
@@ -663,6 +664,28 @@
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
+TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
+ // Create a reactor which supports the kernel idle timer
+ auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
+ std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+ std::make_unique<VSyncTrackerWrapper>(mMockTracker),
+ kPendingLimit, true /* supportKernelIdleTimer */);
+
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+ idleReactor.setIgnorePresentFences(true);
+
+ nsecs_t const newPeriod = 5000;
+ idleReactor.setPeriod(newPeriod);
+
+ EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(idleReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);