blob: b374fad4bceba965472c5593d83059f016e047cd [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 */
Chavi Weingarten847e8512023-03-29 00:26:09 +000016#include <optional>
Arthur Hungb060def2022-05-26 07:41:33 +000017#define LOG_TAG "InputDispatcher"
Vishnu Nairc519ff72021-01-21 08:23:08 -080018#define ATRACE_TAG ATRACE_TAG_INPUT
19
20#define INDENT " "
21#define INDENT2 " "
22
Vishnu Nairc519ff72021-01-21 08:23:08 -080023#include <inttypes.h>
24
25#include <android-base/stringprintf.h>
26#include <binder/Binder.h>
Dominik Laskowski75788452021-02-09 18:51:25 -080027#include <ftl/enum.h>
chaviw98318de2021-05-19 16:45:23 -050028#include <gui/WindowInfo.h>
Chavi Weingarten847e8512023-03-29 00:26:09 +000029#include <unordered_set>
Vishnu Nairc519ff72021-01-21 08:23:08 -080030
Arthur Hungb060def2022-05-26 07:41:33 +000031#include "DebugConfig.h"
Vishnu Nairc519ff72021-01-21 08:23:08 -080032#include "FocusResolver.h"
33
chaviw98318de2021-05-19 16:45:23 -050034using android::gui::FocusRequest;
35using android::gui::WindowInfoHandle;
36
Vishnu Nairc519ff72021-01-21 08:23:08 -080037namespace android::inputdispatcher {
38
Chavi Weingarten847e8512023-03-29 00:26:09 +000039template <typename T>
40struct SpHash {
41 size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
42};
43
Linnan Li13bf76a2024-05-05 19:18:02 +080044sp<IBinder> FocusResolver::getFocusedWindowToken(ui::LogicalDisplayId displayId) const {
Vishnu Nairc519ff72021-01-21 08:23:08 -080045 auto it = mFocusedWindowTokenByDisplay.find(displayId);
46 return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
47}
48
Linnan Li13bf76a2024-05-05 19:18:02 +080049std::optional<FocusRequest> FocusResolver::getFocusRequest(ui::LogicalDisplayId displayId) {
Vishnu Nair1dcad982021-02-24 14:38:25 -080050 auto it = mFocusRequestByDisplay.find(displayId);
51 return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
Vishnu Nairc519ff72021-01-21 08:23:08 -080052}
53
Vishnu Nair1dcad982021-02-24 14:38:25 -080054/**
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 */
Vishnu Nairc519ff72021-01-21 08:23:08 -080060std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
Linnan Li13bf76a2024-05-05 19:18:02 +080061 ui::LogicalDisplayId displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
Vishnu Nair1dcad982021-02-24 14:38:25 -080062 std::string removeFocusReason;
63
Vishnu Nair1dcad982021-02-24 14:38:25 -080064 const std::optional<FocusRequest> request = getFocusRequest(displayId);
Chavi Weingarten847e8512023-03-29 00:26:09 +000065 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.
Vishnu Nair1dcad982021-02-24 14:38:25 -080069 if (request) {
70 sp<IBinder> requestedFocus = request->token;
Chavi Weingarten847e8512023-03-29 00:26:09 +000071 sp<WindowInfoHandle> resolvedFocusWindow;
72 Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
73 if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
74 return std::nullopt;
75 }
Vishnu Nair1dcad982021-02-24 14:38:25 -080076 const Focusability previousResult = mLastFocusResultByDisplay[displayId];
77 mLastFocusResultByDisplay[displayId] = result;
78 if (result == Focusability::OK) {
Chavi Weingarten847e8512023-03-29 00:26:09 +000079 LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
80 "Focused window should be non-null when result is OK!");
Vishnu Nair1dcad982021-02-24 14:38:25 -080081 return updateFocusedWindow(displayId,
82 "Window became focusable. Previous reason: " +
Dominik Laskowski75788452021-02-09 18:51:25 -080083 ftl::enum_string(previousResult),
Chavi Weingarten847e8512023-03-29 00:26:09 +000084 resolvedFocusWindow->getToken(),
85 resolvedFocusWindow->getName());
Vishnu Nairc519ff72021-01-21 08:23:08 -080086 }
Chavi Weingarten847e8512023-03-29 00:26:09 +000087 removeFocusReason = ftl::enum_string(result);
Vishnu Nairc519ff72021-01-21 08:23:08 -080088 }
89
Vishnu Nair1dcad982021-02-24 14:38:25 -080090 // 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);
Vishnu Nairc519ff72021-01-21 08:23:08 -080093}
94
95std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
chaviw98318de2021-05-19 16:45:23 -050096 const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
Linnan Li13bf76a2024-05-05 19:18:02 +080097 const ui::LogicalDisplayId displayId = ui::LogicalDisplayId{request.displayId};
Vishnu Nairc519ff72021-01-21 08:23:08 -080098 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
Vishnu Nairc519ff72021-01-21 08:23:08 -080099 if (currentFocus == request.token) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800100 ALOGD_IF(DEBUG_FOCUS, "setFocusedWindow %s on display %s ignored, reason: already focused",
101 request.windowName.c_str(), displayId.toString().c_str());
Vishnu Nairc519ff72021-01-21 08:23:08 -0800102 return std::nullopt;
103 }
104
Chavi Weingarten847e8512023-03-29 00:26:09 +0000105 sp<WindowInfoHandle> resolvedFocusWindow;
106 Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
Vishnu Nair1dcad982021-02-24 14:38:25 -0800107 // 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) {
Chavi Weingarten847e8512023-03-29 00:26:09 +0000113 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());
Vishnu Nair1dcad982021-02-24 14:38:25 -0800117 }
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.
Dominik Laskowski75788452021-02-09 18:51:25 -0800122 return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
Vishnu Nair1dcad982021-02-24 14:38:25 -0800123 nullptr);
Vishnu Nairc519ff72021-01-21 08:23:08 -0800124}
125
Chavi Weingarten847e8512023-03-29 00:26:09 +0000126FocusResolver::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
Vishnu Nair1dcad982021-02-24 14:38:25 -0800157FocusResolver::Focusability FocusResolver::isTokenFocusable(
Chavi Weingarten847e8512023-03-29 00:26:09 +0000158 const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
159 sp<WindowInfoHandle>& outFocusableWindow) {
Vishnu Nairc519ff72021-01-21 08:23:08 -0800160 bool allWindowsAreFocusable = true;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800161 bool windowFound = false;
Chavi Weingarten847e8512023-03-29 00:26:09 +0000162 sp<WindowInfoHandle> visibleWindowHandle = nullptr;
chaviw98318de2021-05-19 16:45:23 -0500163 for (const sp<WindowInfoHandle>& window : windows) {
Vishnu Nairc519ff72021-01-21 08:23:08 -0800164 if (window->getToken() != token) {
165 continue;
166 }
167 windowFound = true;
Prabir Pradhan4d5c52f2022-01-31 08:52:10 -0800168 if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
Vishnu Nairc519ff72021-01-21 08:23:08 -0800169 // Check if at least a single window is visible.
Chavi Weingarten847e8512023-03-29 00:26:09 +0000170 visibleWindowHandle = window;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800171 }
Prabir Pradhan4d5c52f2022-01-31 08:52:10 -0800172 if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
Vishnu Nairc519ff72021-01-21 08:23:08 -0800173 // Check if all windows with the window token are focusable.
174 allWindowsAreFocusable = false;
175 break;
176 }
177 }
178
179 if (!windowFound) {
Vishnu Nair1dcad982021-02-24 14:38:25 -0800180 return Focusability::NO_WINDOW;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800181 }
182 if (!allWindowsAreFocusable) {
Vishnu Nair1dcad982021-02-24 14:38:25 -0800183 return Focusability::NOT_FOCUSABLE;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800184 }
Chavi Weingarten847e8512023-03-29 00:26:09 +0000185 if (!visibleWindowHandle) {
Vishnu Nair1dcad982021-02-24 14:38:25 -0800186 return Focusability::NOT_VISIBLE;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800187 }
188
Chavi Weingarten847e8512023-03-29 00:26:09 +0000189 // Only set the outFoundWindow if the window can be focused
190 outFocusableWindow = visibleWindowHandle;
Vishnu Nair1dcad982021-02-24 14:38:25 -0800191 return Focusability::OK;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800192}
193
194std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
Linnan Li13bf76a2024-05-05 19:18:02 +0800195 ui::LogicalDisplayId displayId, const std::string& reason, const sp<IBinder>& newFocus,
Vishnu Nairc519ff72021-01-21 08:23:08 -0800196 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
210std::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) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800218 dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s'\n",
219 displayId.toString().c_str(), namedToken.first.c_str());
Vishnu Nairc519ff72021-01-21 08:23:08 -0800220 }
221 return dump;
222}
223
224std::string FocusResolver::dump() const {
225 std::string dump = dumpFocusedWindows();
Vishnu Nair1dcad982021-02-24 14:38:25 -0800226 if (mFocusRequestByDisplay.empty()) {
227 return dump + INDENT "FocusRequests: <none>\n";
Vishnu Nairc519ff72021-01-21 08:23:08 -0800228 }
229
Vishnu Nair1dcad982021-02-24 14:38:25 -0800230 dump += INDENT "FocusRequests:\n";
231 for (const auto& [displayId, request] : mFocusRequestByDisplay) {
232 auto it = mLastFocusResultByDisplay.find(displayId);
233 std::string result =
Dominik Laskowski75788452021-02-09 18:51:25 -0800234 it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
Linnan Li13bf76a2024-05-05 19:18:02 +0800235 dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s' result='%s'\n",
236 displayId.toString().c_str(), request.windowName.c_str(),
237 result.c_str());
Vishnu Nairc519ff72021-01-21 08:23:08 -0800238 }
239 return dump;
240}
241
Linnan Li13bf76a2024-05-05 19:18:02 +0800242void FocusResolver::displayRemoved(ui::LogicalDisplayId displayId) {
Vishnu Nair599f1412021-06-21 10:39:58 -0700243 mFocusRequestByDisplay.erase(displayId);
244 mLastFocusResultByDisplay.erase(displayId);
245}
246
Vishnu Nairc519ff72021-01-21 08:23:08 -0800247} // namespace android::inputdispatcher