• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                     [&registeredListener](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                     [&registeredListener](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