1 /*
2 * Copyright (C) 2019 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 <flag_macros.h>
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <input/PointerController.h>
21 #include <input/SpriteController.h>
22
23 #include <atomic>
24 #include <thread>
25
26 #include "input/Input.h"
27 #include "mocks/MockSprite.h"
28 #include "mocks/MockSpriteController.h"
29
30 namespace android {
31
32 enum TestCursorType {
33 CURSOR_TYPE_DEFAULT = 0,
34 CURSOR_TYPE_HOVER,
35 CURSOR_TYPE_TOUCH,
36 CURSOR_TYPE_ANCHOR,
37 CURSOR_TYPE_ADDITIONAL,
38 CURSOR_TYPE_ADDITIONAL_ANIM,
39 CURSOR_TYPE_STYLUS,
40 CURSOR_TYPE_CUSTOM = -1,
41 };
42
43 using ::testing::AllOf;
44 using ::testing::Field;
45 using ::testing::NiceMock;
46 using ::testing::Return;
47 using ::testing::Test;
48
getHotSpotCoordinatesForType(int32_t type)49 std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
50 return std::make_pair(type * 10, type * 10 + 5);
51 }
52
53 class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
54 public:
55 virtual void loadPointerIcon(SpriteIcon* icon, ui::LogicalDisplayId displayId) override;
56 virtual void loadPointerResources(PointerResources* outResources,
57 ui::LogicalDisplayId displayId) override;
58 virtual void loadAdditionalMouseResources(
59 std::map<PointerIconStyle, SpriteIcon>* outResources,
60 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
61 ui::LogicalDisplayId displayId) override;
62 virtual PointerIconStyle getDefaultPointerIconId() override;
63 virtual PointerIconStyle getDefaultStylusIconId() override;
64 virtual PointerIconStyle getCustomPointerIconId() override;
65
66 bool allResourcesAreLoaded();
67 bool noResourcesAreLoaded();
68
69 private:
70 void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
71
72 bool pointerIconLoaded{false};
73 bool pointerResourcesLoaded{false};
74 bool additionalMouseResourcesLoaded{false};
75 };
76
loadPointerIcon(SpriteIcon * icon,ui::LogicalDisplayId)77 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, ui::LogicalDisplayId) {
78 loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
79 pointerIconLoaded = true;
80 }
81
loadPointerResources(PointerResources * outResources,ui::LogicalDisplayId)82 void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
83 ui::LogicalDisplayId) {
84 loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
85 loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
86 loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
87 pointerResourcesLoaded = true;
88 }
89
loadAdditionalMouseResources(std::map<PointerIconStyle,SpriteIcon> * outResources,std::map<PointerIconStyle,PointerAnimation> * outAnimationResources,ui::LogicalDisplayId)90 void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
91 std::map<PointerIconStyle, SpriteIcon>* outResources,
92 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources, ui::LogicalDisplayId) {
93 SpriteIcon icon;
94 PointerAnimation anim;
95
96 // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
97 int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
98 loadPointerIconForType(&icon, cursorType);
99 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
100
101 // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
102 cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
103 loadPointerIconForType(&icon, cursorType);
104 anim.animationFrames.push_back(icon);
105 anim.durationPerFrame = 10;
106 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
107 (*outAnimationResources)[static_cast<PointerIconStyle>(cursorType)] = anim;
108
109 // CURSOR_TYPE_STYLUS doesn't have animation resource.
110 cursorType = CURSOR_TYPE_STYLUS;
111 loadPointerIconForType(&icon, cursorType);
112 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
113
114 additionalMouseResourcesLoaded = true;
115 }
116
getDefaultPointerIconId()117 PointerIconStyle MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
118 return static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT);
119 }
120
getDefaultStylusIconId()121 PointerIconStyle MockPointerControllerPolicyInterface::getDefaultStylusIconId() {
122 return static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS);
123 }
124
getCustomPointerIconId()125 PointerIconStyle MockPointerControllerPolicyInterface::getCustomPointerIconId() {
126 return static_cast<PointerIconStyle>(CURSOR_TYPE_CUSTOM);
127 }
128
allResourcesAreLoaded()129 bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
130 return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
131 }
132
noResourcesAreLoaded()133 bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
134 return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
135 }
136
loadPointerIconForType(SpriteIcon * icon,int32_t type)137 void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
138 icon->style = static_cast<PointerIconStyle>(type);
139 std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
140 icon->hotSpotX = hotSpot.first;
141 icon->hotSpotY = hotSpot.second;
142 }
143
144 class TestPointerController : public PointerController {
145 public:
TestPointerController(sp<android::gui::WindowInfosListener> & registeredListener,sp<PointerControllerPolicyInterface> policy,const sp<Looper> & looper,SpriteController & spriteController)146 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
147 sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper,
148 SpriteController& spriteController)
149 : PointerController(
150 policy, looper, spriteController,
151 [®isteredListener](const sp<android::gui::WindowInfosListener>& listener)
152 -> std::vector<gui::DisplayInfo> {
153 // Register listener
154 registeredListener = listener;
155 return {};
156 },
__anon9f87e01c0202(const sp<android::gui::WindowInfosListener>& listener) 157 [®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
158 // Unregister listener
159 if (registeredListener == listener) registeredListener = nullptr;
160 }) {}
~TestPointerController()161 ~TestPointerController() override {}
162 };
163
164 class PointerControllerTest : public Test {
165 private:
166 void loopThread();
167
168 std::atomic<bool> mRunning = true;
169 class MyLooper : public Looper {
170 public:
MyLooper()171 MyLooper() : Looper(false) {}
172 ~MyLooper() = default;
173 };
174
175 protected:
176 PointerControllerTest();
177 ~PointerControllerTest();
178
179 void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT);
180
181 sp<MockSprite> mPointerSprite;
182 sp<MockPointerControllerPolicyInterface> mPolicy;
183 std::unique_ptr<MockSpriteController> mSpriteController;
184 std::shared_ptr<PointerController> mPointerController;
185 sp<android::gui::WindowInfosListener> mRegisteredListener;
186 sp<MyLooper> mLooper;
187
188 private:
189 std::thread mThread;
190 };
191
PointerControllerTest()192 PointerControllerTest::PointerControllerTest()
193 : mPointerSprite(new NiceMock<MockSprite>),
194 mLooper(new MyLooper),
195 mThread(&PointerControllerTest::loopThread, this) {
196 mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
197 mPolicy = new MockPointerControllerPolicyInterface();
198
199 EXPECT_CALL(*mSpriteController, createSprite())
200 .WillOnce(Return(mPointerSprite));
201
202 mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy,
203 mLooper, *mSpriteController);
204 }
205
~PointerControllerTest()206 PointerControllerTest::~PointerControllerTest() {
207 mPointerController.reset();
208 mRunning.store(false, std::memory_order_relaxed);
209 mThread.join();
210 }
211
ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId)212 void PointerControllerTest::ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId) {
213 DisplayViewport viewport;
214 viewport.displayId = displayId;
215 viewport.logicalRight = 1600;
216 viewport.logicalBottom = 1200;
217 viewport.physicalRight = 800;
218 viewport.physicalBottom = 600;
219 viewport.deviceWidth = 400;
220 viewport.deviceHeight = 300;
221 mPointerController->setDisplayViewport(viewport);
222 }
223
loopThread()224 void PointerControllerTest::loopThread() {
225 Looper::setForThread(mLooper);
226
227 while (mRunning.load(std::memory_order_relaxed)) {
228 mLooper->pollOnce(100);
229 }
230 }
231
TEST_F(PointerControllerTest,useDefaultCursorTypeByDefault)232 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
233 ensureDisplayViewportIsSet();
234 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
235
236 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
237 EXPECT_CALL(*mPointerSprite, setVisible(true));
238 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
239 EXPECT_CALL(*mPointerSprite,
240 setIcon(AllOf(Field(&SpriteIcon::style,
241 static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
242 Field(&SpriteIcon::hotSpotX, hotspot.first),
243 Field(&SpriteIcon::hotSpotY, hotspot.second))));
244 mPointerController->reloadPointerResources();
245 }
246
TEST_F(PointerControllerTest,useStylusTypeForStylusHover)247 TEST_F(PointerControllerTest, useStylusTypeForStylusHover) {
248 ensureDisplayViewportIsSet();
249 mPointerController->setPresentation(PointerController::Presentation::STYLUS_HOVER);
250 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
251 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_STYLUS);
252 EXPECT_CALL(*mPointerSprite, setVisible(true));
253 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
254 EXPECT_CALL(*mPointerSprite,
255 setIcon(AllOf(Field(&SpriteIcon::style,
256 static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS)),
257 Field(&SpriteIcon::hotSpotX, hotspot.first),
258 Field(&SpriteIcon::hotSpotY, hotspot.second))));
259 mPointerController->reloadPointerResources();
260 }
261
TEST_F(PointerControllerTest,setPresentationBeforeDisplayViewportDoesNotLoadResources)262 TEST_F(PointerControllerTest, setPresentationBeforeDisplayViewportDoesNotLoadResources) {
263 // Setting the presentation mode before a display viewport is set will not load any resources.
264 mPointerController->setPresentation(PointerController::Presentation::POINTER);
265 ASSERT_TRUE(mPolicy->noResourcesAreLoaded());
266
267 // When the display is set, then the resources are loaded.
268 ensureDisplayViewportIsSet();
269 ASSERT_TRUE(mPolicy->allResourcesAreLoaded());
270 }
271
TEST_F(PointerControllerTest,updatePointerIconWithChoreographer)272 TEST_F(PointerControllerTest, updatePointerIconWithChoreographer) {
273 // When PointerChoreographer is enabled, the presentation mode is set before the viewport.
274 mPointerController->setPresentation(PointerController::Presentation::POINTER);
275 ensureDisplayViewportIsSet();
276 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
277
278 int32_t type = CURSOR_TYPE_ADDITIONAL;
279 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
280 EXPECT_CALL(*mPointerSprite, setVisible(true));
281 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
282 EXPECT_CALL(*mPointerSprite,
283 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
284 Field(&SpriteIcon::hotSpotX, hotspot.first),
285 Field(&SpriteIcon::hotSpotY, hotspot.second))));
286 mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
287 }
288
TEST_F(PointerControllerTest,setCustomPointerIcon)289 TEST_F(PointerControllerTest, setCustomPointerIcon) {
290 ensureDisplayViewportIsSet();
291 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
292
293 int32_t style = CURSOR_TYPE_CUSTOM;
294 float hotSpotX = 15;
295 float hotSpotY = 20;
296
297 SpriteIcon icon;
298 icon.style = static_cast<PointerIconStyle>(style);
299 icon.hotSpotX = hotSpotX;
300 icon.hotSpotY = hotSpotY;
301
302 EXPECT_CALL(*mPointerSprite, setVisible(true));
303 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
304 EXPECT_CALL(*mPointerSprite,
305 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
306 Field(&SpriteIcon::hotSpotX, hotSpotX),
307 Field(&SpriteIcon::hotSpotY, hotSpotY))));
308 mPointerController->setCustomPointerIcon(icon);
309 }
310
TEST_F(PointerControllerTest,doesNotGetResourcesBeforeSettingViewport)311 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
312 mPointerController->setPresentation(PointerController::Presentation::POINTER);
313 mPointerController->setPosition(1.0f, 1.0f);
314 mPointerController->move(1.0f, 1.0f);
315 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
316 mPointerController->fade(PointerController::Transition::IMMEDIATE);
317
318 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
319
320 ensureDisplayViewportIsSet();
321 }
322
TEST_F(PointerControllerTest,updatesSkipScreenshotFlagForTouchSpots)323 TEST_F(PointerControllerTest, updatesSkipScreenshotFlagForTouchSpots) {
324 ensureDisplayViewportIsSet();
325
326 PointerCoords testSpotCoords;
327 testSpotCoords.clear();
328 testSpotCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
329 testSpotCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
330 BitSet32 testIdBits;
331 testIdBits.markBit(0);
332 std::array<uint32_t, MAX_POINTER_ID + 1> testIdToIndex;
333
334 sp<MockSprite> testSpotSprite(new NiceMock<MockSprite>);
335
336 // By default sprite is not marked secure
337 EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testSpotSprite));
338 EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
339
340 // Update spots to sync state with sprite
341 mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
342 ui::LogicalDisplayId::DEFAULT);
343 testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
344
345 // Marking the display to skip screenshot should update sprite as well
346 mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT);
347 EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
348
349 // Update spots to sync state with sprite
350 mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
351 ui::LogicalDisplayId::DEFAULT);
352 testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
353
354 // Reset flag and verify again
355 mPointerController->clearSkipScreenshotFlags();
356 EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
357 mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
358 ui::LogicalDisplayId::DEFAULT);
359 testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
360 }
361
362 class PointerControllerSkipScreenshotFlagTest
363 : public PointerControllerTest,
364 public testing::WithParamInterface<PointerControllerInterface::ControllerType> {};
365
TEST_P(PointerControllerSkipScreenshotFlagTest,updatesSkipScreenshotFlag)366 TEST_P(PointerControllerSkipScreenshotFlagTest, updatesSkipScreenshotFlag) {
367 sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>);
368 EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite));
369
370 // Create a pointer controller
371 mPointerController =
372 PointerController::create(mPolicy, mLooper, *mSpriteController, GetParam());
373 ensureDisplayViewportIsSet(ui::LogicalDisplayId::DEFAULT);
374
375 // By default skip screenshot flag is not set for the sprite
376 EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false));
377
378 // Update pointer to sync state with sprite
379 mPointerController->setPosition(100, 100);
380 testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
381
382 // Marking the controller to skip screenshot should update pointer sprite
383 mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT);
384 EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(true));
385
386 // Update pointer to sync state with sprite
387 mPointerController->move(10, 10);
388 testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
389
390 // Reset flag and verify again
391 mPointerController->clearSkipScreenshotFlags();
392 EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false));
393 mPointerController->move(10, 10);
394 testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
395 }
396
397 INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest,
398 PointerControllerSkipScreenshotFlagTest,
399 testing::Values(PointerControllerInterface::ControllerType::MOUSE,
400 PointerControllerInterface::ControllerType::STYLUS));
401
402 class PointerControllerWindowInfoListenerTest : public Test {};
403
TEST_F(PointerControllerWindowInfoListenerTest,doesNotCrashIfListenerCalledAfterPointerControllerDestroyed)404 TEST_F(PointerControllerWindowInfoListenerTest,
405 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
406 sp<Looper> looper = new Looper(false);
407 auto spriteController = NiceMock<MockSpriteController>(looper);
408 sp<android::gui::WindowInfosListener> registeredListener;
409 sp<android::gui::WindowInfosListener> localListenerCopy;
410 sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface();
411 {
412 TestPointerController pointerController(registeredListener, policy, looper,
413 spriteController);
414 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
415 localListenerCopy = registeredListener;
416 }
417 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
418 localListenerCopy->onWindowInfosChanged({{}, {}, 0, 0});
419 }
420
421 } // namespace android
422