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 #include <gtest/gtest.h>
18
19 #include "../FocusResolver.h"
20
21 #define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
22 { \
23 ASSERT_EQ(_oldFocus, _changes->oldFocus); \
24 ASSERT_EQ(_newFocus, _changes->newFocus); \
25 }
26
27 // atest inputflinger_tests:FocusResolverTest
28
29 using android::gui::FocusRequest;
30 using android::gui::WindowInfoHandle;
31
32 namespace android::inputdispatcher {
33
34 class FakeWindowHandle : public WindowInfoHandle {
35 public:
FakeWindowHandle(const std::string & name,const sp<IBinder> & token,bool focusable,bool visible)36 FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
37 bool visible) {
38 mInfo.token = token;
39 mInfo.name = name;
40 mInfo.visible = visible;
41 mInfo.focusable = focusable;
42 }
43
setFocusable(bool focusable)44 void setFocusable(bool focusable) { mInfo.focusable = focusable; }
setVisible(bool visible)45 void setVisible(bool visible) { mInfo.visible = visible; }
46 };
47
TEST(FocusResolverTest,SetFocusedWindow)48 TEST(FocusResolverTest, SetFocusedWindow) {
49 sp<IBinder> focusableWindowToken = new BBinder();
50 sp<IBinder> invisibleWindowToken = new BBinder();
51 sp<IBinder> unfocusableWindowToken = new BBinder();
52 std::vector<sp<WindowInfoHandle>> windows;
53 windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
54 true /* visible */));
55 windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
56 false /* visible */));
57 windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
58 false /* focusable */, true /* visible */));
59
60 // focusable window can get focused
61 FocusRequest request;
62 request.displayId = 42;
63 request.token = focusableWindowToken;
64 FocusResolver focusResolver;
65 std::optional<FocusResolver::FocusChanges> changes =
66 focusResolver.setFocusedWindow(request, windows);
67 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
68 ASSERT_EQ(request.displayId, changes->displayId);
69
70 // invisible window cannot get focused
71 request.token = invisibleWindowToken;
72 changes = focusResolver.setFocusedWindow(request, windows);
73 ASSERT_EQ(focusableWindowToken, changes->oldFocus);
74 ASSERT_EQ(nullptr, changes->newFocus);
75 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
76
77 // unfocusableWindowToken window cannot get focused
78 request.token = unfocusableWindowToken;
79 changes = focusResolver.setFocusedWindow(request, windows);
80 ASSERT_FALSE(changes);
81 }
82
TEST(FocusResolverTest,SetFocusedMirroredWindow)83 TEST(FocusResolverTest, SetFocusedMirroredWindow) {
84 sp<IBinder> focusableWindowToken = new BBinder();
85 sp<IBinder> invisibleWindowToken = new BBinder();
86 sp<IBinder> unfocusableWindowToken = new BBinder();
87 std::vector<sp<WindowInfoHandle>> windows;
88 windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
89 true /* visible */));
90 windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
91 true /* visible */));
92
93 windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
94 true /* focusable */, true /* visible */));
95 windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
96 true /* focusable */, false /* visible */));
97
98 windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
99 true /* focusable */, true /* visible */));
100 windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
101 false /* focusable */, true /* visible */));
102
103 // mirrored window can get focused
104 FocusRequest request;
105 request.displayId = 42;
106 request.token = focusableWindowToken;
107 FocusResolver focusResolver;
108 std::optional<FocusResolver::FocusChanges> changes =
109 focusResolver.setFocusedWindow(request, windows);
110 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
111
112 // mirrored window with one visible window can get focused
113 request.token = invisibleWindowToken;
114 changes = focusResolver.setFocusedWindow(request, windows);
115 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
116
117 // mirrored window with one or more unfocusable window cannot get focused
118 request.token = unfocusableWindowToken;
119 changes = focusResolver.setFocusedWindow(request, windows);
120 ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
121 }
122
TEST(FocusResolverTest,SetInputWindows)123 TEST(FocusResolverTest, SetInputWindows) {
124 sp<IBinder> focusableWindowToken = new BBinder();
125 std::vector<sp<WindowInfoHandle>> windows;
126 sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
127 true /* focusable */, true /* visible */);
128 windows.push_back(window);
129
130 // focusable window can get focused
131 FocusRequest request;
132 request.displayId = 42;
133 request.token = focusableWindowToken;
134 FocusResolver focusResolver;
135 std::optional<FocusResolver::FocusChanges> changes =
136 focusResolver.setFocusedWindow(request, windows);
137 ASSERT_EQ(focusableWindowToken, changes->newFocus);
138
139 // Window visibility changes and the window loses focus
140 window->setVisible(false);
141 changes = focusResolver.setInputWindows(request.displayId, windows);
142 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
143 }
144
TEST(FocusResolverTest,FocusRequestsCanBePending)145 TEST(FocusResolverTest, FocusRequestsCanBePending) {
146 sp<IBinder> invisibleWindowToken = new BBinder();
147 std::vector<sp<WindowInfoHandle>> windows;
148
149 sp<FakeWindowHandle> invisibleWindow =
150 new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
151 false /* visible */);
152 windows.push_back(invisibleWindow);
153
154 // invisible window cannot get focused
155 FocusRequest request;
156 request.displayId = 42;
157 request.token = invisibleWindowToken;
158 FocusResolver focusResolver;
159 std::optional<FocusResolver::FocusChanges> changes =
160 focusResolver.setFocusedWindow(request, windows);
161 ASSERT_FALSE(changes);
162
163 // Window visibility changes and the window gets focused
164 invisibleWindow->setVisible(true);
165 changes = focusResolver.setInputWindows(request.displayId, windows);
166 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
167 }
168
TEST(FocusResolverTest,FocusRequestsArePersistent)169 TEST(FocusResolverTest, FocusRequestsArePersistent) {
170 sp<IBinder> windowToken = new BBinder();
171 std::vector<sp<WindowInfoHandle>> windows;
172
173 sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
174 false /* focusable */, true /* visible */);
175 windows.push_back(window);
176
177 // non-focusable window cannot get focused
178 FocusRequest request;
179 request.displayId = 42;
180 request.token = windowToken;
181 FocusResolver focusResolver;
182 std::optional<FocusResolver::FocusChanges> changes =
183 focusResolver.setFocusedWindow(request, windows);
184 ASSERT_FALSE(changes);
185
186 // Focusability changes and the window gets focused
187 window->setFocusable(true);
188 changes = focusResolver.setInputWindows(request.displayId, windows);
189 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
190
191 // Visibility changes and the window loses focus
192 window->setVisible(false);
193 changes = focusResolver.setInputWindows(request.displayId, windows);
194 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
195
196 // Visibility changes and the window gets focused
197 window->setVisible(true);
198 changes = focusResolver.setInputWindows(request.displayId, windows);
199 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
200
201 // Window is gone and the window loses focus
202 changes = focusResolver.setInputWindows(request.displayId, {});
203 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
204
205 // Window returns and the window gains focus
206 changes = focusResolver.setInputWindows(request.displayId, windows);
207 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
208 }
209
TEST(FocusResolverTest,ConditionalFocusRequestsAreNotPersistent)210 TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
211 sp<IBinder> hostWindowToken = new BBinder();
212 std::vector<sp<WindowInfoHandle>> windows;
213
214 sp<FakeWindowHandle> hostWindow =
215 new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
216 true /* visible */);
217 windows.push_back(hostWindow);
218 sp<IBinder> embeddedWindowToken = new BBinder();
219 sp<FakeWindowHandle> embeddedWindow =
220 new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
221 true /* visible */);
222 windows.push_back(embeddedWindow);
223
224 FocusRequest request;
225 request.displayId = 42;
226 request.token = hostWindowToken;
227 FocusResolver focusResolver;
228 std::optional<FocusResolver::FocusChanges> changes =
229 focusResolver.setFocusedWindow(request, windows);
230 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
231
232 request.focusedToken = hostWindow->getToken();
233 request.token = embeddedWindowToken;
234 changes = focusResolver.setFocusedWindow(request, windows);
235 ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
236
237 embeddedWindow->setFocusable(false);
238 changes = focusResolver.setInputWindows(request.displayId, windows);
239 // The embedded window is no longer focusable, provide focus back to the original focused
240 // window.
241 ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
242
243 embeddedWindow->setFocusable(true);
244 changes = focusResolver.setInputWindows(request.displayId, windows);
245 // The embedded window is focusable again, but we it cannot gain focus unless there is another
246 // focus request.
247 ASSERT_FALSE(changes);
248
249 embeddedWindow->setVisible(false);
250 changes = focusResolver.setFocusedWindow(request, windows);
251 // If the embedded window is not visible/focusable, then we do not grant it focus and the
252 // request is dropped.
253 ASSERT_FALSE(changes);
254
255 embeddedWindow->setVisible(true);
256 changes = focusResolver.setInputWindows(request.displayId, windows);
257 // If the embedded window becomes visble/focusable, nothing changes since the request has been
258 // dropped.
259 ASSERT_FALSE(changes);
260 }
TEST(FocusResolverTest,FocusRequestsAreClearedWhenWindowIsRemoved)261 TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
262 sp<IBinder> windowToken = new BBinder();
263 std::vector<sp<WindowInfoHandle>> windows;
264
265 sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
266 true /* focusable */, true /* visible */);
267 windows.push_back(window);
268
269 FocusRequest request;
270 request.displayId = 42;
271 request.token = windowToken;
272 FocusResolver focusResolver;
273 std::optional<FocusResolver::FocusChanges> changes =
274 focusResolver.setFocusedWindow(request, windows);
275 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
276 ASSERT_EQ(request.displayId, changes->displayId);
277
278 // Start with a focused window
279 window->setFocusable(true);
280 changes = focusResolver.setInputWindows(request.displayId, windows);
281 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
282
283 // When a display is removed, all windows are removed from the display
284 // and our focused window loses focus
285 changes = focusResolver.setInputWindows(request.displayId, {});
286 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
287 focusResolver.displayRemoved(request.displayId);
288
289 // When a display is readded, the window does not get focus since the request was cleared.
290 changes = focusResolver.setInputWindows(request.displayId, windows);
291 ASSERT_FALSE(changes);
292 }
293
294 } // namespace android::inputdispatcher
295