Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1caf3b7d authored by Patrick Williams's avatar Patrick Williams
Browse files

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.

This reverts commit 1234a337.

Bug: 270894765
Test: presubmits
Test: manual fuzz testing (random sleeps added to input flinger listener)
Change-Id: If43b7ab91e05df863e9e6ac51b0bbd36cabe85d7
parent 29b38f3b
Loading
Loading
Loading
Loading
+1 −10
Original line number Diff line number Diff line
@@ -2442,16 +2442,7 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
        info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
    }

    // For compatibility reasons we let layers which can receive input
    // receive input before they have actually submitted a buffer. Because
    // of this we use canReceiveInput instead of isVisible to check the
    // policy-visibility, ignoring the buffer state. However for layers with
    // hasInputInfo()==false we can use the real visibility state.
    // We are just using these layers for occlusion detection in
    // InputDispatcher, and obviously if they aren't visible they can't occlude
    // anything.
    const bool visible = hasInputInfo() ? canReceiveInput() : isVisible();
    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
    info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());

    info.alpha = getAlpha();
    fillTouchOcclusionMode(info);
+15 −0
Original line number Diff line number Diff line
@@ -371,6 +371,21 @@ public:
     */
    bool canReceiveInput() const;

    /*
     * Whether or not the layer should be considered visible for input calculations.
     */
    virtual bool isVisibleForInput() const {
        // For compatibility reasons we let layers which can receive input
        // receive input before they have actually submitted a buffer. Because
        // of this we use canReceiveInput instead of isVisible to check the
        // policy-visibility, ignoring the buffer state. However for layers with
        // hasInputInfo()==false we can use the real visibility state.
        // We are just using these layers for occlusion detection in
        // InputDispatcher, and obviously if they aren't visible they can't occlude
        // anything.
        return hasInputInfo() ? canReceiveInput() : isVisible();
    }

    /*
     * isProtected - true if the layer may contain protected contents in the
     * GRALLOC_USAGE_PROTECTED sense.
+19 −3
Original line number Diff line number Diff line
@@ -3715,17 +3715,33 @@ void SurfaceFlinger::updateInputFlinger() {
        return;
    }

    std::unordered_set<Layer*> visibleLayers;
    mDrawingState.traverse([&visibleLayers](Layer* layer) {
        if (layer->isVisibleForInput()) {
            visibleLayers.insert(layer);
        }
    });
    bool visibleLayersChanged = false;
    if (visibleLayers != mVisibleLayers) {
        visibleLayersChanged = true;
        mVisibleLayers = std::move(visibleLayers);
    }

    BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
                                                      windowInfos = std::move(windowInfos),
                                                      displayInfos = std::move(displayInfos),
                                                      inputWindowCommands =
                                                              std::move(mInputWindowCommands),
                                                      inputFlinger = mInputFlinger, this]() {
                                                      inputFlinger = mInputFlinger, this,
                                                      visibleLayersChanged]() {
        ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
        if (updateWindowInfo) {
            mWindowInfosListenerInvoker
                    ->windowInfosChanged(windowInfos, displayInfos,
                                         inputWindowCommands.windowInfosReportedListeners);
                    ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
                                         std::move(
                                                 inputWindowCommands.windowInfosReportedListeners),
                                         /* forceImmediateCall= */ visibleLayersChanged ||
                                                 !inputWindowCommands.focusRequests.empty());
        } else {
            // If there are listeners but no changes to input windows, call the listeners
            // immediately.
+5 −0
Original line number Diff line number Diff line
@@ -1424,6 +1424,11 @@ private:
    TransactionHandler mTransactionHandler;
    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
    bool mFrontEndDisplayInfosChanged = false;

    // Layers visible during the last commit. This set should only be used for testing set equality
    // and membership. The pointers should not be dereferenced as it's possible the set contains
    // pointers to freed layers.
    std::unordered_set<Layer*> mVisibleLayers;
};

