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(ui::LogicalDisplayId displayId) const44 sp<IBinder> FocusResolver::getFocusedWindowToken(ui::LogicalDisplayId displayId) const {
45     auto it = mFocusedWindowTokenByDisplay.find(displayId);
46     return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
47 }
48 
getFocusRequest(ui::LogicalDisplayId displayId)49 std::optional<FocusRequest> FocusResolver::getFocusRequest(ui::LogicalDisplayId 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(ui::LogicalDisplayId displayId,const std::vector<sp<WindowInfoHandle>> & windows)60 std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
61         ui::LogicalDisplayId 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 ui::LogicalDisplayId displayId = ui::LogicalDisplayId{request.displayId};
98     const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
99     if (currentFocus == request.token) {
100         ALOGD_IF(DEBUG_FOCUS, "setFocusedWindow %s on display %s ignored, reason: already focused",
101                  request.windowName.c_str(), displayId.toString().c_str());
102         return std::nullopt;
103     }
104 
105     sp<WindowInfoHandle> resolvedFocusWindow;
106     Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
107     // Update focus request. The focus resolver will always try to handle this request if there is
108     // no focused window on the display.
109     mFocusRequestByDisplay[displayId] = request;
110     mLastFocusResultByDisplay[displayId] = result;
111 
112     if (result == Focusability::OK) {
113         LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
114                             "Focused window should be non-null when result is OK!");
115         return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(),
116                                    resolvedFocusWindow->getName());
117     }
118 
119     // The requested window is not currently focusable. Wait for the window to become focusable
120     // but remove focus from the current window so that input events can go into a pending queue
121     // and be sent to the window when it becomes focused.
122     return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
123                                nullptr);
124 }
125 
getResolvedFocusWindow(const sp<IBinder> & token,const std::vector<sp<WindowInfoHandle>> & windows,sp<WindowInfoHandle> & outFocusableWindow)126 FocusResolver::Focusability FocusResolver::getResolvedFocusWindow(
127         const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
128         sp<WindowInfoHandle>& outFocusableWindow) {
129     sp<IBinder> curFocusCandidate = token;
130     bool focusedWindowFound = false;
131 
132     // Keep track of all windows reached to prevent a cyclical transferFocus request.
133     std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached;
134 
135     while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) {
136         tokensReached.emplace(curFocusCandidate);
137         Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow);
138         if (result == Focusability::OK) {
139             LOG_ALWAYS_FATAL_IF(!outFocusableWindow,
140                                 "Focused window should be non-null when result is OK!");
141             focusedWindowFound = true;
142             // outFocusableWindow has been updated by isTokenFocusable to contain
143             // the window info for curFocusCandidate. See if we can grant focus
144             // to the token that it wants to transfer its focus to.
145             curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget;
146         }
147 
148         // If the initial token is not focusable, return early with the failed result.
149         if (!focusedWindowFound) {
150             return result;
151         }
152     }
153 
154     return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW;
155 }
156 
isTokenFocusable(const sp<IBinder> & token,const std::vector<sp<WindowInfoHandle>> & windows,sp<WindowInfoHandle> & outFocusableWindow)157 FocusResolver::Focusability FocusResolver::isTokenFocusable(
158         const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
159         sp<WindowInfoHandle>& outFocusableWindow) {
160     bool allWindowsAreFocusable = true;
161     bool windowFound = false;
162     sp<WindowInfoHandle> visibleWindowHandle = nullptr;
163     for (const sp<WindowInfoHandle>& window : windows) {
164         if (window->getToken() != token) {
165             continue;
166         }
167         windowFound = true;
168         if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
169             // Check if at least a single window is visible.
170             visibleWindowHandle = window;
171         }
172         if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
173             // Check if all windows with the window token are focusable.
174             allWindowsAreFocusable = false;
175             break;
176         }
177     }
178 
179     if (!windowFound) {
180         return Focusability::NO_WINDOW;
181     }
182     if (!allWindowsAreFocusable) {
183         return Focusability::NOT_FOCUSABLE;
184     }
185     if (!visibleWindowHandle) {
186         return Focusability::NOT_VISIBLE;
187     }
188 
189     // Only set the outFoundWindow if the window can be focused
190     outFocusableWindow = visibleWindowHandle;
191     return Focusability::OK;
192 }
193 
updateFocusedWindow(ui::LogicalDisplayId displayId,const std::string & reason,const sp<IBinder> & newFocus,const std::string & tokenName)194 std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
195         ui::LogicalDisplayId displayId, const std::string& reason, const sp<IBinder>& newFocus,
196         const std::string& tokenName) {
197     sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
198     if (newFocus == oldFocus) {
199         return std::nullopt;
200     }
201     if (newFocus) {
202         mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
203     } else {
204         mFocusedWindowTokenByDisplay.erase(displayId);
205     }
206 
207     return {{oldFocus, newFocus, displayId, reason}};
208 }
209 
dumpFocusedWindows() const210 std::string FocusResolver::dumpFocusedWindows() const {
211     if (mFocusedWindowTokenByDisplay.empty()) {
212         return INDENT "FocusedWindows: <none>\n";
213     }
214 
215     std::string dump;
216     dump += INDENT "FocusedWindows:\n";
217     for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
218         dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s'\n",
219                                    displayId.toString().c_str(), namedToken.first.c_str());
220     }
221     return dump;
222 }
223 
dump() const224 std::string FocusResolver::dump() const {
225     std::string dump = dumpFocusedWindows();
226     if (mFocusRequestByDisplay.empty()) {
227         return dump + INDENT "FocusRequests: <none>\n";
228     }
229 
230     dump += INDENT "FocusRequests:\n";
231     for (const auto& [displayId, request] : mFocusRequestByDisplay) {
232         auto it = mLastFocusResultByDisplay.find(displayId);
233         std::string result =
234                 it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
235         dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s' result='%s'\n",
236                                    displayId.toString().c_str(), request.windowName.c_str(),
237                                    result.c_str());
238     }
239     return dump;
240 }
241 
displayRemoved(ui::LogicalDisplayId displayId)242 void FocusResolver::displayRemoved(ui::LogicalDisplayId displayId) {
243     mFocusRequestByDisplay.erase(displayId);
244     mLastFocusResultByDisplay.erase(displayId);
245 }
246 
247 } // namespace android::inputdispatcher
248