1 /*
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 #include <optional>
17 #define LOG_TAG "InputDispatcher"
18 #define ATRACE_TAG ATRACE_TAG_INPUT
19
20 #define INDENT " "
21 #define INDENT2 " "
22
23 #include <inttypes.h>
24
25 #include <android-base/stringprintf.h>
26 #include <binder/Binder.h>
27 #include <ftl/enum.h>
28 #include <gui/WindowInfo.h>
29 #include <unordered_set>
30
31 #include "DebugConfig.h"
32 #include "FocusResolver.h"
33
34 using android::gui::FocusRequest;
35 using android::gui::WindowInfoHandle;
36
37 namespace android::inputdispatcher {
38
39 template <typename T>
40 struct SpHash {
operator ()android::inputdispatcher::SpHash41 size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
42 };
43
getFocusedWindowToken(int32_t displayId) const44 sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
45 auto it = mFocusedWindowTokenByDisplay.find(displayId);
46 return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
47 }
48
getFocusRequest(int32_t displayId)49 std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
50 auto it = mFocusRequestByDisplay.find(displayId);
51 return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
52 }
53
54 /**
55 * 'setInputWindows' is called when the window properties change. Here we will check whether the
56 * currently focused window can remain focused. If the currently focused window remains eligible
57 * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
58 * we will check if the previous focus request is eligible to receive focus.
59 */
setInputWindows(int32_t displayId,const std::vector<sp<WindowInfoHandle>> & windows)60 std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
61 int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
62 std::string removeFocusReason;
63
64 const std::optional<FocusRequest> request = getFocusRequest(displayId);
65 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
66
67 // Find the next focused token based on the latest FocusRequest. If the requested focus window
68 // cannot be focused, focus will be removed.
69 if (request) {
70 sp<IBinder> requestedFocus = request->token;
71 sp<WindowInfoHandle> resolvedFocusWindow;
72 Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
73 if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
74 return std::nullopt;
75 }
76 const Focusability previousResult = mLastFocusResultByDisplay[displayId];
77 mLastFocusResultByDisplay[displayId] = result;
78 if (result == Focusability::OK) {
79 LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
80 "Focused window should be non-null when result is OK!");
81 return updateFocusedWindow(displayId,
82 "Window became focusable. Previous reason: " +
83 ftl::enum_string(previousResult),
84 resolvedFocusWindow->getToken(),
85 resolvedFocusWindow->getName());
86 }
87 removeFocusReason = ftl::enum_string(result);
88 }
89
90 // Focused window is no longer focusable and we don't have a suitable focus request to grant.
91 // Remove focus if needed.
92 return updateFocusedWindow(displayId, removeFocusReason, nullptr);
93 }
94
setFocusedWindow(const FocusRequest & request,const std::vector<sp<WindowInfoHandle>> & windows)95 std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
96 const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
97 const int32_t displayId = request.displayId;
98 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
99 if (currentFocus == request.token) {
100 ALOGD_IF(DEBUG_FOCUS,
101 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
102 request.windowName.c_str(), displayId);
103 return std::nullopt;
104 }
105
106 sp<WindowInfoHandle> resolvedFocusWindow;
107 Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
108 // Update focus request. The focus resolver will always try to handle this request if there is
109 // no focused window on the display.
110 mFocusRequestByDisplay[displayId] = request;
111 mLastFocusResultByDisplay[displayId] = result;
112
113 if (result == Focusability::OK) {
114 LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
115 "Focused window should be non-null when result is OK!");
116 return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(),
117 resolvedFocusWindow->getName());
118 }
119
120 // The requested window is not currently focusable. Wait for the window to become focusable
121 // but remove focus from the current window so that input events can go into a pending queue
122 // and be sent to the window when it becomes focused.
123 return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
124 nullptr);
125 }
126
getResolvedFocusWindow(const sp<IBinder> & token,const std::vector<sp<WindowInfoHandle>> & windows,sp<WindowInfoHandle> & outFocusableWindow)127 FocusResolver::Focusability FocusResolver::getResolvedFocusWindow(
128 const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
129 sp<WindowInfoHandle>& outFocusableWindow) {
130 sp<IBinder> curFocusCandidate = token;
131 bool focusedWindowFound = false;
132
133 // Keep track of all windows reached to prevent a cyclical transferFocus request.
134 std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached;
135
136 while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) {
137 tokensReached.emplace(curFocusCandidate);
138 Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow);
139 if (result == Focusability::OK) {
140 LOG_ALWAYS_FATAL_IF(!outFocusableWindow,
141 "Focused window should be non-null when result is OK!");
142 focusedWindowFound = true;
143 // outFocusableWindow has been updated by isTokenFocusable to contain
144 // the window info for curFocusCandidate. See if we can grant focus
145 // to the token that it wants to transfer its focus to.
146 curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget;
147 }
148
149 // If the initial token is not focusable, return early with the failed result.
150 if (!focusedWindowFound) {
151 return result;
152 }
153 }
154
155 return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW;
156 }
157
isTokenFocusable(const sp<IBinder> & token,const std::vector<sp<WindowInfoHandle>> & windows,sp<WindowInfoHandle> & outFocusableWindow)158 FocusResolver::Focusability FocusResolver::isTokenFocusable(
159 const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
160 sp<WindowInfoHandle>& outFocusableWindow) {
161 bool allWindowsAreFocusable = true;
162 bool windowFound = false;
163 sp<WindowInfoHandle> visibleWindowHandle = nullptr;
164 for (const sp<WindowInfoHandle>& window : windows) {
165 if (window->getToken() != token) {
166 continue;
167 }
168 windowFound = true;
169 if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
170 // Check if at least a single window is visible.
171 visibleWindowHandle = window;
172 }
173 if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
174 // Check if all windows with the window token are focusable.
175 allWindowsAreFocusable = false;
176 break;
177 }
178 }
179
180 if (!windowFound) {
181 return Focusability::NO_WINDOW;
182 }
183 if (!allWindowsAreFocusable) {
184 return Focusability::NOT_FOCUSABLE;
185 }
186 if (!visibleWindowHandle) {
187 return Focusability::NOT_VISIBLE;
188 }
189
190 // Only set the outFoundWindow if the window can be focused
191 outFocusableWindow = visibleWindowHandle;
192 return Focusability::OK;
193 }
194
updateFocusedWindow(int32_t displayId,const std::string & reason,const sp<IBinder> & newFocus,const std::string & tokenName)195 std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
196 int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
197 const std::string& tokenName) {
198 sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
199 if (newFocus == oldFocus) {
200 return std::nullopt;
201 }
202 if (newFocus) {
203 mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
204 } else {
205 mFocusedWindowTokenByDisplay.erase(displayId);
206 }
207
208 return {{oldFocus, newFocus, displayId, reason}};
209 }
210
dumpFocusedWindows() const211 std::string FocusResolver::dumpFocusedWindows() const {
212 if (mFocusedWindowTokenByDisplay.empty()) {
213 return INDENT "FocusedWindows: <none>\n";
214 }
215
216 std::string dump;
217 dump += INDENT "FocusedWindows:\n";
218 for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
219 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
220 namedToken.first.c_str());
221 }
222 return dump;
223 }
224
dump() const225 std::string FocusResolver::dump() const {
226 std::string dump = dumpFocusedWindows();
227 if (mFocusRequestByDisplay.empty()) {
228 return dump + INDENT "FocusRequests: <none>\n";
229 }
230
231 dump += INDENT "FocusRequests:\n";
232 for (const auto& [displayId, request] : mFocusRequestByDisplay) {
233 auto it = mLastFocusResultByDisplay.find(displayId);
234 std::string result =
235 it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
236 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
237 displayId, request.windowName.c_str(), result.c_str());
238 }
239 return dump;
240 }
241
displayRemoved(int32_t displayId)242 void FocusResolver::displayRemoved(int32_t displayId) {
243 mFocusRequestByDisplay.erase(displayId);
244 mLastFocusResultByDisplay.erase(displayId);
245 }
246
247 } // namespace android::inputdispatcher
248