InputDispatcher: Refactor focus logic into FocusResolver

Pull out logic that keeps track of the focused window per display and
the logic used to determine focus changes to a new class. Input
Dispatcher will feed in focus requests & window handle changes. It
will then consume any focus changes.

There is no functional changes in this cl. Additional logging has been
added when we drop a focus request.

Test: atest inputflinger_tests

Change-Id: I04c0c38907cca5d6d98405a6a9f7eb7c497fd90f
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
new file mode 100644
index 0000000..ee6b38b
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FocusResolver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#define INDENT "  "
+#define INDENT2 "    "
+
+// Log debug messages about input focus tracking.
+static constexpr bool DEBUG_FOCUS = false;
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+#include <input/NamedEnum.h>
+#include <log/log.h>
+
+#include "FocusResolver.h"
+
+namespace android::inputdispatcher {
+
+sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+    auto it = mFocusedWindowTokenByDisplay.find(displayId);
+    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<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);
+        }
+    }
+
+    // Check if any pending focus requests can be resolved.
+    std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
+    if (!pendingRequest) {
+        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;
+    }
+
+    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);
+    }
+
+    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;
+}
+
+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",
+                 request.windowName.c_str(), displayId);
+        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);
+    }
+
+    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;
+}
+
+FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+        const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+    bool allWindowsAreFocusable = true;
+    bool visibleWindowFound = false;
+    bool windowFound = false;
+    for (const sp<InputWindowHandle>& window : windows) {
+        if (window->getToken() != token) {
+            continue;
+        }
+        windowFound = true;
+        if (window->getInfo()->visible) {
+            // Check if at least a single window is visible.
+            visibleWindowFound = true;
+        }
+        if (!window->getInfo()->focusable) {
+            // Check if all windows with the window token are focusable.
+            allWindowsAreFocusable = false;
+            break;
+        }
+    }
+
+    if (!windowFound) {
+        return FocusResult::NO_WINDOW;
+    }
+    if (!allWindowsAreFocusable) {
+        return FocusResult::NOT_FOCUSABLE;
+    }
+    if (!visibleWindowFound) {
+        return FocusResult::NOT_VISIBLE;
+    }
+
+    return FocusResult::OK;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
+        int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+        const std::string& tokenName) {
+    sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
+    if (newFocus == oldFocus) {
+        return std::nullopt;
+    }
+    if (newFocus) {
+        mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
+    } else {
+        mFocusedWindowTokenByDisplay.erase(displayId);
+    }
+
+    return {{oldFocus, newFocus, displayId, reason}};
+}
+
+std::string FocusResolver::dumpFocusedWindows() const {
+    if (mFocusedWindowTokenByDisplay.empty()) {
+        return INDENT "FocusedWindows: <none>\n";
+    }
+
+    std::string dump;
+    dump += INDENT "FocusedWindows:\n";
+    for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
+        dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+                                   namedToken.first.c_str());
+    }
+    return dump;
+}
+
+std::string FocusResolver::dump() const {
+    std::string dump = dumpFocusedWindows();
+
+    if (mPendingFocusRequests.empty()) {
+        return dump + INDENT "PendingFocusRequests: <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());
+    }
+    return dump;
+}
+
+} // namespace android::inputdispatcher