1 /*
2  * Copyright 2023 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 "PointerChoreographer"
18 
19 #include <android-base/logging.h>
20 #include <com_android_input_flags.h>
21 #if defined(__ANDROID__)
22 #include <gui/SurfaceComposerClient.h>
23 #endif
24 #include <input/Keyboard.h>
25 #include <input/PrintTools.h>
26 #include <unordered_set>
27 
28 #include "PointerChoreographer.h"
29 
30 #define INDENT "  "
31 
32 namespace android {
33 
34 namespace {
35 
isFromMouse(const NotifyMotionArgs & args)36 bool isFromMouse(const NotifyMotionArgs& args) {
37     return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
38             args.pointerProperties[0].toolType == ToolType::MOUSE;
39 }
40 
isFromTouchpad(const NotifyMotionArgs & args)41 bool isFromTouchpad(const NotifyMotionArgs& args) {
42     return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
43             args.pointerProperties[0].toolType == ToolType::FINGER;
44 }
45 
isFromDrawingTablet(const NotifyMotionArgs & args)46 bool isFromDrawingTablet(const NotifyMotionArgs& args) {
47     return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
48             isStylusToolType(args.pointerProperties[0].toolType);
49 }
50 
isHoverAction(int32_t action)51 bool isHoverAction(int32_t action) {
52     return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
53             action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
54 }
55 
isStylusHoverEvent(const NotifyMotionArgs & args)56 bool isStylusHoverEvent(const NotifyMotionArgs& args) {
57     return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
58 }
59 
isMouseOrTouchpad(uint32_t sources)60 bool isMouseOrTouchpad(uint32_t sources) {
61     // Check if this is a mouse or touchpad, but not a drawing tablet.
62     return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
63             (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
64              !isFromSource(sources, AINPUT_SOURCE_STYLUS));
65 }
66 
notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId,FloatPoint>> change,PointerChoreographerPolicyInterface & policy)67 inline void notifyPointerDisplayChange(
68         std::optional<std::tuple<ui::LogicalDisplayId, FloatPoint>> change,
69         PointerChoreographerPolicyInterface& policy) {
70     if (!change) {
71         return;
72     }
73     const auto& [displayId, cursorPosition] = *change;
74     policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
75 }
76 
setIconForController(const std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> & icon,PointerControllerInterface & controller)77 void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
78                           PointerControllerInterface& controller) {
79     if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
80         if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
81             LOG(FATAL) << "SpriteIcon should not be null";
82         }
83         controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
84     } else {
85         controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
86     }
87 }
88 
89 // filters and returns a set of privacy sensitive displays that are currently visible.
getPrivacySensitiveDisplaysFromWindowInfos(const std::vector<gui::WindowInfo> & windowInfos)90 std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
91         const std::vector<gui::WindowInfo>& windowInfos) {
92     std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
93     for (const auto& windowInfo : windowInfos) {
94         if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
95             windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
96             privacySensitiveDisplays.insert(windowInfo.displayId);
97         }
98     }
99     return privacySensitiveDisplays;
100 }
101 
102 } // namespace
103 
104 // --- PointerChoreographer ---
105 
PointerChoreographer(InputListenerInterface & inputListener,PointerChoreographerPolicyInterface & policy)106 PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
107                                            PointerChoreographerPolicyInterface& policy)
108       : PointerChoreographer(
109                 inputListener, policy,
110                 [](const sp<android::gui::WindowInfosListener>& listener) {
111                     auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
112                                                       std::vector<android::gui::DisplayInfo>{});
113 #if defined(__ANDROID__)
114                     SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
115                                                                                 &initialInfo);
116 #endif
117                     return initialInfo.first;
118                 },
__anonb85a99590302(const sp<android::gui::WindowInfosListener>& listener) 119                 [](const sp<android::gui::WindowInfosListener>& listener) {
120 #if defined(__ANDROID__)
121                     SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
122 #endif
123                 }) {
124 }
125 
PointerChoreographer(android::InputListenerInterface & listener,android::PointerChoreographerPolicyInterface & policy,const android::PointerChoreographer::WindowListenerRegisterConsumer & registerListener,const android::PointerChoreographer::WindowListenerUnregisterConsumer & unregisterListener)126 PointerChoreographer::PointerChoreographer(
127         android::InputListenerInterface& listener,
128         android::PointerChoreographerPolicyInterface& policy,
129         const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
130         const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
131       : mTouchControllerConstructor([this]() {
132             return mPolicy.createPointerController(
133                     PointerControllerInterface::ControllerType::TOUCH);
134         }),
135         mNextListener(listener),
136         mPolicy(policy),
137         mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
138         mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
139         mShowTouchesEnabled(false),
140         mStylusPointerIconEnabled(false),
141         mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
142         mRegisterListener(registerListener),
143         mUnregisterListener(unregisterListener) {}
144 
~PointerChoreographer()145 PointerChoreographer::~PointerChoreographer() {
146     std::scoped_lock _l(mLock);
147     if (mWindowInfoListener == nullptr) {
148         return;
149     }
150     mWindowInfoListener->onPointerChoreographerDestroyed();
151     mUnregisterListener(mWindowInfoListener);
152 }
153 
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)154 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
155     PointerDisplayChange pointerDisplayChange;
156 
157     { // acquire lock
158         std::scoped_lock _l(mLock);
159 
160         mInputDeviceInfos = args.inputDeviceInfos;
161         pointerDisplayChange = updatePointerControllersLocked();
162     } // release lock
163 
164     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
165     mNextListener.notify(args);
166 }
167 
notifyConfigurationChanged(const NotifyConfigurationChangedArgs & args)168 void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
169     mNextListener.notify(args);
170 }
171 
notifyKey(const NotifyKeyArgs & args)172 void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
173     fadeMouseCursorOnKeyPress(args);
174     mNextListener.notify(args);
175 }
176 
notifyMotion(const NotifyMotionArgs & args)177 void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
178     NotifyMotionArgs newArgs = processMotion(args);
179 
180     mNextListener.notify(newArgs);
181 }
182 
fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs & args)183 void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
184     if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
185         return;
186     }
187     // Meta state for these keys is ignored for dismissing cursor while typing
188     constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
189             AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
190     if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
191         // Do not fade if any other meta state is active
192         return;
193     }
194     if (!mPolicy.isInputMethodConnectionActive()) {
195         return;
196     }
197 
198     std::scoped_lock _l(mLock);
199     ui::LogicalDisplayId targetDisplay = args.displayId;
200     if (targetDisplay == ui::LogicalDisplayId::INVALID) {
201         targetDisplay = mCurrentFocusedDisplay;
202     }
203     auto it = mMousePointersByDisplay.find(targetDisplay);
204     if (it != mMousePointersByDisplay.end()) {
205         it->second->fade(PointerControllerInterface::Transition::GRADUAL);
206     }
207 }
208 
processMotion(const NotifyMotionArgs & args)209 NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
210     std::scoped_lock _l(mLock);
211 
212     if (isFromMouse(args)) {
213         return processMouseEventLocked(args);
214     } else if (isFromTouchpad(args)) {
215         return processTouchpadEventLocked(args);
216     } else if (isFromDrawingTablet(args)) {
217         processDrawingTabletEventLocked(args);
218     } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
219         processStylusHoverEventLocked(args);
220     } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
221         processTouchscreenAndStylusEventLocked(args);
222     }
223     return args;
224 }
225 
processMouseEventLocked(const NotifyMotionArgs & args)226 NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
227     if (args.getPointerCount() != 1) {
228         LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
229                    << args.dump();
230     }
231 
232     mMouseDevices.emplace(args.deviceId);
233     auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
234     NotifyMotionArgs newArgs(args);
235     newArgs.displayId = displayId;
236 
237     if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
238         // This is an absolute mouse device that knows about the location of the cursor on the
239         // display, so set the cursor position to the specified location.
240         const auto [x, y] = pc.getPosition();
241         const float deltaX = args.xCursorPosition - x;
242         const float deltaY = args.yCursorPosition - y;
243         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
244         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
245         pc.setPosition(args.xCursorPosition, args.yCursorPosition);
246     } else {
247         // This is a relative mouse, so move the cursor by the specified amount.
248         const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
249         const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
250         pc.move(deltaX, deltaY);
251         const auto [x, y] = pc.getPosition();
252         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
253         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
254         newArgs.xCursorPosition = x;
255         newArgs.yCursorPosition = y;
256     }
257     if (canUnfadeOnDisplay(displayId)) {
258         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
259     }
260     return newArgs;
261 }
262 
processTouchpadEventLocked(const NotifyMotionArgs & args)263 NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
264     mMouseDevices.emplace(args.deviceId);
265     auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
266 
267     NotifyMotionArgs newArgs(args);
268     newArgs.displayId = displayId;
269     if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
270         // This is a movement of the mouse pointer.
271         const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
272         const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
273         pc.move(deltaX, deltaY);
274         if (canUnfadeOnDisplay(displayId)) {
275             pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
276         }
277 
278         const auto [x, y] = pc.getPosition();
279         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
280         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
281         newArgs.xCursorPosition = x;
282         newArgs.yCursorPosition = y;
283     } else {
284         // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
285         if (canUnfadeOnDisplay(displayId)) {
286             pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
287         }
288 
289         const auto [x, y] = pc.getPosition();
290         for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
291             newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
292                                                   args.pointerCoords[i].getX() + x);
293             newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
294                                                   args.pointerCoords[i].getY() + y);
295         }
296         newArgs.xCursorPosition = x;
297         newArgs.yCursorPosition = y;
298     }
299     return newArgs;
300 }
301 
processDrawingTabletEventLocked(const android::NotifyMotionArgs & args)302 void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
303     if (args.displayId == ui::LogicalDisplayId::INVALID) {
304         return;
305     }
306 
307     if (args.getPointerCount() != 1) {
308         LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
309                      << args.dump();
310     }
311 
312     // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
313     auto [it, controllerAdded] =
314             mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
315                                                        getMouseControllerConstructor(
316                                                                args.displayId));
317     if (controllerAdded) {
318         onControllerAddedOrRemovedLocked();
319     }
320 
321     PointerControllerInterface& pc = *it->second;
322 
323     const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
324     const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
325     pc.setPosition(x, y);
326     if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
327         // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
328         //   immediately by a DOWN event.
329         pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
330         pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
331     } else if (canUnfadeOnDisplay(args.displayId)) {
332         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
333     }
334 }
335 
336 /**
337  * When screen is touched, fade the mouse pointer on that display. We only call fade for
338  * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
339  * mouse device keeps moving and unfades the cursor.
340  * For touch events, we do not need to populate the cursor position.
341  */
processTouchscreenAndStylusEventLocked(const NotifyMotionArgs & args)342 void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
343     if (!args.displayId.isValid()) {
344         return;
345     }
346 
347     if (const auto it = mMousePointersByDisplay.find(args.displayId);
348         it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
349         it->second->fade(PointerControllerInterface::Transition::GRADUAL);
350     }
351 
352     if (!mShowTouchesEnabled) {
353         return;
354     }
355 
356     // Get the touch pointer controller for the device, or create one if it doesn't exist.
357     auto [it, controllerAdded] =
358             mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
359     if (controllerAdded) {
360         onControllerAddedOrRemovedLocked();
361     }
362 
363     PointerControllerInterface& pc = *it->second;
364 
365     const PointerCoords* coords = args.pointerCoords.data();
366     const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
367     const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
368     std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
369     BitSet32 idBits;
370     if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
371         for (size_t i = 0; i < args.getPointerCount(); i++) {
372             if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
373                 continue;
374             }
375             uint32_t id = args.pointerProperties[i].id;
376             idToIndex[id] = i;
377             idBits.markBit(id);
378         }
379     }
380     // The PointerController already handles setting spots per-display, so
381     // we do not need to manually manage display changes for touch spots for now.
382     pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
383 }
384 
processStylusHoverEventLocked(const NotifyMotionArgs & args)385 void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
386     if (!args.displayId.isValid()) {
387         return;
388     }
389 
390     if (args.getPointerCount() != 1) {
391         LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
392                      << args.dump();
393     }
394 
395     // Get the stylus pointer controller for the device, or create one if it doesn't exist.
396     auto [it, controllerAdded] =
397             mStylusPointersByDevice.try_emplace(args.deviceId,
398                                                 getStylusControllerConstructor(args.displayId));
399     if (controllerAdded) {
400         onControllerAddedOrRemovedLocked();
401     }
402 
403     PointerControllerInterface& pc = *it->second;
404 
405     const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
406     const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
407     pc.setPosition(x, y);
408     if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
409         // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
410         //   immediately by a DOWN event.
411         pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
412         pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
413     } else if (canUnfadeOnDisplay(args.displayId)) {
414         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
415     }
416 }
417 
notifySwitch(const NotifySwitchArgs & args)418 void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
419     mNextListener.notify(args);
420 }
421 
notifySensor(const NotifySensorArgs & args)422 void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
423     mNextListener.notify(args);
424 }
425 
notifyVibratorState(const NotifyVibratorStateArgs & args)426 void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
427     mNextListener.notify(args);
428 }
429 
notifyDeviceReset(const NotifyDeviceResetArgs & args)430 void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
431     processDeviceReset(args);
432 
433     mNextListener.notify(args);
434 }
435 
processDeviceReset(const NotifyDeviceResetArgs & args)436 void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
437     std::scoped_lock _l(mLock);
438     mTouchPointersByDevice.erase(args.deviceId);
439     mStylusPointersByDevice.erase(args.deviceId);
440     mDrawingTabletPointersByDevice.erase(args.deviceId);
441     onControllerAddedOrRemovedLocked();
442 }
443 
onControllerAddedOrRemovedLocked()444 void PointerChoreographer::onControllerAddedOrRemovedLocked() {
445     if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
446         return;
447     }
448     bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
449             !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
450 
451     if (requireListener && mWindowInfoListener == nullptr) {
452         mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
453         mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
454         onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
455     } else if (!requireListener && mWindowInfoListener != nullptr) {
456         mUnregisterListener(mWindowInfoListener);
457         mWindowInfoListener = nullptr;
458     } else if (requireListener && mWindowInfoListener != nullptr) {
459         // controller may have been added to an existing privacy sensitive display, we need to
460         // update all controllers again
461         onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
462     }
463 }
464 
onPrivacySensitiveDisplaysChangedLocked(const std::unordered_set<ui::LogicalDisplayId> & privacySensitiveDisplays)465 void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
466         const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
467     for (auto& [_, pc] : mTouchPointersByDevice) {
468         pc->clearSkipScreenshotFlags();
469         for (auto displayId : privacySensitiveDisplays) {
470             pc->setSkipScreenshotFlagForDisplay(displayId);
471         }
472     }
473 
474     for (auto& [displayId, pc] : mMousePointersByDisplay) {
475         if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
476             pc->setSkipScreenshotFlagForDisplay(displayId);
477         } else {
478             pc->clearSkipScreenshotFlags();
479         }
480     }
481 
482     for (auto* pointerControllerByDevice :
483          {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
484         for (auto& [_, pc] : *pointerControllerByDevice) {
485             auto displayId = pc->getDisplayId();
486             if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
487                 pc->setSkipScreenshotFlagForDisplay(displayId);
488             } else {
489                 pc->clearSkipScreenshotFlags();
490             }
491         }
492     }
493 }
494 
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)495 void PointerChoreographer::notifyPointerCaptureChanged(
496         const NotifyPointerCaptureChangedArgs& args) {
497     if (args.request.isEnable()) {
498         std::scoped_lock _l(mLock);
499         for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
500             mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
501         }
502     }
503     mNextListener.notify(args);
504 }
505 
onPrivacySensitiveDisplaysChanged(const std::unordered_set<ui::LogicalDisplayId> & privacySensitiveDisplays)506 void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
507         const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
508     std::scoped_lock _l(mLock);
509     onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
510 }
511 
dump(std::string & dump)512 void PointerChoreographer::dump(std::string& dump) {
513     std::scoped_lock _l(mLock);
514 
515     dump += "PointerChoreographer:\n";
516     dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
517     dump += StringPrintf("stylus pointer icon enabled: %s\n",
518                          mStylusPointerIconEnabled ? "true" : "false");
519 
520     dump += INDENT "MousePointerControllers:\n";
521     for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
522         std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
523         dump += INDENT + displayId.toString() + " : " + pointerControllerDump;
524     }
525     dump += INDENT "TouchPointerControllers:\n";
526     for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
527         std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
528         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
529     }
530     dump += INDENT "StylusPointerControllers:\n";
531     for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
532         std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
533         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
534     }
535     dump += INDENT "DrawingTabletControllers:\n";
536     for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
537         std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
538         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
539     }
540     dump += "\n";
541 }
542 
findViewportByIdLocked(ui::LogicalDisplayId displayId) const543 const DisplayViewport* PointerChoreographer::findViewportByIdLocked(
544         ui::LogicalDisplayId displayId) const {
545     for (auto& viewport : mViewports) {
546         if (viewport.displayId == displayId) {
547             return &viewport;
548         }
549     }
550     return nullptr;
551 }
552 
getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const553 ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
554         ui::LogicalDisplayId associatedDisplayId) const {
555     return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
556 }
557 
558 std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId)559 PointerChoreographer::ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) {
560     const ui::LogicalDisplayId displayId = getTargetMouseDisplayLocked(associatedDisplayId);
561 
562     auto it = mMousePointersByDisplay.find(displayId);
563     if (it == mMousePointersByDisplay.end()) {
564         it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
565                      .first;
566         onControllerAddedOrRemovedLocked();
567     }
568 
569     return {displayId, *it->second};
570 }
571 
findInputDeviceLocked(DeviceId deviceId)572 InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
573     auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
574                            [deviceId](const auto& info) { return info.getId() == deviceId; });
575     return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
576 }
577 
canUnfadeOnDisplay(ui::LogicalDisplayId displayId)578 bool PointerChoreographer::canUnfadeOnDisplay(ui::LogicalDisplayId displayId) {
579     return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
580 }
581 
updatePointerControllersLocked()582 PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
583     std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
584     std::set<DeviceId> touchDevicesToKeep;
585     std::set<DeviceId> stylusDevicesToKeep;
586     std::set<DeviceId> drawingTabletDevicesToKeep;
587 
588     // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
589     // new PointerControllers if necessary.
590     for (const auto& info : mInputDeviceInfos) {
591         if (!info.isEnabled()) {
592             // If device is disabled, we should not keep it, and should not show pointer for
593             // disabled mouse device.
594             continue;
595         }
596         const uint32_t sources = info.getSources();
597         const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
598 
599         if (isMouseOrTouchpad(sources) || isKnownMouse) {
600             const ui::LogicalDisplayId displayId =
601                     getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
602             mouseDisplaysToKeep.insert(displayId);
603             // For mice, show the cursor immediately when the device is first connected or
604             // when it moves to a new display.
605             auto [mousePointerIt, isNewMousePointer] =
606                     mMousePointersByDisplay.try_emplace(displayId,
607                                                         getMouseControllerConstructor(displayId));
608             if (isNewMousePointer) {
609                 onControllerAddedOrRemovedLocked();
610             }
611 
612             mMouseDevices.emplace(info.getId());
613             if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
614                 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
615             }
616         }
617         if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
618             info.getAssociatedDisplayId().isValid()) {
619             touchDevicesToKeep.insert(info.getId());
620         }
621         if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
622             info.getAssociatedDisplayId().isValid()) {
623             stylusDevicesToKeep.insert(info.getId());
624         }
625         if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
626             info.getAssociatedDisplayId().isValid()) {
627             drawingTabletDevicesToKeep.insert(info.getId());
628         }
629     }
630 
631     // Remove PointerControllers no longer needed.
632     std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
633         return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
634     });
635     std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
636         return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
637     });
638     std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
639         return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
640     });
641     std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
642         return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
643     });
644     std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
645         return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
646                             [id](const auto& info) { return info.getId() == id; }) ==
647                 mInputDeviceInfos.end();
648     });
649 
650     onControllerAddedOrRemovedLocked();
651 
652     // Check if we need to notify the policy if there's a change on the pointer display ID.
653     return calculatePointerDisplayChangeToNotify();
654 }
655 
656 PointerChoreographer::PointerDisplayChange
calculatePointerDisplayChangeToNotify()657 PointerChoreographer::calculatePointerDisplayChangeToNotify() {
658     ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
659     FloatPoint cursorPosition = {0, 0};
660     if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
661         it != mMousePointersByDisplay.end()) {
662         const auto& pointerController = it->second;
663         // Use the displayId from the pointerController, because it accurately reflects whether
664         // the viewport has been added for that display. Otherwise, we would have to check if
665         // the viewport exists separately.
666         displayIdToNotify = pointerController->getDisplayId();
667         cursorPosition = pointerController->getPosition();
668     }
669     if (mNotifiedPointerDisplayId == displayIdToNotify) {
670         return {};
671     }
672     mNotifiedPointerDisplayId = displayIdToNotify;
673     return {{displayIdToNotify, cursorPosition}};
674 }
675 
setDefaultMouseDisplayId(ui::LogicalDisplayId displayId)676 void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
677     PointerDisplayChange pointerDisplayChange;
678 
679     { // acquire lock
680         std::scoped_lock _l(mLock);
681 
682         mDefaultMouseDisplayId = displayId;
683         pointerDisplayChange = updatePointerControllersLocked();
684     } // release lock
685 
686     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
687 }
688 
setDisplayViewports(const std::vector<DisplayViewport> & viewports)689 void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
690     PointerDisplayChange pointerDisplayChange;
691 
692     { // acquire lock
693         std::scoped_lock _l(mLock);
694         for (const auto& viewport : viewports) {
695             const ui::LogicalDisplayId displayId = viewport.displayId;
696             if (const auto it = mMousePointersByDisplay.find(displayId);
697                 it != mMousePointersByDisplay.end()) {
698                 it->second->setDisplayViewport(viewport);
699             }
700             for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
701                 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
702                 if (info && info->getAssociatedDisplayId() == displayId) {
703                     stylusPointerController->setDisplayViewport(viewport);
704                 }
705             }
706             for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
707                 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
708                 if (info && info->getAssociatedDisplayId() == displayId) {
709                     drawingTabletController->setDisplayViewport(viewport);
710                 }
711             }
712         }
713         mViewports = viewports;
714         pointerDisplayChange = calculatePointerDisplayChangeToNotify();
715     } // release lock
716 
717     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
718 }
719 
getViewportForPointerDevice(ui::LogicalDisplayId associatedDisplayId)720 std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
721         ui::LogicalDisplayId associatedDisplayId) {
722     std::scoped_lock _l(mLock);
723     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
724     if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
725         return *viewport;
726     }
727     return std::nullopt;
728 }
729 
getMouseCursorPosition(ui::LogicalDisplayId displayId)730 FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
731     std::scoped_lock _l(mLock);
732     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
733     if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
734         it != mMousePointersByDisplay.end()) {
735         return it->second->getPosition();
736     }
737     return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
738 }
739 
setShowTouchesEnabled(bool enabled)740 void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
741     PointerDisplayChange pointerDisplayChange;
742 
743     { // acquire lock
744         std::scoped_lock _l(mLock);
745         if (mShowTouchesEnabled == enabled) {
746             return;
747         }
748         mShowTouchesEnabled = enabled;
749         pointerDisplayChange = updatePointerControllersLocked();
750     } // release lock
751 
752     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
753 }
754 
setStylusPointerIconEnabled(bool enabled)755 void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
756     PointerDisplayChange pointerDisplayChange;
757 
758     { // acquire lock
759         std::scoped_lock _l(mLock);
760         if (mStylusPointerIconEnabled == enabled) {
761             return;
762         }
763         mStylusPointerIconEnabled = enabled;
764         pointerDisplayChange = updatePointerControllersLocked();
765     } // release lock
766 
767     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
768 }
769 
setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> icon,ui::LogicalDisplayId displayId,DeviceId deviceId)770 bool PointerChoreographer::setPointerIcon(
771         std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
772         ui::LogicalDisplayId displayId, DeviceId deviceId) {
773     std::scoped_lock _l(mLock);
774     if (deviceId < 0) {
775         LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
776         return false;
777     }
778     const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
779     if (!info) {
780         LOG(WARNING) << "No input device info found for id " << deviceId
781                      << ". Cannot set pointer icon.";
782         return false;
783     }
784     const uint32_t sources = info->getSources();
785 
786     if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
787         auto it = mDrawingTabletPointersByDevice.find(deviceId);
788         if (it != mDrawingTabletPointersByDevice.end()) {
789             setIconForController(icon, *it->second);
790             return true;
791         }
792     }
793     if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
794         auto it = mStylusPointersByDevice.find(deviceId);
795         if (it != mStylusPointersByDevice.end()) {
796             setIconForController(icon, *it->second);
797             return true;
798         }
799     }
800     if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
801         auto it = mMousePointersByDisplay.find(displayId);
802         if (it != mMousePointersByDisplay.end()) {
803             setIconForController(icon, *it->second);
804             return true;
805         } else {
806             LOG(WARNING) << "No mouse pointer controller found for display " << displayId
807                          << ", device " << deviceId << ".";
808             return false;
809         }
810     }
811     LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
812                  << ".";
813     return false;
814 }
815 
setPointerIconVisibility(ui::LogicalDisplayId displayId,bool visible)816 void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
817     std::scoped_lock lock(mLock);
818     if (visible) {
819         mDisplaysWithPointersHidden.erase(displayId);
820         // We do not unfade the icons here, because we don't know when the last event happened.
821         return;
822     }
823 
824     mDisplaysWithPointersHidden.emplace(displayId);
825 
826     // Hide any icons that are currently visible on the display.
827     if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
828         const auto& [_, controller] = *it;
829         controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
830     }
831     for (const auto& [_, controller] : mStylusPointersByDevice) {
832         if (controller->getDisplayId() == displayId) {
833             controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
834         }
835     }
836 }
837 
setFocusedDisplay(ui::LogicalDisplayId displayId)838 void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
839     std::scoped_lock lock(mLock);
840     mCurrentFocusedDisplay = displayId;
841 }
842 
getMouseControllerConstructor(ui::LogicalDisplayId displayId)843 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
844         ui::LogicalDisplayId displayId) {
845     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
846             [this, displayId]() REQUIRES(mLock) {
847                 auto pc = mPolicy.createPointerController(
848                         PointerControllerInterface::ControllerType::MOUSE);
849                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
850                     pc->setDisplayViewport(*viewport);
851                 }
852                 return pc;
853             };
854     return ConstructorDelegate(std::move(ctor));
855 }
856 
getStylusControllerConstructor(ui::LogicalDisplayId displayId)857 PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
858         ui::LogicalDisplayId displayId) {
859     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
860             [this, displayId]() REQUIRES(mLock) {
861                 auto pc = mPolicy.createPointerController(
862                         PointerControllerInterface::ControllerType::STYLUS);
863                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
864                     pc->setDisplayViewport(*viewport);
865                 }
866                 return pc;
867             };
868     return ConstructorDelegate(std::move(ctor));
869 }
870 
onWindowInfosChanged(const gui::WindowInfosUpdate & windowInfosUpdate)871 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
872         const gui::WindowInfosUpdate& windowInfosUpdate) {
873     std::scoped_lock _l(mListenerLock);
874     if (mPointerChoreographer == nullptr) {
875         return;
876     }
877     auto newPrivacySensitiveDisplays =
878             getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
879     if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
880         mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
881         mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
882     }
883 }
884 
setInitialDisplayInfos(const std::vector<gui::WindowInfo> & windowInfos)885 void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
886         const std::vector<gui::WindowInfo>& windowInfos) {
887     std::scoped_lock _l(mListenerLock);
888     mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
889 }
890 
891 std::unordered_set<ui::LogicalDisplayId /*displayId*/>
getPrivacySensitiveDisplays()892 PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
893     std::scoped_lock _l(mListenerLock);
894     return mPrivacySensitiveDisplays;
895 }
896 
897 void PointerChoreographer::PointerChoreographerDisplayInfoListener::
onPointerChoreographerDestroyed()898         onPointerChoreographerDestroyed() {
899     std::scoped_lock _l(mListenerLock);
900     mPointerChoreographer = nullptr;
901 }
902 
903 } // namespace android
904