blob: ee6b38b93fa6bf0a233382fc2f07e3f0d11858ba [file] [log] [blame]
Vishnu Nairc519ff72021-01-21 08:23:08 -08001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "FocusResolver"
18#define ATRACE_TAG ATRACE_TAG_INPUT
19
20#define INDENT " "
21#define INDENT2 " "
22
23// Log debug messages about input focus tracking.
24static constexpr bool DEBUG_FOCUS = false;
25
26#include <inttypes.h>
27
28#include <android-base/stringprintf.h>
29#include <binder/Binder.h>
30#include <input/InputWindow.h>
31#include <input/NamedEnum.h>
32#include <log/log.h>
33
34#include "FocusResolver.h"
35
36namespace android::inputdispatcher {
37
38sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
39 auto it = mFocusedWindowTokenByDisplay.find(displayId);
40 return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
41}
42
43std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
44 auto it = mPendingFocusRequests.find(displayId);
45 return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
46}
47
48std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
49 int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
50 // If the current focused window becomes unfocusable, remove focus.
51 sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
52 if (currentFocus) {
53 FocusResult result = isTokenFocusable(currentFocus, windows);
54 if (result != FocusResult::OK) {
55 return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
56 }
57 }
58
59 // Check if any pending focus requests can be resolved.
60 std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
61 if (!pendingRequest) {
62 return std::nullopt;
63 }
64
65 sp<IBinder> requestedFocus = pendingRequest->token;
66 std::string windowName = pendingRequest->windowName;
67 if (currentFocus == requestedFocus) {
68 ALOGD_IF(DEBUG_FOCUS,
69 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
70 windowName.c_str(), displayId);
71 mPendingFocusRequests.erase(displayId);
72 return std::nullopt;
73 }
74
75 FocusResult result = isTokenFocusable(requestedFocus, windows);
76 // If the window from the pending request is now visible, provide it focus.
77 if (result == FocusResult::OK) {
78 mPendingFocusRequests.erase(displayId);
79 return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
80 }
81
82 if (result != FocusResult::NOT_VISIBLE) {
83 // Drop the request if we are unable to change the focus for a reason other than visibility.
84 ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
85 displayId, NamedEnum::string(result).c_str());
86 mPendingFocusRequests.erase(displayId);
87 }
88 return std::nullopt;
89}
90
91std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
92 const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
93 const int32_t displayId = request.displayId;
94 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
95 if (request.focusedToken && currentFocus != request.focusedToken) {
96 ALOGW("setFocusedWindow %s on display %" PRId32
97 " ignored, reason: focusedToken %s is not focused",
98 request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
99 return std::nullopt;
100 }
101
102 std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
103 if (pendingRequest) {
104 ALOGW("Pending focus request %s on display %" PRId32
105 " ignored, reason:replaced by new request",
106 pendingRequest->windowName.c_str(), displayId);
107
108 // clear any pending focus requests
109 mPendingFocusRequests.erase(displayId);
110 }
111
112 if (currentFocus == request.token) {
113 ALOGD_IF(DEBUG_FOCUS,
114 "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
115 request.windowName.c_str(), displayId);
116 return std::nullopt;
117 }
118
119 FocusResult result = isTokenFocusable(request.token, windows);
120 if (result == FocusResult::OK) {
121 std::string reason =
122 (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
123 return updateFocusedWindow(displayId, reason, request.token, request.windowName);
124 }
125
126 if (result == FocusResult::NOT_VISIBLE) {
127 // The requested window is not currently visible. Wait for the window to become visible
128 // and then provide it focus. This is to handle situations where a user action triggers
129 // a new window to appear. We want to be able to queue any key events after the user
130 // action and deliver it to the newly focused window. In order for this to happen, we
131 // take focus from the currently focused window so key events can be queued.
132 ALOGD_IF(DEBUG_FOCUS,
133 "setFocusedWindow %s on display %" PRId32
134 " pending, reason: window is not visible",
135 request.windowName.c_str(), displayId);
136 mPendingFocusRequests[displayId] = request;
137 return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
138 } else {
139 ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
140 request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
141 }
142
143 return std::nullopt;
144}
145
146FocusResolver::FocusResult FocusResolver::isTokenFocusable(
147 const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
148 bool allWindowsAreFocusable = true;
149 bool visibleWindowFound = false;
150 bool windowFound = false;
151 for (const sp<InputWindowHandle>& window : windows) {
152 if (window->getToken() != token) {
153 continue;
154 }
155 windowFound = true;
156 if (window->getInfo()->visible) {
157 // Check if at least a single window is visible.
158 visibleWindowFound = true;
159 }
160 if (!window->getInfo()->focusable) {
161 // Check if all windows with the window token are focusable.
162 allWindowsAreFocusable = false;
163 break;
164 }
165 }
166
167 if (!windowFound) {
168 return FocusResult::NO_WINDOW;
169 }
170 if (!allWindowsAreFocusable) {
171 return FocusResult::NOT_FOCUSABLE;
172 }
173 if (!visibleWindowFound) {
174 return FocusResult::NOT_VISIBLE;
175 }
176
177 return FocusResult::OK;
178}
179
180std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
181 int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
182 const std::string& tokenName) {
183 sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
184 if (newFocus == oldFocus) {
185 return std::nullopt;
186 }
187 if (newFocus) {
188 mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
189 } else {
190 mFocusedWindowTokenByDisplay.erase(displayId);
191 }
192
193 return {{oldFocus, newFocus, displayId, reason}};
194}
195
196std::string FocusResolver::dumpFocusedWindows() const {
197 if (mFocusedWindowTokenByDisplay.empty()) {
198 return INDENT "FocusedWindows: <none>\n";
199 }
200
201 std::string dump;
202 dump += INDENT "FocusedWindows:\n";
203 for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
204 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
205 namedToken.first.c_str());
206 }
207 return dump;
208}
209
210std::string FocusResolver::dump() const {
211 std::string dump = dumpFocusedWindows();
212
213 if (mPendingFocusRequests.empty()) {
214 return dump + INDENT "PendingFocusRequests: <none>\n";
215 }
216
217 dump += INDENT "PendingFocusRequests:\n";
218 for (const auto& [displayId, request] : mPendingFocusRequests) {
219 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
220 request.windowName.c_str());
221 }
222 return dump;
223}
224
225} // namespace android::inputdispatcher