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