Merge "Do not accept blur transaction when disabled" into rvc-dev
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 6100626..2427a07 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -74,36 +74,40 @@
int32_t physicalBottom;
int32_t deviceWidth;
int32_t deviceHeight;
+ bool isActive;
std::string uniqueId;
// The actual (hardware) port that the associated display is connected to.
// Not all viewports will have this specified.
std::optional<uint8_t> physicalPort;
ViewportType type;
- DisplayViewport() :
- displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
- logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
- physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
- deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
- type(ViewportType::VIEWPORT_INTERNAL) {
- }
+ DisplayViewport()
+ : displayId(ADISPLAY_ID_NONE),
+ orientation(DISPLAY_ORIENTATION_0),
+ logicalLeft(0),
+ logicalTop(0),
+ logicalRight(0),
+ logicalBottom(0),
+ physicalLeft(0),
+ physicalTop(0),
+ physicalRight(0),
+ physicalBottom(0),
+ deviceWidth(0),
+ deviceHeight(0),
+ isActive(false),
+ uniqueId(),
+ physicalPort(std::nullopt),
+ type(ViewportType::VIEWPORT_INTERNAL) {}
bool operator==(const DisplayViewport& other) const {
- return displayId == other.displayId
- && orientation == other.orientation
- && logicalLeft == other.logicalLeft
- && logicalTop == other.logicalTop
- && logicalRight == other.logicalRight
- && logicalBottom == other.logicalBottom
- && physicalLeft == other.physicalLeft
- && physicalTop == other.physicalTop
- && physicalRight == other.physicalRight
- && physicalBottom == other.physicalBottom
- && deviceWidth == other.deviceWidth
- && deviceHeight == other.deviceHeight
- && uniqueId == other.uniqueId
- && physicalPort == other.physicalPort
- && type == other.type;
+ return displayId == other.displayId && orientation == other.orientation &&
+ logicalLeft == other.logicalLeft && logicalTop == other.logicalTop &&
+ logicalRight == other.logicalRight && logicalBottom == other.logicalBottom &&
+ physicalLeft == other.physicalLeft && physicalTop == other.physicalTop &&
+ physicalRight == other.physicalRight && physicalBottom == other.physicalBottom &&
+ deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight &&
+ isActive == other.isActive && uniqueId == other.uniqueId &&
+ physicalPort == other.physicalPort && type == other.type;
}
bool operator!=(const DisplayViewport& other) const {
@@ -127,6 +131,7 @@
physicalBottom = height;
deviceWidth = width;
deviceHeight = height;
+ isActive = false;
uniqueId.clear();
physicalPort = std::nullopt;
type = ViewportType::VIEWPORT_INTERNAL;
@@ -134,18 +139,16 @@
std::string toString() const {
return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
- "logicalFrame=[%d, %d, %d, %d], "
- "physicalFrame=[%d, %d, %d, %d], "
- "deviceSize=[%d, %d]",
- viewportTypeToString(type), displayId,
- uniqueId.c_str(),
- physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
- orientation,
- logicalLeft, logicalTop,
- logicalRight, logicalBottom,
- physicalLeft, physicalTop,
- physicalRight, physicalBottom,
- deviceWidth, deviceHeight);
+ "logicalFrame=[%d, %d, %d, %d], "
+ "physicalFrame=[%d, %d, %d, %d], "
+ "deviceSize=[%d, %d], "
+ "isActive=[%d]",
+ viewportTypeToString(type), displayId, uniqueId.c_str(),
+ physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
+ : "<none>",
+ orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
+ physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
+ deviceHeight, isActive);
}
};
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/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index deaa15f..a9f2d73 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -95,6 +95,7 @@
while (sm == nullptr) {
sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
if (sm == nullptr) {
+ ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());
sleep(1);
}
}
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 8d12754..1a7c2c9 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -431,6 +431,12 @@
static_cast<int64_t>(
aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ PlaneLayoutComponentType_RAW =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::RAW)};
+
/*---------------------------------------------------------------------------------------------*/
/**
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 a239723..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
@@ -620,6 +620,33 @@
}
}
+/**
+ * Return true if the events preceding this incoming motion event should be dropped
+ * Return false otherwise (the default behaviour)
+ */
+bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
+ bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+ (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+ if (isPointerDownEvent &&
+ mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
+ mInputTargetWaitApplicationToken != nullptr) {
+ int32_t displayId = motionEntry.displayId;
+ int32_t x = static_cast<int32_t>(
+ motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(
+ motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
+ if (touchedWindowHandle != nullptr &&
+ touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) {
+ // User touched a different application than the one we are waiting on.
+ // Flag the event, and start pruning the input queue.
+ ALOGI("Pruning input queue because user touched a different application");
+ return true;
+ }
+ }
+ return false;
+}
+
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(entry);
@@ -653,32 +680,18 @@
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
- (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
- mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
- mInputTargetWaitApplicationToken != nullptr) {
- int32_t displayId = motionEntry->displayId;
- int32_t x =
- int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y =
- int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
- if (touchedWindowHandle != nullptr &&
- touchedWindowHandle->getApplicationToken() !=
- mInputTargetWaitApplicationToken) {
- // User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- mNextUnblockedEvent = motionEntry;
- needWake = true;
- }
+ if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
+ mNextUnblockedEvent = entry;
+ needWake = true;
}
break;
}
- case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::FOCUS: {
+ LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
+ break;
+ }
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
// nothing to do
break;
}
@@ -980,9 +993,24 @@
}
void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+ if (mPendingEvent != nullptr) {
+ // Move the pending event to the front of the queue. This will give the chance
+ // for the pending event to get dispatched to the newly focused window
+ mInboundQueue.push_front(mPendingEvent);
+ mPendingEvent = nullptr;
+ }
+
FocusEntry* focusEntry =
new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
- enqueueInboundEventLocked(focusEntry);
+
+ // This event should go to the front of the queue, but behind all other focus events
+ // Find the last focus event, and insert right after it
+ std::deque<EventEntry*>::reverse_iterator it =
+ std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+ [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+
+ // Maintain the order of focus events. Insert the entry after all other focus events.
+ mInboundQueue.insert(it.base(), focusEntry);
}
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
@@ -1267,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) {
@@ -1283,7 +1309,7 @@
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout;
+ mInputTargetWaitTimeoutTime = currentTime + timeout.count();
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationToken.clear();
@@ -1325,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;
@@ -2108,15 +2134,12 @@
const sp<InputWindowHandle>& windowHandle) {
if (applicationHandle != nullptr) {
if (windowHandle != nullptr) {
- std::string label(applicationHandle->getName());
- label += " - ";
- label += windowHandle->getName();
- return label;
+ return applicationHandle->getName() + " - " + windowHandle->getName();
} else {
return applicationHandle->getName();
}
} else if (windowHandle != nullptr) {
- return windowHandle->getName();
+ return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName();
} else {
return "<unknown application or window>";
}
@@ -4023,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");
@@ -4107,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";
@@ -4142,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";
@@ -4153,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";
}
@@ -4165,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";
@@ -4200,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";
@@ -4215,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";
@@ -4229,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) {
@@ -4340,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) {
@@ -4443,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;
}
@@ -4458,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) {
@@ -4562,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(
@@ -4620,13 +4649,10 @@
}
DispatchEntry* dispatchEntry = *dispatchEntryIt;
- nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+ 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);
@@ -4644,7 +4670,7 @@
}
// Dequeue the event and start the next cycle.
- // Note that because the lock might have been released, it is possible that the
+ // Because the lock might have been released, it is possible that the
// contents of the wait queue to have been drained, so we need to double-check
// a few things.
dispatchEntryIt = connection->findWaitQueueEntry(seq);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d122282..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);
@@ -345,6 +349,8 @@
bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+ bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle 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/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 648d129..8722952 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -452,6 +452,13 @@
}
void BufferLayerConsumer::onDisconnect() {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
mLayer->onDisconnect();
}
@@ -486,6 +493,13 @@
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index c71a1d9..5e3044f 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -331,8 +331,8 @@
// construction time.
const uint32_t mTexName;
- // The layer for this BufferLayerConsumer
- Layer* mLayer;
+ // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
+ Layer* mLayer GUARDED_BY(mMutex);
wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 5ce2fdc..f680460 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -63,6 +63,12 @@
// Sets the dataspace used for rendering the surface
virtual void setBufferDataspace(ui::Dataspace) = 0;
+ // Sets the pixel format used for rendering the surface.
+ // Changing the pixel format of the buffer will result in buffer
+ // reallocation as well as some reconfiguration of the graphics context,
+ // which are both expensive operations.
+ virtual void setBufferPixelFormat(ui::PixelFormat) = 0;
+
// Configures the protected rendering on the surface
virtual void setProtected(bool useProtected) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 9ca7d2f..7a4f738 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -70,11 +70,13 @@
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
+ using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
virtual bool anyLayersRequireClientComposition() const;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 692d78d..5127a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -49,6 +49,7 @@
const sp<Fence>& getClientTargetAcquireFence() const override;
void setBufferDataspace(ui::Dataspace) override;
+ void setBufferPixelFormat(ui::PixelFormat) override;
void setDisplaySize(const ui::Size&) override;
void setProtected(bool useProtected) override;
status_t beginFrame(bool mustRecompose) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ed4d492..a0cae6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -36,6 +36,7 @@
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD1(setProtected, void(bool));
MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+ MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD2(prepareFrame, void(bool, bool));
MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ab26939..d201104 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -259,6 +259,7 @@
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
+ applyClientTargetRequests(changes->clientTargetProperty);
}
// Determine what type of composition we are doing from the final state
@@ -326,6 +327,16 @@
}
}
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+ if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
+ return;
+ }
+ auto outputState = editState();
+ outputState.dataspace = clientTargetProperty.dataspace;
+ getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
+ getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
+}
+
compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
auto result = impl::Output::presentAndGetFrameFences();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ca5be48..4835aef 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -38,6 +38,7 @@
out.append("\n ");
+ dumpVal(out, "bounds", bounds);
dumpVal(out, "frame", frame);
dumpVal(out, "viewport", viewport);
dumpVal(out, "sourceClip", sourceClip);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 4084653..2773fd3 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -104,6 +104,10 @@
static_cast<android_dataspace>(dataspace));
}
+void RenderSurface::setBufferPixelFormat(ui::PixelFormat pixelFormat) {
+ native_window_set_buffers_format(mNativeWindow.get(), static_cast<int32_t>(pixelFormat));
+}
+
void RenderSurface::setProtected(bool useProtected) {
uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
if (useProtected) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 62977a4..09f37fb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -654,6 +654,7 @@
{{nullptr, hal::Composition::CLIENT}},
hal::DisplayRequest::FLIP_CLIENT_TARGET,
{{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+ {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
};
// Since two calls are made to anyLayersRequireClientComposition with different return
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 0c8aafc..730f297 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -227,6 +227,8 @@
if (destinationClip.isEmpty()) {
destinationClip = displayBounds;
}
+ // Make sure the destination clip is contained in the display bounds
+ destinationClip.intersect(displayBounds, &destinationClip);
uint32_t transformOrientation;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 97eeea2..dbdffec 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1354,6 +1354,12 @@
return error;
}
+Error Composer::getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ return Error::NONE;
+}
+
CommandReader::~CommandReader()
{
resetData();
@@ -1662,10 +1668,22 @@
*state = data.presentOrValidateState;
}
+void CommandReader::takeClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ auto found = mReturnData.find(display);
+
+ // If not found, return the default values.
+ if (found == mReturnData.end()) {
+ outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+ outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+ }
+
+ ReturnData& data = found->second;
+ *outClientTargetProperty = data.clientTargetProperty;
+}
+
} // namespace impl
-
} // namespace Hwc2
-
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index aa43f09..00ef782 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -240,6 +240,8 @@
const std::vector<uint8_t>& value) = 0;
virtual V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+ virtual Error getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
};
namespace impl {
@@ -282,6 +284,10 @@
// Get what stage succeeded during PresentOrValidate: Present or Validate
void takePresentOrValidateStage(Display display, uint32_t * state);
+ // Get the client target properties requested by hardware composer.
+ void takeClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
private:
void resetData();
@@ -479,6 +485,9 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+ Error getClientTargetProperty(
+ Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index fb82033..af76b81 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -668,6 +668,11 @@
return static_cast<Error>(intError);
}
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
+ const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+ return static_cast<Error>(error);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index f4c7fdd..6819ff4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -234,6 +234,8 @@
[[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
+ [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
+ hal::ClientTargetProperty* outClientTargetProperty) = 0;
};
namespace impl {
@@ -305,6 +307,8 @@
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
hal::Error setContentType(hal::ContentType) override;
+ hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 038cec4..7a2f0f3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -541,9 +541,12 @@
error = hwcDisplay->getRequests(&displayRequests, &layerRequests);
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
- outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
- std::move(layerRequests)});
+ DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
+ std::move(layerRequests),
+ std::move(clientTargetProperty)});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b7e9f3a..c355ebd 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -66,6 +66,18 @@
class HWComposer {
public:
+ struct DeviceRequestedChanges {
+ using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+ using ClientTargetProperty = hal::ClientTargetProperty;
+ using DisplayRequests = hal::DisplayRequest;
+ using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+
+ ChangedTypes changedTypes;
+ DisplayRequests displayRequests;
+ LayerRequests layerRequests;
+ ClientTargetProperty clientTargetProperty;
+ };
+
virtual ~HWComposer();
virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
@@ -88,16 +100,6 @@
// Destroy a previously created layer
virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
- struct DeviceRequestedChanges {
- using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
- using DisplayRequests = hal::DisplayRequest;
- using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
-
- ChangedTypes changedTypes;
- DisplayRequests displayRequests;
- LayerRequests layerRequests;
- };
-
// Gets any required composition change requests from the HWC device.
//
// Note that frameUsesClientComposition must be set correctly based on
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 66ee425..bb2888e 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -53,6 +53,7 @@
using Connection = IComposerCallback::Connection;
using ContentType = IComposerClient::ContentType;
using Capability = IComposer::Capability;
+using ClientTargetProperty = IComposerClient::ClientTargetProperty;
using DisplayCapability = IComposerClient::DisplayCapability;
using DisplayRequest = IComposerClient::DisplayRequest;
using DisplayType = IComposerClient::DisplayType;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 17458e3..5b9dbf2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1353,8 +1353,15 @@
// First traverse the tree and count how many layers has votes
int layersWithVote = 0;
traverseTree([&layersWithVote](Layer* layer) {
- if (layer->mCurrentState.frameRate.rate > 0 ||
- layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+ const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+
+ // We do not count layers that are ExactOrMultiple for the same reason
+ // we are allowing touch boost for those layers. See
+ // RefreshRateConfigs::getBestRefreshRate for more details.
+ if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
layersWithVote++;
}
});
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index a1ae35c..d5bebf6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -52,6 +52,8 @@
// Sets the display size. Client is responsible for synchronization.
virtual void setDisplayArea(uint32_t displayArea) = 0;
+ virtual void setConfigChangePending(bool pending) = 0;
+
// Marks the layer as active, and records the given state to its history.
virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
@@ -78,6 +80,8 @@
void setDisplayArea(uint32_t /*displayArea*/) override {}
+ 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;
@@ -134,6 +138,8 @@
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+ 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;
@@ -178,6 +184,9 @@
// Whether to use priority sent from WindowManager to determine the relevancy of the layer.
const bool mUseFrameRatePriority;
+
+ // Whether a config change is in progress or not
+ std::atomic<bool> mConfigChangePending = false;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 120a60f..6570b1a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -62,13 +62,17 @@
const auto layer = weak.promote();
if (!layer) return;
- const auto& name = layer->getName();
- const auto noVoteTag = "LFPS NoVote " + name;
- const auto heuristicVoteTag = "LFPS Heuristic " + name;
- const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
- const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
- const auto minVoteTag = "LFPS Min " + name;
- const auto maxVoteTag = "LFPS Max " + name;
+ const auto makeTag = [layer](LayerHistory::LayerVoteType vote) {
+ return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName();
+ };
+
+ const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote);
+ const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic);
+ const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault);
+ const auto explicitExactOrMultipleVoteTag =
+ makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple);
+ const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min);
+ const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max);
ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
@@ -79,7 +83,7 @@
ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
}
} // namespace
@@ -103,7 +107,7 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now);
+ info->setLastPresentTime(presentTime, now, mConfigChangePending);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -188,7 +192,7 @@
trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
}
- info->clearHistory();
+ info->onLayerInactive(now);
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
@@ -209,7 +213,7 @@
std::lock_guard lock(mLock);
for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory();
+ info->clearHistory(systemTime());
}
}
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 255eac6..25dca39 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -34,12 +34,15 @@
mDefaultVote(defaultVote),
mLayerVote({defaultVote, 0.0f}) {}
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+ bool pendingConfigChange) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
- FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+ FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingConfigChange = pendingConfigChange};
mFrameTimes.push_back(frameTime);
if (mFrameTimes.size() > HISTORY_SIZE) {
@@ -47,23 +50,35 @@
}
}
-bool LayerInfoV2::isFrequent(nsecs_t now) const {
- for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) {
- if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) {
- ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(),
- (now - mFrameTimes.back().queueTime) / 1e6f);
- return false;
- }
+bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
+ return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+ mFrameTimeValidSince.time_since_epoch())
+ .count();
+}
- const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1);
- if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) {
- ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames);
- return true;
+bool LayerInfoV2::isFrequent(nsecs_t now) const {
+ // If we know nothing about this layer we consider it as frequent as it might be the start
+ // of an animation.
+ if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+ return true;
+ }
+
+ // Find the first active frame
+ auto it = mFrameTimes.begin();
+ for (; it != mFrameTimes.end(); ++it) {
+ if (it->queueTime >= getActiveLayerThreshold(now)) {
+ break;
}
}
- ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size());
- return false;
+ const auto numFrames = std::distance(it, mFrameTimes.end());
+ if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
+ return false;
+ }
+
+ // Layer is considered frequent if the average frame rate is higher than the threshold
+ const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
+ return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
@@ -72,6 +87,10 @@
return false;
}
+ if (!isFrameTimeValid(mFrameTimes.front())) {
+ return false;
+ }
+
if (mFrameTimes.size() < HISTORY_SIZE &&
mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
return false;
@@ -80,6 +99,63 @@
return true;
}
+std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
+ nsecs_t totalPresentTimeDeltas = 0;
+ nsecs_t totalQueueTimeDeltas = 0;
+ bool missingPresentTime = false;
+ int numFrames = 0;
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+ continue;
+ }
+
+ totalQueueTimeDeltas +=
+ std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+ numFrames++;
+
+ if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+ missingPresentTime = true;
+ continue;
+ }
+
+ totalPresentTimeDeltas +=
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ }
+
+ // Calculate the average frame time based on presentation timestamps. If those
+ // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+ // we calculated a refresh rate based on presentation timestamps in the past. The reason
+ // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+ // when implementing render ahead for specific refresh rates. When hwui no longer provides
+ // presentation timestamps we look at the queue time to see if the current refresh rate still
+ // matches the content.
+ const auto averageFrameTime =
+ static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+ numFrames;
+ return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
+}
+
+bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+ continue;
+ }
+ const auto presentTimeDeltas = [&] {
+ const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
+ : (it + 1)->presetTime - it->presetTime;
+ return std::max(delta, mHighRefreshRatePeriod);
+ }();
+
+ if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
static constexpr float MARGIN = 1.0f; // 1Hz
@@ -88,51 +164,15 @@
return std::nullopt;
}
- // Calculate the refresh rate by finding the average delta between frames
- nsecs_t totalPresentTimeDeltas = 0;
- nsecs_t totalQueueTimeDeltas = 0;
- auto missingPresentTime = false;
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- totalQueueTimeDeltas +=
- std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
-
- if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
- missingPresentTime = true;
- continue;
- }
-
- totalPresentTimeDeltas +=
- std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
- }
+ const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
// If there are no presentation timestamps provided we can't calculate the refresh rate
if (missingPresentTime && mLastReportedRefreshRate == 0) {
return std::nullopt;
}
- // Calculate the average frame time based on presentation timestamps. If those
- // doesn't exist, we look at the time the buffer was queued only. We can do that only if
- // we calculated a refresh rate based on presentation timestamps in the past. The reason
- // we look at the queue time is to handle cases where hwui attaches presentation timestamps
- // when implementing render ahead for specific refresh rates. When hwui no longer provides
- // presentation timestamps we look at the queue time to see if the current refresh rate still
- // matches the content.
- const float averageFrameTime =
- static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
- (mFrameTimes.size() - 1);
-
- // Now once we calculated the refresh rate we need to make sure that all the frames we captured
- // are evenly distributed and we don't calculate the average across some burst of frames.
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- const auto presentTimeDeltas = [&] {
- const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
- : (it + 1)->presetTime - it->presetTime;
- return std::max(delta, mHighRefreshRatePeriod);
- }();
-
- if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
- return std::nullopt;
- }
+ if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
+ return std::nullopt;
}
const auto refreshRate = 1e9f / averageFrameTime;
@@ -161,7 +201,7 @@
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
}
- ALOGV("%s Max (can't resolve refresh rate", mName.c_str());
+ ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
return {LayerHistory::LayerVoteType::Max, 0};
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 97c7017..5f50d5a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -63,7 +63,7 @@
// 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);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -83,21 +83,35 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- void clearHistory() {
- mFrameTimes.clear();
+ void onLayerInactive(nsecs_t now) {
+ // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+ // We are not deleting the old frame to keep track of whether we should treat the first
+ // buffer as Max as we don't know anything about this layer or Min as this layer is
+ // posting infrequent updates.
+ const auto timePoint = std::chrono::nanoseconds(now);
+ mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
mLastReportedRefreshRate = 0.0f;
}
+ void clearHistory(nsecs_t now) {
+ onLayerInactive(now);
+ mFrameTimes.clear();
+ }
+
private:
// Used to store the layer timestamps
struct FrameTimeData {
nsecs_t presetTime; // desiredPresentTime, if provided
nsecs_t queueTime; // buffer queue time
+ bool pendingConfigChange;
};
bool isFrequent(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible();
+ std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
+ bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;
+ bool isFrameTimeValid(const FrameTimeData&) const;
const std::string mName;
@@ -116,6 +130,8 @@
} mLayerVote;
std::deque<FrameTimeData> mFrameTimes;
+ std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+ std::chrono::steady_clock::now();
static constexpr size_t HISTORY_SIZE = 90;
static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 4a4f9c8..3c8bd68 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -31,6 +31,23 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
+ switch (vote) {
+ case LayerVoteType::NoVote:
+ return "NoVote";
+ case LayerVoteType::Min:
+ return "Min";
+ case LayerVoteType::Max:
+ return "Max";
+ case LayerVoteType::Heuristic:
+ return "Heuristic";
+ case LayerVoteType::ExplicitDefault:
+ return "ExplicitDefault";
+ case LayerVoteType::ExplicitExactOrMultiple:
+ return "ExplicitExactOrMultiple";
+ }
+}
+
const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
const std::vector<LayerRequirement>& layers) const {
std::lock_guard lock(mLock);
@@ -128,15 +145,25 @@
}
}
+ const bool hasExplicitVoteLayers =
+ explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 0;
+
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
- if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) {
+ if (touchActive && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
if (touchConsidered) *touchConsidered = true;
return getMaxRefreshRateByPolicyLocked();
}
- if (!touchActive && idle) {
+ // If the primary range consists of a single refresh rate then we can only
+ // move out the of range if layers explicitly request a different refresh
+ // rate.
+ const Policy* policy = getCurrentPolicyLocked();
+ const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
+
+ if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
return getMinRefreshRateByPolicyLocked();
}
@@ -150,8 +177,6 @@
return getMinRefreshRateByPolicyLocked();
}
- const Policy* policy = getCurrentPolicyLocked();
-
// Find the best refresh rate based on score
std::vector<std::pair<const RefreshRate*, float>> scores;
scores.reserve(mAppRequestRefreshRates.size());
@@ -161,7 +186,8 @@
}
for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
+ ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
+ layerVoteTypeString(layer.vote).c_str(), layer.weight);
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
}
@@ -171,7 +197,8 @@
for (auto i = 0u; i < scores.size(); i++) {
bool inPrimaryRange =
scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
- if (!inPrimaryRange && layer.vote != LayerVoteType::ExplicitDefault &&
+ if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+ layer.vote != LayerVoteType::ExplicitDefault &&
layer.vote != LayerVoteType::ExplicitExactOrMultiple) {
// Only layers with explicit frame rate settings are allowed to score refresh rates
// outside the primary range.
@@ -246,10 +273,8 @@
return 1.0f / iter;
}();
ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
- layer.vote == LayerVoteType::ExplicitExactOrMultiple
- ? "ExplicitExactOrMultiple"
- : "Heuristic",
- weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore);
+ layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
+ scores[i].first->name.c_str(), layerScore);
scores[i].second += weight * layerScore;
continue;
}
@@ -263,11 +288,25 @@
? getBestRefreshRate(scores.rbegin(), scores.rend())
: getBestRefreshRate(scores.begin(), scores.end());
+ if (primaryRangeIsSingleRate) {
+ // If we never scored any layers, then choose the rate from the primary
+ // range instead of picking a random score from the app range.
+ if (std::all_of(scores.begin(), scores.end(),
+ [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+ ALOGV("layers not scored - choose %s",
+ getMaxRefreshRateByPolicyLocked().getName().c_str());
+ return getMaxRefreshRateByPolicyLocked();
+ } else {
+ return *bestRefreshRate;
+ }
+ }
+
// Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+
if (touchActive && explicitDefaultVoteLayers == 0 &&
bestRefreshRate->fps < touchRefreshRate.fps) {
if (touchConsidered) *touchConsidered = true;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2657dee..ff1eabd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -255,6 +255,9 @@
// Stores the current configId the device operates at
void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+ // Returns a string that represents the layer vote type
+ static std::string layerVoteTypeString(LayerVoteType vote);
+
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4eef81d..7c2af23 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -423,6 +423,12 @@
}
}
+void Scheduler::setConfigChangePending(bool pending) {
+ if (mLayerHistory) {
+ mLayerHistory->setConfigChangePending(pending);
+ }
+}
+
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 6eabfd2..066e9ca 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -119,6 +119,7 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
void recordLayerHistory(Layer*, nsecs_t presentTime);
+ void setConfigChangePending(bool pending);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 05e160c..c3214f0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1010,6 +1010,7 @@
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(true);
}
if (mRefreshRateOverlay) {
@@ -1090,6 +1091,7 @@
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+ mScheduler->setConfigChangePending(false);
}
void SurfaceFlinger::performSetActiveConfig() {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index d55648a..dc705ed 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -37,6 +37,7 @@
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
+ static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME;
static constexpr float LO_FPS = 30.f;
static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -71,6 +72,9 @@
}
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer(std::string name) {
+ return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ }
Hwc2::mock::Display mDisplay;
RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
@@ -103,22 +107,12 @@
EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
- // The first few updates are considered infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), 0, time);
- 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));
- }
-
// Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, time);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
}
// Max is returned since we have enough history but there is no timestamp votes.
@@ -127,7 +121,6 @@
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
}
}
@@ -145,7 +138,7 @@
auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size());
// Layer is still considered inactive so we expect to get Min
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
@@ -475,15 +468,28 @@
nsecs_t time = systemTime();
- // The first few updates are considered infrequent
+ // the very first updates makes the layer frequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), 0, time);
+ history().record(layer.get(), time, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, frequentLayerCount(time));
}
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ history().record(layer.get(), time, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ 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));
+
// advance the time for the previous frame to be inactive
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
@@ -526,11 +532,9 @@
nsecs_t time = systemTime();
- // Post a few buffers to the layers to make them active
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(explicitVisiblelayer.get(), time, time);
- history().record(explicitInvisiblelayer.get(), time, time);
- }
+ // Post a buffer to the layers to make them active
+ history().record(explicitVisiblelayer.get(), time, time);
+ history().record(explicitInvisiblelayer.get(), time, time);
EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, history().summarize(time).size());
@@ -541,5 +545,75 @@
EXPECT_EQ(2, frequentLayerCount(time));
}
+class LayerHistoryTestV2Parameterized
+ : public LayerHistoryTestV2,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLayer("HeuristicLayer");
+
+ EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ auto infrequentLayer = createLayer("InfrequentLayer");
+ EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ 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);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ int infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ history().record(heuristicLayer.get(), time, time);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ history().record(infrequentLayer.get(), time, time);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) {
+ ASSERT_NE(0, history().summarize(time).size());
+ ASSERT_GE(2, history().summarize(time).size());
+
+ bool max = false;
+ bool min = false;
+ float heuristic = 0;
+ for (const auto& layer : history().summarize(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_FLOAT_EQ(24.0f, heuristic);
+ EXPECT_FALSE(max);
+ if (history().summarize(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 692f71f..c919e93 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1205,6 +1205,121 @@
}
}
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ bool touchConsidered = false;
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
+ &touchConsidered));
+ EXPECT_EQ(false, touchConsidered);
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitDefault";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
+ &touchConsidered));
+ EXPECT_EQ(false, touchConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+ 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ bool touchConsidered = false;
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 90.0f;
+ lr.name = "90Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
+ &touchConsidered));
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 90.0f;
+ lr.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
+ &touchConsidered));
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+ 0);
+
+ bool touchConsidered = false;
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate({}, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+ EXPECT_EQ(false, touchConsidered);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz ExplicitDefault";
+ EXPECT_EQ(mExpected60Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+
+ lr.vote = LayerVoteType::Heuristic;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Heuristic";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+
+ lr.vote = LayerVoteType::Max;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Max";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+
+ lr.vote = LayerVoteType::Min;
+ lr.desiredRefreshRate = 60.0f;
+ lr.name = "60Hz Min";
+ EXPECT_EQ(mExpected90Config,
+ refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
+ &touchConsidered));
+}
+
TEST_F(RefreshRateConfigsTest, groupSwitching) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
@@ -1234,7 +1349,7 @@
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
+ std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
@@ -1253,7 +1368,7 @@
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+ {HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
0);
bool touchConsidered;
EXPECT_EQ(HWC_CONFIG_ID_60,
@@ -1262,7 +1377,7 @@
&touchConsidered)
.getConfigId());
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90.f));
+ EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2a31078..c2c5072 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -140,6 +140,7 @@
const std::vector<uint8_t>&));
MOCK_METHOD1(getLayerGenericMetadataKeys,
V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
+ MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index dade9fc..fe99e77 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -93,6 +93,7 @@
MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
+ MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 119f580..078d8e07 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,8 +24,9 @@
class MockLayer : public Layer {
public:
- explicit MockLayer(SurfaceFlinger* flinger)
- : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+ MockLayer(SurfaceFlinger* flinger, std::string name)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+ explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 37b5368..d9a9427 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -621,6 +621,7 @@
// global functions
if (instance == VK_NULL_HANDLE) {
if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+ if (strcmp(pName, "vkGetInstanceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties);
if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties);
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
index 7c39075..be24172 100644
--- a/vulkan/scripts/api_generator.py
+++ b/vulkan/scripts/api_generator.py
@@ -152,7 +152,9 @@
if (instance == VK_NULL_HANDLE) {\n""")
for cmd in gencom.command_list:
- if gencom.is_globally_dispatched(cmd):
+ # vkGetInstanceProcAddr(nullptr, "vkGetInstanceProcAddr") is effectively
+ # globally dispatched
+ if gencom.is_globally_dispatched(cmd) or cmd == 'vkGetInstanceProcAddr':
f.write(gencom.indent(2) +
'if (strcmp(pName, \"' + cmd +
'\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +