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

Commit 834da01d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "FocusResolver: Persist focus requests" into sc-dev

parents 671994c8 1dcad985
Loading
Loading
Loading
Loading
+83 −89
Original line number Diff line number Diff line
@@ -40,75 +40,56 @@ sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
    return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}

std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
    auto it = mPendingFocusRequests.find(displayId);
    return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
    auto it = mFocusRequestByDisplay.find(displayId);
    return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
}

/**
 * 'setInputWindows' is called when the window properties change. Here we will check whether the
 * currently focused window can remain focused. If the currently focused window remains eligible
 * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
 * we will check if the previous focus request is eligible to receive focus.
 */
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
        int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
    // If the current focused window becomes unfocusable, remove focus.
    sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
    if (currentFocus) {
        FocusResult result = isTokenFocusable(currentFocus, windows);
        if (result != FocusResult::OK) {
            return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
        }
    }
    std::string removeFocusReason;

    // Check if any pending focus requests can be resolved.
    std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
    if (!pendingRequest) {
    // Check if the currently focused window is still focusable.
    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
    if (currentFocus) {
        Focusability result = isTokenFocusable(currentFocus, windows);
        if (result == Focusability::OK) {
            return std::nullopt;
        }

    sp<IBinder> requestedFocus = pendingRequest->token;
    std::string windowName = pendingRequest->windowName;
    if (currentFocus == requestedFocus) {
        ALOGD_IF(DEBUG_FOCUS,
                 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
                 windowName.c_str(), displayId);
        mPendingFocusRequests.erase(displayId);
        return std::nullopt;
        removeFocusReason = NamedEnum::string(result);
    }

    FocusResult result = isTokenFocusable(requestedFocus, windows);
    // If the window from the pending request is now visible, provide it focus.
    if (result == FocusResult::OK) {
        mPendingFocusRequests.erase(displayId);
        return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
    // We don't have a focused window or the currently focused window is no longer focusable. Check
    // to see if we can grant focus to the window that previously requested focus.
    const std::optional<FocusRequest> request = getFocusRequest(displayId);
    if (request) {
        sp<IBinder> requestedFocus = request->token;
        const Focusability result = isTokenFocusable(requestedFocus, windows);
        const Focusability previousResult = mLastFocusResultByDisplay[displayId];
        mLastFocusResultByDisplay[displayId] = result;
        if (result == Focusability::OK) {
            return updateFocusedWindow(displayId,
                                       "Window became focusable. Previous reason: " +
                                               NamedEnum::string(previousResult),
                                       requestedFocus, request->windowName);
        }

    if (result != FocusResult::NOT_VISIBLE) {
        // Drop the request if we are unable to change the focus for a reason other than visibility.
        ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
              displayId, NamedEnum::string(result).c_str());
        mPendingFocusRequests.erase(displayId);
    }
    return std::nullopt;

    // Focused window is no longer focusable and we don't have a suitable focus request to grant.
    // Remove focus if needed.
    return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}

std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
        const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
    const int32_t displayId = request.displayId;
    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
    if (request.focusedToken && currentFocus != request.focusedToken) {
        ALOGW("setFocusedWindow %s on display %" PRId32
              " ignored, reason: focusedToken  %s is not focused",
              request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
        return std::nullopt;
    }

    std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
    if (pendingRequest) {
        ALOGW("Pending focus request %s on display %" PRId32
              " ignored, reason:replaced by new request",
              pendingRequest->windowName.c_str(), displayId);

        // clear any pending focus requests
        mPendingFocusRequests.erase(displayId);
    }

    if (currentFocus == request.token) {
        ALOGD_IF(DEBUG_FOCUS,
                 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
@@ -116,34 +97,45 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
        return std::nullopt;
    }

    FocusResult result = isTokenFocusable(request.token, windows);
    if (result == FocusResult::OK) {
        std::string reason =
                (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
        return updateFocusedWindow(displayId, reason, request.token, request.windowName);
    // Handle conditional focus requests, i.e. requests that have a focused token. These requests
    // are not persistent. If the window is no longer focusable, we expect focus to go back to the
    // previously focused window.
    if (request.focusedToken) {
        if (currentFocus != request.focusedToken) {
            ALOGW("setFocusedWindow %s on display %" PRId32
                  " ignored, reason: focusedToken %s is not focused",
                  request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
            return std::nullopt;
        }
        Focusability result = isTokenFocusable(request.token, windows);
        if (result == Focusability::OK) {
            return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
                                       request.token, request.windowName);
        }

    if (result == FocusResult::NOT_VISIBLE) {
        // The requested window is not currently visible. Wait for the window to become visible
        // and then provide it focus. This is to handle situations where a user action triggers
        // a new window to appear. We want to be able to queue any key events after the user
        // action and deliver it to the newly focused window. In order for this to happen, we
        // take focus from the currently focused window so key events can be queued.
        ALOGD_IF(DEBUG_FOCUS,
                 "setFocusedWindow %s on display %" PRId32
                 " pending, reason: window is not visible",
                 request.windowName.c_str(), displayId);
        mPendingFocusRequests[displayId] = request;
        return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
    } else {
        ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
              request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
        return std::nullopt;
    }

    return std::nullopt;
    Focusability result = isTokenFocusable(request.token, windows);
    // Update focus request. The focus resolver will always try to handle this request if there is
    // no focused window on the display.
    mFocusRequestByDisplay[displayId] = request;
    mLastFocusResultByDisplay[displayId] = result;

    if (result == Focusability::OK) {
        return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
                                   request.windowName);
    }

FocusResolver::FocusResult FocusResolver::isTokenFocusable(
    // The requested window is not currently focusable. Wait for the window to become focusable
    // but remove focus from the current window so that input events can go into a pending queue
    // and be sent to the window when it becomes focused.
    return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
                               nullptr);
}

FocusResolver::Focusability FocusResolver::isTokenFocusable(
        const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
    bool allWindowsAreFocusable = true;
    bool visibleWindowFound = false;
@@ -165,16 +157,16 @@ FocusResolver::FocusResult FocusResolver::isTokenFocusable(
    }

    if (!windowFound) {
        return FocusResult::NO_WINDOW;
        return Focusability::NO_WINDOW;
    }
    if (!allWindowsAreFocusable) {
        return FocusResult::NOT_FOCUSABLE;
        return Focusability::NOT_FOCUSABLE;
    }
    if (!visibleWindowFound) {
        return FocusResult::NOT_VISIBLE;
        return Focusability::NOT_VISIBLE;
    }

    return FocusResult::OK;
    return Focusability::OK;
}

std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
@@ -209,15 +201,17 @@ std::string FocusResolver::dumpFocusedWindows() const {

std::string FocusResolver::dump() const {
    std::string dump = dumpFocusedWindows();

    if (mPendingFocusRequests.empty()) {
        return dump + INDENT "PendingFocusRequests: <none>\n";
    if (mFocusRequestByDisplay.empty()) {
        return dump + INDENT "FocusRequests: <none>\n";
    }

    dump += INDENT "PendingFocusRequests:\n";
    for (const auto& [displayId, request] : mPendingFocusRequests) {
        dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
                                   request.windowName.c_str());
    dump += INDENT "FocusRequests:\n";
    for (const auto& [displayId, request] : mFocusRequestByDisplay) {
        auto it = mLastFocusResultByDisplay.find(displayId);
        std::string result =
                it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
        dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
                                   displayId, request.windowName.c_str(), result.c_str());
    }
    return dump;
}
+21 −11
Original line number Diff line number Diff line
@@ -34,11 +34,18 @@ namespace android::inputdispatcher {
//   is visible with the same token and all window handles with the same token are focusable.
//   See FocusResolver::isTokenFocusable
//
//   Focus request - Request will be granted if the window is focusable. If the window is not
//   visible, then the request is kept in a pending state and granted when it becomes visible.
//   If window becomes not focusable, or another request comes in, the pending request is dropped.
//   Focus request - Request will be granted if the window is focusable. If it's not
//   focusable, then the request is persisted and granted when it becomes focusable. The currently
//   focused window will lose focus and any pending keys will be added to a queue so it can be sent
//   to the window when it gets focus.
//
//   Condition focus request - Request with a focus token specified. Request will be granted if the
//   window is focusable and the focus token is the currently focused. Otherwise, the request is
//   dropped. Conditional focus requests are not persisted. The window will lose focus and go back
//   to the focus token if it becomes not focusable.
//
//   Window handle updates - Focus is lost when the currently focused window becomes not focusable.
//   If the previous focus request is focusable, then we will try to grant that window focus.
class FocusResolver {
public:
    // Returns the focused window token on the specified display.
@@ -61,7 +68,7 @@ public:
    std::string dump() const;

private:
    enum class FocusResult {
    enum class Focusability {
        OK,
        NO_WINDOW,
        NOT_FOCUSABLE,
@@ -77,7 +84,7 @@ private:
    // we expect the focusability of the windows to match since its hard to reason why one window
    // can receive focus events and the other cannot when both are backed by the same input channel.
    //
    static FocusResult isTokenFocusable(const sp<IBinder>& token,
    static Focusability isTokenFocusable(const sp<IBinder>& token,
                                         const std::vector<sp<InputWindowHandle>>& windows);

    // Focus tracking for keys, trackball, etc. A window token can be associated with one or
@@ -87,15 +94,18 @@ private:
    typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
    std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;

    // This map will store a single pending focus request per display that cannot be currently
    // processed. This can happen if the window requested to be focused is not currently visible.
    // Such a window might become visible later, and these requests would be processed at that time.
    std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
    // This map will store the focus request per display. When the input window handles are updated,
    // the current request will be checked to see if it can be processed at that time.
    std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;

    // Last reason for not granting a focus request. This is used to add more debug information
    // in the event logs.
    std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;

    std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
            int32_t displayId, const std::string& reason, const sp<IBinder>& token,
            const std::string& tokenName = "");
    std::optional<FocusRequest> getPendingRequest(int32_t displayId);
    std::optional<FocusRequest> getFocusRequest(int32_t displayId);
};

} // namespace android::inputdispatcher
 No newline at end of file
+107 −12
Original line number Diff line number Diff line
@@ -18,6 +18,12 @@

#include "../FocusResolver.h"

#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
    {                                                       \
        ASSERT_EQ(_oldFocus, _changes->oldFocus);           \
        ASSERT_EQ(_newFocus, _changes->newFocus);           \
    }

// atest inputflinger_tests:FocusResolverTest

namespace android::inputdispatcher {
@@ -56,8 +62,7 @@ TEST(FocusResolverTest, SetFocusedWindow) {
    FocusResolver focusResolver;
    std::optional<FocusResolver::FocusChanges> changes =
            focusResolver.setFocusedWindow(request, windows);
    ASSERT_EQ(nullptr, changes->oldFocus);
    ASSERT_EQ(focusableWindowToken, changes->newFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
    ASSERT_EQ(request.displayId, changes->displayId);

    // invisible window cannot get focused
@@ -65,6 +70,7 @@ TEST(FocusResolverTest, SetFocusedWindow) {
    changes = focusResolver.setFocusedWindow(request, windows);
    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
    ASSERT_EQ(nullptr, changes->newFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);

    // unfocusableWindowToken window cannot get focused
    request.token = unfocusableWindowToken;
@@ -99,19 +105,17 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) {
    FocusResolver focusResolver;
    std::optional<FocusResolver::FocusChanges> changes =
            focusResolver.setFocusedWindow(request, windows);
    ASSERT_EQ(nullptr, changes->oldFocus);
    ASSERT_EQ(focusableWindowToken, changes->newFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);

    // mirrored window with one visible window can get focused
    request.token = invisibleWindowToken;
    changes = focusResolver.setFocusedWindow(request, windows);
    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
    ASSERT_EQ(invisibleWindowToken, changes->newFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);

    // mirrored window with one or more unfocusable window cannot get focused
    request.token = unfocusableWindowToken;
    changes = focusResolver.setFocusedWindow(request, windows);
    ASSERT_FALSE(changes);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}

TEST(FocusResolverTest, SetInputWindows) {
@@ -130,11 +134,10 @@ TEST(FocusResolverTest, SetInputWindows) {
            focusResolver.setFocusedWindow(request, windows);
    ASSERT_EQ(focusableWindowToken, changes->newFocus);

    // Window visibility changes and the window loses focused
    // Window visibility changes and the window loses focus
    window->setVisible(false);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_EQ(nullptr, changes->newFocus);
    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}

TEST(FocusResolverTest, FocusRequestsCanBePending) {
@@ -158,8 +161,100 @@ TEST(FocusResolverTest, FocusRequestsCanBePending) {
    // Window visibility changes and the window gets focused
    invisibleWindow->setVisible(true);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_EQ(nullptr, changes->oldFocus);
    ASSERT_EQ(invisibleWindowToken, changes->newFocus);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
}

TEST(FocusResolverTest, FocusRequestsArePersistent) {
    sp<IBinder> windowToken = new BBinder();
    std::vector<sp<InputWindowHandle>> windows;

    sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
                                                       false /* focusable */, true /* visible */);
    windows.push_back(window);

    // non-focusable window cannot get focused
    FocusRequest request;
    request.displayId = 42;
    request.token = windowToken;
    FocusResolver focusResolver;
    std::optional<FocusResolver::FocusChanges> changes =
            focusResolver.setFocusedWindow(request, windows);
    ASSERT_FALSE(changes);

    // Focusability changes and the window gets focused
    window->setFocusable(true);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);

    // Visibility changes and the window loses focus
    window->setVisible(false);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);

    // Visibility changes and the window gets focused
    window->setVisible(true);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);

    // Window is gone and the window loses focus
    changes = focusResolver.setInputWindows(request.displayId, {});
    ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);

    // Window returns and the window gains focus
    changes = focusResolver.setInputWindows(request.displayId, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
}

TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
    sp<IBinder> hostWindowToken = new BBinder();
    std::vector<sp<InputWindowHandle>> windows;

    sp<FakeWindowHandle> hostWindow =
            new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
                                 true /* visible */);
    windows.push_back(hostWindow);
    sp<IBinder> embeddedWindowToken = new BBinder();
    sp<FakeWindowHandle> embeddedWindow =
            new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
                                 true /* visible */);
    windows.push_back(embeddedWindow);

    FocusRequest request;
    request.displayId = 42;
    request.token = hostWindowToken;
    FocusResolver focusResolver;
    std::optional<FocusResolver::FocusChanges> changes =
            focusResolver.setFocusedWindow(request, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);

    request.focusedToken = hostWindow->getToken();
    request.token = embeddedWindowToken;
    changes = focusResolver.setFocusedWindow(request, windows);
    ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);

    embeddedWindow->setFocusable(false);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    // The embedded window is no longer focusable, provide focus back to the original focused
    // window.
    ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);

    embeddedWindow->setFocusable(true);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    // The embedded window is focusable again, but we it cannot gain focus unless there is another
    // focus request.
    ASSERT_FALSE(changes);

    embeddedWindow->setVisible(false);
    changes = focusResolver.setFocusedWindow(request, windows);
    // If the embedded window is not visible/focusable, then we do not grant it focus and the
    // request is dropped.
    ASSERT_FALSE(changes);

    embeddedWindow->setVisible(true);
    changes = focusResolver.setInputWindows(request.displayId, windows);
    // If the embedded window becomes visble/focusable, nothing changes since the request has been
    // dropped.
    ASSERT_FALSE(changes);
}

} // namespace android::inputdispatcher