blob: ee6b38b93fa6bf0a233382fc2f07e3f0d11858ba [file] [log] [blame]
/*
* 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