SF: throttle WindowInfosListener calls
This change updates WindowInfosListenerInvoker to delay and drop
messages when there are unacked messages. When WindowInfosListener calls
are acknowledged before the next windowInfosChanged call, there is no
behavior change. If windowInfosChanged is called and there are unacked
messages, then the update is delayed and sent once the messages are
acked via WindowInfosReportedListener. If windowInfosChanged is called
and there is already a delayed update, then the previous delayed update
is overwritten and only the latest update is sent.
WindowInfosListeners are still called immediately when there are focus
requests. This means the number of unacked messages may be greater than
one.
Bug: 270894765
Test: presubmits
Test: manual fuzz testing (random sleeps added to input flinger listener)
Change-Id: I78b39b10c52f4e4939f1741516d0edbe9898ef3c
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 639ea27..234c6fe 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3725,8 +3725,11 @@
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
- ->windowInfosChanged(windowInfos, displayInfos,
- inputWindowCommands.windowInfosReportedListeners);
+ ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
+ std::move(
+ inputWindowCommands.windowInfosReportedListeners),
+ /* forceImmediateCall= */
+ !inputWindowCommands.focusRequests.empty());
} else {
// If there are listeners but no changes to input windows, call the listeners
// immediately.
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 292083b..73a7cae 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -25,20 +25,14 @@
using gui::IWindowInfosListener;
using gui::WindowInfo;
-struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener,
- DeathRecipient {
- explicit WindowInfosReportedListener(
- size_t callbackCount,
- const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
- SpHash<gui::IWindowInfosReportedListener>>&
- windowInfosReportedListeners)
+struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
+ IBinder::DeathRecipient {
+ WindowInfosReportedListenerInvoker(size_t callbackCount,
+ WindowInfosReportedListenerSet windowInfosReportedListeners)
: mCallbacksPending(callbackCount),
- mWindowInfosReportedListeners(windowInfosReportedListeners) {}
+ mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
binder::Status onWindowInfosReported() override {
- // TODO(b/222421815) There could potentially be callbacks that we don't need to wait for
- // before calling the WindowInfosReportedListeners coming from InputWindowCommands. Filter
- // the list of callbacks down to those from system server.
if (--mCallbacksPending == 0) {
for (const auto& listener : mWindowInfosReportedListeners) {
sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -54,9 +48,7 @@
private:
std::atomic<size_t> mCallbacksPending;
- std::unordered_set<sp<gui::IWindowInfosReportedListener>,
- SpHash<gui::IWindowInfosReportedListener>>
- mWindowInfosReportedListeners;
+ WindowInfosReportedListenerSet mWindowInfosReportedListeners;
};
void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
@@ -82,38 +74,76 @@
}
void WindowInfosListenerInvoker::windowInfosChanged(
- const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
- const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
- SpHash<gui::IWindowInfosReportedListener>>&
- windowInfosReportedListeners) {
- ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+ std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
+ WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall) {
+ reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
+ auto callListeners = [this, windowInfos = std::move(windowInfos),
+ displayInfos = std::move(displayInfos),
+ reportedListeners = std::move(reportedListeners)]() mutable {
+ ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
+ {
+ std::scoped_lock lock(mListenersMutex);
+ for (const auto& [_, listener] : mWindowInfosListeners) {
+ windowInfosListeners.push_back(listener);
+ }
+ }
+
+ auto reportedInvoker =
+ sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners.size(),
+ std::move(reportedListeners));
+
+ for (const auto& listener : windowInfosListeners) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+ // linkToDeath is used here to ensure that the windowInfosReportedListeners
+ // are called even if one of the windowInfosListeners dies before
+ // calling onWindowInfosReported.
+ asBinder->linkToDeath(reportedInvoker);
+
+ auto status =
+ listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
+ if (!status.isOk()) {
+ reportedInvoker->onWindowInfosReported();
+ }
+ }
+ };
+
{
- std::scoped_lock lock(mListenersMutex);
- for (const auto& [_, listener] : mWindowInfosListeners) {
- windowInfosListeners.push_back(listener);
+ std::scoped_lock lock(mMessagesMutex);
+ // If there are unacked messages and this isn't a forced call, then return immediately.
+ // If a forced window infos change doesn't happen first, the update will be sent after
+ // the WindowInfosReportedListeners are called. If a forced window infos change happens or
+ // if there are subsequent delayed messages before this update is sent, then this message
+ // will be dropped and the listeners will only be called with the latest info. This is done
+ // to reduce the amount of binder memory used.
+ if (mActiveMessageCount > 0 && !forceImmediateCall) {
+ mWindowInfosChangedDelayed = std::move(callListeners);
+ return;
}
+
+ mWindowInfosChangedDelayed = nullptr;
+ mActiveMessageCount++;
+ }
+ callListeners();
+}
+
+binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
+ std::function<void()> callListeners;
+
+ {
+ std::scoped_lock lock{mMessagesMutex};
+ mActiveMessageCount--;
+ if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
+ return binder::Status::ok();
+ }
+
+ mActiveMessageCount++;
+ callListeners = std::move(mWindowInfosChangedDelayed);
+ mWindowInfosChangedDelayed = nullptr;
}
- auto windowInfosReportedListener = windowInfosReportedListeners.empty()
- ? nullptr
- : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(),
- windowInfosReportedListeners);
- for (const auto& listener : windowInfosListeners) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
-
- // linkToDeath is used here to ensure that the windowInfosReportedListeners
- // are called even if one of the windowInfosListeners dies before
- // calling onWindowInfosReported.
- if (windowInfosReportedListener) {
- asBinder->linkToDeath(windowInfosReportedListener);
- }
-
- auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
- windowInfosReportedListener);
- if (windowInfosReportedListener && !status.isOk()) {
- windowInfosReportedListener->onWindowInfosReported();
- }
- }
+ callListeners();
+ return binder::Status::ok();
}
} // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index d60a9c4..bfe036e 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,34 +23,40 @@
#include <android/gui/IWindowInfosReportedListener.h>
#include <binder/IBinder.h>
#include <ftl/small_map.h>
+#include <gui/SpHash.h>
#include <utils/Mutex.h>
namespace android {
-class SurfaceFlinger;
+using WindowInfosReportedListenerSet =
+ std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+ gui::SpHash<gui::IWindowInfosReportedListener>>;
-class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+ public IBinder::DeathRecipient {
public:
void addWindowInfosListener(sp<gui::IWindowInfosListener>);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
- void windowInfosChanged(const std::vector<gui::WindowInfo>&,
- const std::vector<gui::DisplayInfo>&,
- const std::unordered_set<sp<gui::IWindowInfosReportedListener>,
- SpHash<gui::IWindowInfosReportedListener>>&
- windowInfosReportedListeners);
+ void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
+ WindowInfosReportedListenerSet windowInfosReportedListeners,
+ bool forceImmediateCall);
+
+ binder::Status onWindowInfosReported() override;
protected:
void binderDied(const wp<IBinder>& who) override;
private:
- struct WindowInfosReportedListener;
-
std::mutex mListenersMutex;
static constexpr size_t kStaticCapacity = 3;
ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity>
mWindowInfosListeners GUARDED_BY(mListenersMutex);
+
+ std::mutex mMessagesMutex;
+ uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
+ std::function<void()> mWindowInfosChangedDelayed GUARDED_BY(mMessagesMutex);
};
} // namespace android