1 /*
2  * Copyright (C) 2020 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 #define LOG_TAG "TouchSpotController"
18 
19 // Log debug messages about pointer updates
20 #define DEBUG_SPOT_UPDATES 0
21 
22 #include "TouchSpotController.h"
23 
24 #include <android-base/stringprintf.h>
25 #include <input/PrintTools.h>
26 #include <log/log.h>
27 
28 #include <mutex>
29 
30 #define INDENT "  "
31 #define INDENT2 "    "
32 
33 namespace {
34 // Time to spend fading out the spot completely.
35 const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
36 } // namespace
37 
38 namespace android {
39 
40 // --- Spot ---
41 
updateSprite(const SpriteIcon * icon,float newX,float newY,ui::LogicalDisplayId displayId,bool skipScreenshot)42 void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float newX, float newY,
43                                              ui::LogicalDisplayId displayId, bool skipScreenshot) {
44     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
45     sprite->setAlpha(alpha);
46     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
47     sprite->setPosition(newX, newY);
48     sprite->setDisplayId(displayId);
49     sprite->setSkipScreenshot(skipScreenshot);
50     x = newX;
51     y = newY;
52 
53     if (icon != mLastIcon) {
54         mLastIcon = icon;
55         if (icon) {
56             sprite->setIcon(*icon);
57             sprite->setVisible(true);
58         } else {
59             sprite->setVisible(false);
60         }
61     }
62 }
63 
dump(std::string & out,const char * prefix) const64 void TouchSpotController::Spot::dump(std::string& out, const char* prefix) const {
65     out += prefix;
66     base::StringAppendF(&out, "Spot{id=%" PRIx32 ", alpha=%f, scale=%f, pos=[%f, %f]}\n", id, alpha,
67                         scale, x, y);
68 }
69 
70 // --- TouchSpotController ---
71 
TouchSpotController(ui::LogicalDisplayId displayId,PointerControllerContext & context)72 TouchSpotController::TouchSpotController(ui::LogicalDisplayId displayId,
73                                          PointerControllerContext& context)
74       : mDisplayId(displayId), mContext(context) {
75     mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
76 }
77 
~TouchSpotController()78 TouchSpotController::~TouchSpotController() {
79     std::scoped_lock lock(mLock);
80 
81     size_t numSpots = mLocked.displaySpots.size();
82     for (size_t i = 0; i < numSpots; i++) {
83         delete mLocked.displaySpots[i];
84     }
85     mLocked.displaySpots.clear();
86 }
87 
setSpots(const PointerCoords * spotCoords,const uint32_t * spotIdToIndex,BitSet32 spotIdBits,bool skipScreenshot)88 void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
89                                    BitSet32 spotIdBits, bool skipScreenshot) {
90 #if DEBUG_SPOT_UPDATES
91     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
92     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
93         uint32_t id = idBits.firstMarkedBit();
94         idBits.clearBit(id);
95         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
96         ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
97               c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
98               c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), mDisplayId.id);
99     }
100 #endif
101 
102     std::scoped_lock lock(mLock);
103     auto& spriteController = mContext.getSpriteController();
104     spriteController.openTransaction();
105 
106     // Add or move spots for fingers that are down.
107     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
108         uint32_t id = idBits.clearFirstMarkedBit();
109         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
110         const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
111                 ? mResources.spotTouch
112                 : mResources.spotHover;
113         float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
114         float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
115 
116         Spot* spot = getSpot(id, mLocked.displaySpots);
117         if (!spot) {
118             spot = createAndAddSpotLocked(id, mLocked.displaySpots);
119         }
120 
121         spot->updateSprite(&icon, x, y, mDisplayId, skipScreenshot);
122     }
123 
124     for (Spot* spot : mLocked.displaySpots) {
125         if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
126             fadeOutAndReleaseSpotLocked(spot);
127         }
128     }
129 
130     spriteController.closeTransaction();
131 }
132 
clearSpots()133 void TouchSpotController::clearSpots() {
134 #if DEBUG_SPOT_UPDATES
135     ALOGD("clearSpots");
136 #endif
137 
138     std::scoped_lock lock(mLock);
139     fadeOutAndReleaseAllSpotsLocked();
140 }
141 
getSpot(uint32_t id,const std::vector<Spot * > & spots)142 TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
143                                                         const std::vector<Spot*>& spots) {
144     for (size_t i = 0; i < spots.size(); i++) {
145         Spot* spot = spots[i];
146         if (spot->id == id) {
147             return spot;
148         }
149     }
150     return nullptr;
151 }
152 
createAndAddSpotLocked(uint32_t id,std::vector<Spot * > & spots)153 TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
154                                                                        std::vector<Spot*>& spots)
155         REQUIRES(mLock) {
156     // Remove spots until we have fewer than MAX_SPOTS remaining.
157     while (spots.size() >= MAX_SPOTS) {
158         Spot* spot = removeFirstFadingSpotLocked(spots);
159         if (!spot) {
160             spot = spots[0];
161             spots.erase(spots.begin());
162         }
163         releaseSpotLocked(spot);
164     }
165 
166     // Obtain a sprite from the recycled pool.
167     sp<Sprite> sprite;
168     if (!mLocked.recycledSprites.empty()) {
169         sprite = mLocked.recycledSprites.back();
170         mLocked.recycledSprites.pop_back();
171     } else {
172         sprite = mContext.getSpriteController().createSprite();
173     }
174 
175     // Return the new spot.
176     Spot* spot = new Spot(id, sprite);
177     spots.push_back(spot);
178     return spot;
179 }
180 
removeFirstFadingSpotLocked(std::vector<Spot * > & spots)181 TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
182         std::vector<Spot*>& spots) REQUIRES(mLock) {
183     for (size_t i = 0; i < spots.size(); i++) {
184         Spot* spot = spots[i];
185         if (spot->id == Spot::INVALID_ID) {
186             spots.erase(spots.begin() + i);
187             return spot;
188         }
189     }
190     return NULL;
191 }
192 
releaseSpotLocked(Spot * spot)193 void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
194     spot->sprite->clearIcon();
195 
196     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
197         mLocked.recycledSprites.push_back(spot->sprite);
198     }
199     delete spot;
200 }
201 
fadeOutAndReleaseSpotLocked(Spot * spot)202 void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
203     if (spot->id != Spot::INVALID_ID) {
204         spot->id = Spot::INVALID_ID;
205         startAnimationLocked();
206     }
207 }
208 
fadeOutAndReleaseAllSpotsLocked()209 void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
210     size_t numSpots = mLocked.displaySpots.size();
211     for (size_t i = 0; i < numSpots; i++) {
212         Spot* spot = mLocked.displaySpots[i];
213         fadeOutAndReleaseSpotLocked(spot);
214     }
215 }
216 
reloadSpotResources()217 void TouchSpotController::reloadSpotResources() {
218     mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
219 }
220 
doAnimations(nsecs_t timestamp)221 bool TouchSpotController::doAnimations(nsecs_t timestamp) {
222     std::scoped_lock lock(mLock);
223     bool keepAnimating = doFadingAnimationLocked(timestamp);
224     if (!keepAnimating) {
225         /*
226          * We know that this callback will be removed before another
227          * is added. mLock in PointerAnimator will not be released
228          * until after this is removed, and adding another callback
229          * requires that lock. Thus it's safe to set mLocked.animating
230          * here.
231          */
232         mLocked.animating = false;
233     }
234     return keepAnimating;
235 }
236 
doFadingAnimationLocked(nsecs_t timestamp)237 bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
238     bool keepAnimating = false;
239     nsecs_t animationTime = mContext.getAnimationTime();
240     nsecs_t frameDelay = timestamp - animationTime;
241     size_t numSpots = mLocked.displaySpots.size();
242     for (size_t i = 0; i < numSpots;) {
243         Spot* spot = mLocked.displaySpots[i];
244         if (spot->id == Spot::INVALID_ID) {
245             spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
246             if (spot->alpha <= 0) {
247                 mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
248                 releaseSpotLocked(spot);
249                 numSpots--;
250                 continue;
251             } else {
252                 spot->sprite->setAlpha(spot->alpha);
253                 keepAnimating = true;
254             }
255         }
256         ++i;
257     }
258     return keepAnimating;
259 }
260 
startAnimationLocked()261 void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
262     using namespace std::placeholders;
263 
264     if (mLocked.animating) {
265         return;
266     }
267     mLocked.animating = true;
268 
269     std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1);
270     mContext.addAnimationCallback(mDisplayId, func);
271 }
272 
dump(std::string & out,const char * prefix) const273 void TouchSpotController::dump(std::string& out, const char* prefix) const {
274     using base::StringAppendF;
275     out += prefix;
276     out += "SpotController:\n";
277     out += prefix;
278     StringAppendF(&out, INDENT "DisplayId: %s\n", mDisplayId.toString().c_str());
279     std::scoped_lock lock(mLock);
280     out += prefix;
281     StringAppendF(&out, INDENT "Animating: %s\n", toString(mLocked.animating));
282     out += prefix;
283     out += INDENT "Spots:\n";
284     std::string spotPrefix = prefix;
285     spotPrefix += INDENT2;
286     for (const auto& spot : mLocked.displaySpots) {
287         spot->dump(out, spotPrefix.c_str());
288     }
289 }
290 
291 } // namespace android
292