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
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.
24 static constexpr bool DEBUG_FOCUS = false;
25
26 #include <inttypes.h>
27
28 #include <android-base/stringprintf.h>
29 #include <binder/Binder.h>
30 #include <ftl/enum.h>
31 #include <gui/WindowInfo.h>
32 #include <log/log.h>
33
34 #include "FocusResolver.h"
35
36 using android::gui::FocusRequest;
37 using android::gui::WindowInfoHandle;
38
39 namespace android::inputdispatcher {
40
getFocusedWindowToken(int32_t displayId) const41 sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
42 auto it = mFocusedWindowTokenByDisplay.find(displayId);
43 return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
44 }
45
getFocusRequest(int32_t displayId)46 std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
47 auto it = mFocusRequestByDisplay.find(displayId);
48 return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
49 }
50
51 /**
52 * 'setInputWindows' is called when the window properties change. Here we will check whether the
53 * currently focused window can remain focused. If the currently focused window remains eligible
54 * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
55 * we will check if the previous focus request is eligible to receive focus.
56 */
setInputWindows(int32_t displayId,const std::vector<sp<WindowInfoHandle>> & windows)57 std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
58 int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
59 std::string removeFocusReason;
60
61 // Check if the currently focused window is still focusable.
62 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
63 if (currentFocus) {
64 Focusability result = isTokenFocusable(currentFocus, windows);
65 if (result == Focusability::OK) {
66 return std::nullopt;
67 }
68 removeFocusReason = ftl::enum_string(result);
69 }
70
71 // We don't have a focused window or the currently focused window is no longer focusable. Check
72 // to see if we can grant focus to the window that previously requested focus.
73 const std::optional<FocusRequest> request = getFocusRequest(displayId);
74 if (request) {
75 sp<IBinder> requestedFocus = request->token;
76 const Focusability result = isTokenFocusable(requestedFocus, windows);
77 const Focusability previousResult = mLastFocusResultByDisplay[displayId];
78 mLastFocusResultByDisplay[displayId] = result;
79 if (result == Focusability::OK) {
80 return updateFocusedWindow(displayId,
81 "Window became focusable. Previous reason: " +
82 ftl::enum_string(previousResult),
83 requestedFocus, request->windowName);
84 }
85 }
86
87 // Focused window is no longer focusable and we don't have a suitable focus request to grant.
88 // Remove focus if needed.
89 return updateFocusedWindow(displayId, removeFocusReason, nullptr);
90 }
91
setFocusedWindow(const FocusRequest & request,const std::vector<sp<WindowInfoHandle>> & windows)92 std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
93 const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
94 const int32_t displayId = request.displayId;
95 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
96 if (currentFocus == request.token) {
97 ALOGD_IF(DEBUG_FOCUS,
98 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
99 request.windowName.c_str(), displayId);
100 return std::nullopt;
101 }
102
103 // Handle conditional focus requests, i.e. requests that have a focused token. These requests
104 // are not persistent. If the window is no longer focusable, we expect focus to go back to the
105 // previously focused window.
106 if (request.focusedToken) {
107 if (currentFocus != request.focusedToken) {
108 ALOGW("setFocusedWindow %s on display %" PRId32
109 " ignored, reason: focusedToken %s is not focused",
110 request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
111 return std::nullopt;
112 }
113 Focusability result = isTokenFocusable(request.token, windows);
114 if (result == Focusability::OK) {
115 return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
116 request.token, request.windowName);
117 }
118 ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
119 request.windowName.c_str(), displayId, ftl::enum_string(result).c_str());
120 return std::nullopt;
121 }
122
123 Focusability result = isTokenFocusable(request.token, windows);
124 // Update focus request. The focus resolver will always try to handle this request if there is
125 // no focused window on the display.
126 mFocusRequestByDisplay[displayId] = request;
127 mLastFocusResultByDisplay[displayId] = result;
128
129 if (result == Focusability::OK) {
130 return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
131 request.windowName);
132 }
133
134 // The requested window is not currently focusable. Wait for the window to become focusable
135 // but remove focus from the current window so that input events can go into a pending queue
136 // and be sent to the window when it becomes focused.
137 return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
138 nullptr);
139 }
140
isTokenFocusable(const sp<IBinder> & token,const std::vector<sp<WindowInfoHandle>> & windows)141 FocusResolver::Focusability FocusResolver::isTokenFocusable(
142 const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows) {
143 bool allWindowsAreFocusable = true;
144 bool visibleWindowFound = false;
145 bool windowFound = false;
146 for (const sp<WindowInfoHandle>& window : windows) {
147 if (window->getToken() != token) {
148 continue;
149 }
150 windowFound = true;
151 if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
152 // Check if at least a single window is visible.
153 visibleWindowFound = true;
154 }
155 if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
156 // Check if all windows with the window token are focusable.
157 allWindowsAreFocusable = false;
158 break;
159 }
160 }
161
162 if (!windowFound) {
163 return Focusability::NO_WINDOW;
164 }
165 if (!allWindowsAreFocusable) {
166 return Focusability::NOT_FOCUSABLE;
167 }
168 if (!visibleWindowFound) {
169 return Focusability::NOT_VISIBLE;
170 }
171
172 return Focusability::OK;
173 }
174
updateFocusedWindow(int32_t displayId,const std::string & reason,const sp<IBinder> & newFocus,const std::string & tokenName)175 std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
176 int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
177 const std::string& tokenName) {
178 sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
179 if (newFocus == oldFocus) {
180 return std::nullopt;
181 }
182 if (newFocus) {
183 mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
184 } else {
185 mFocusedWindowTokenByDisplay.erase(displayId);
186 }
187
188 return {{oldFocus, newFocus, displayId, reason}};
189 }
190
dumpFocusedWindows() const191 std::string FocusResolver::dumpFocusedWindows() const {
192 if (mFocusedWindowTokenByDisplay.empty()) {
193 return INDENT "FocusedWindows: <none>\n";
194 }
195
196 std::string dump;
197 dump += INDENT "FocusedWindows:\n";
198 for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
199 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
200 namedToken.first.c_str());
201 }
202 return dump;
203 }
204
dump() const205 std::string FocusResolver::dump() const {
206 std::string dump = dumpFocusedWindows();
207 if (mFocusRequestByDisplay.empty()) {
208 return dump + INDENT "FocusRequests: <none>\n";
209 }
210
211 dump += INDENT "FocusRequests:\n";
212 for (const auto& [displayId, request] : mFocusRequestByDisplay) {
213 auto it = mLastFocusResultByDisplay.find(displayId);
214 std::string result =
215 it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
216 dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
217 displayId, request.windowName.c_str(), result.c_str());
218 }
219 return dump;
220 }
221
displayRemoved(int32_t displayId)222 void FocusResolver::displayRemoved(int32_t displayId) {
223 mFocusRequestByDisplay.erase(displayId);
224 mLastFocusResultByDisplay.erase(displayId);
225 }
226
227 } // namespace android::inputdispatcher
228