class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
+86 −40
Original line number Diff line number Diff line
@@ -25,20 +25,17 @@ using gui::DisplayInfo;
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)
          : mCallbacksPending(callbackCount),
            mWindowInfosReportedListeners(windowInfosReportedListeners) {}
using WindowInfosListenerVector = ftl::SmallVector<const sp<IWindowInfosListener>, 3>;

struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
                                            IBinder::DeathRecipient {
    WindowInfosReportedListenerInvoker(WindowInfosListenerVector windowInfosListeners,
                                       WindowInfosReportedListenerSet windowInfosReportedListeners)
          : mCallbacksPending(windowInfosListeners.size()),
            mWindowInfosListeners(std::move(windowInfosListeners)),
            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);
@@ -46,6 +43,12 @@ struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowIn
                    listener->onWindowInfosReported();
                }
            }

            auto wpThis = wp<WindowInfosReportedListenerInvoker>::fromExisting(this);
            for (const auto& listener : mWindowInfosListeners) {
                sp<IBinder> binder = IInterface::asBinder(listener);
                binder->unlinkToDeath(wpThis);
            }
        }
        return binder::Status::ok();
    }
@@ -54,9 +57,9 @@ struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowIn

private:
    std::atomic<size_t> mCallbacksPending;
    std::unordered_set<sp<gui::IWindowInfosReportedListener>,
                       SpHash<gui::IWindowInfosReportedListener>>
            mWindowInfosReportedListeners;
    static constexpr size_t kStaticCapacity = 3;
    const WindowInfosListenerVector mWindowInfosListeners;
    WindowInfosReportedListenerSet mWindowInfosReportedListeners;
};

void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
@@ -82,11 +85,13 @@ void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
}

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)](
                                 WindowInfosReportedListenerSet reportedListeners) mutable {
        WindowInfosListenerVector windowInfosListeners;
        {
            std::scoped_lock lock(mListenersMutex);
            for (const auto& [_, listener] : mWindowInfosListeners) {
@@ -94,26 +99,67 @@ void WindowInfosListenerInvoker::windowInfosChanged(
            }
        }

    auto windowInfosReportedListener = windowInfosReportedListeners.empty()
            ? nullptr
            : sp<WindowInfosReportedListener>::make(windowInfosListeners.size(),
                                                    windowInfosReportedListeners);
        auto reportedInvoker =
                sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners,
                                                             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.
        if (windowInfosReportedListener) {
            asBinder->linkToDeath(windowInfosReportedListener);
            asBinder->linkToDeath(reportedInvoker);

            auto status =
                    listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
            if (!status.isOk()) {
                reportedInvoker->onWindowInfosReported();
            }
        }
    };

    {
        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);
            mReportedListenersDelayed.merge(reportedListeners);
            return;
        }

        auto status = listener->onWindowInfosChanged(windowInfos, displayInfos,
                                                     windowInfosReportedListener);
        if (windowInfosReportedListener && !status.isOk()) {
            windowInfosReportedListener->onWindowInfosReported();
        mWindowInfosChangedDelayed = nullptr;
        reportedListeners.merge(mReportedListenersDelayed);
        mActiveMessageCount++;
    }
    callListeners(std::move(reportedListeners));
}

binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
    std::function<void(WindowInfosReportedListenerSet)> callListeners;
    WindowInfosReportedListenerSet reportedListeners;

    {
        std::scoped_lock lock{mMessagesMutex};
        mActiveMessageCount--;
        if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
            return binder::Status::ok();
        }

        mActiveMessageCount++;
        callListeners = std::move(mWindowInfosChangedDelayed);
        mWindowInfosChangedDelayed = nullptr;
        reportedListeners = std::move(mReportedListenersDelayed);
        mReportedListenersDelayed.clear();
    }

    callListeners(std::move(reportedListeners));
    return binder::Status::ok();
}

} // namespace android
Loading