• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android/configuration.h>
21 #include <com_android_input_flags.h>
22 #include <algorithm>
23 #if defined(__ANDROID__)
24 #include <gui/SurfaceComposerClient.h>
25 #endif
26 #include <input/InputFlags.h>
27 #include <input/Keyboard.h>
28 #include <input/PrintTools.h>
29 #include <unordered_set>
30 
31 #include "PointerChoreographer.h"
32 
33 #define INDENT "  "
34 #define INDENT2 "    "
35 
36 namespace android {
37 
38 namespace {
39 
notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId,vec2>> change,PointerChoreographerPolicyInterface & policy)40 inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
41                                        PointerChoreographerPolicyInterface& policy) {
42     if (!change) {
43         return;
44     }
45     const auto& [displayId, cursorPosition] = *change;
46     policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
47 }
48 
setIconForController(const std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> & icon,PointerControllerInterface & controller)49 void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
50                           PointerControllerInterface& controller) {
51     if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
52         if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
53             LOG(FATAL) << "SpriteIcon should not be null";
54         }
55         controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
56     } else {
57         controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
58     }
59 }
60 
61 // filters and returns a set of privacy sensitive displays that are currently visible.
getPrivacySensitiveDisplaysFromWindowInfos(const std::vector<gui::WindowInfo> & windowInfos)62 std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
63         const std::vector<gui::WindowInfo>& windowInfos) {
64     std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
65     for (const auto& windowInfo : windowInfos) {
66         if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
67             windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
68             privacySensitiveDisplays.insert(windowInfo.displayId);
69         }
70     }
71     return privacySensitiveDisplays;
72 }
73 
calculatePositionOnDestinationViewport(const DisplayViewport & destinationViewport,float pointerOffset,DisplayTopologyPosition sourceBoundary)74 vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport,
75                                             float pointerOffset,
76                                             DisplayTopologyPosition sourceBoundary) {
77     // destination is opposite of the source boundary
78     switch (sourceBoundary) {
79         case DisplayTopologyPosition::RIGHT:
80             return {0, pointerOffset}; // left edge
81         case DisplayTopologyPosition::TOP:
82             return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
83         case DisplayTopologyPosition::LEFT:
84             return {destinationViewport.logicalRight, pointerOffset}; // right edge
85         case DisplayTopologyPosition::BOTTOM:
86             return {pointerOffset, 0}; // top edge
87     }
88 }
89 
90 // The standardised medium display density for which 1 px  = 1 dp
91 constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
92 
pxToDp(int px,int dpi)93 inline float pxToDp(int px, int dpi) {
94     return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi);
95 }
96 
dpToPx(float dp,int dpi)97 inline int dpToPx(float dp, int dpi) {
98     return static_cast<int>((dp * dpi) / DENSITY_MEDIUM);
99 }
100 
101 } // namespace
102 
103 // --- PointerChoreographer ---
104 
PointerChoreographer(InputListenerInterface & inputListener,PointerChoreographerPolicyInterface & policy)105 PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
106                                            PointerChoreographerPolicyInterface& policy)
107       : PointerChoreographer(
108                 inputListener, policy,
109                 [](const sp<android::gui::WindowInfosListener>& listener) {
110                     auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
111                                                       std::vector<android::gui::DisplayInfo>{});
112 #if defined(__ANDROID__)
113                     SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
114                                                                                 &initialInfo);
115 #endif
116                     return initialInfo.first;
117                 },
__anon2bcd3f0f0302(const sp<android::gui::WindowInfosListener>& listener) 118                 [](const sp<android::gui::WindowInfosListener>& listener) {
119 #if defined(__ANDROID__)
120                     SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
121 #endif
122                 }) {
123 }
124 
PointerChoreographer(android::InputListenerInterface & listener,android::PointerChoreographerPolicyInterface & policy,const android::PointerChoreographer::WindowListenerRegisterConsumer & registerListener,const android::PointerChoreographer::WindowListenerUnregisterConsumer & unregisterListener)125 PointerChoreographer::PointerChoreographer(
126         android::InputListenerInterface& listener,
127         android::PointerChoreographerPolicyInterface& policy,
128         const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
129         const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
130       : mTouchControllerConstructor([this]() {
131             return mPolicy.createPointerController(
132                     PointerControllerInterface::ControllerType::TOUCH);
133         }),
134         mNextListener(listener),
135         mPolicy(policy),
136         mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
137         mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
138         mShowTouchesEnabled(false),
139         mStylusPointerIconEnabled(false),
140         mPointerMotionFilterEnabled(false),
141         mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
142         mIsWindowInfoListenerRegistered(false),
143         mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
144         mRegisterListener(registerListener),
145         mUnregisterListener(unregisterListener) {}
146 
~PointerChoreographer()147 PointerChoreographer::~PointerChoreographer() {
148     if (mIsWindowInfoListenerRegistered) {
149         mUnregisterListener(mWindowInfoListener);
150         mIsWindowInfoListenerRegistered = false;
151     }
152     mWindowInfoListener->onPointerChoreographerDestroyed();
153 }
154 
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)155 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
156     PointerDisplayChange pointerDisplayChange;
157 
158     { // acquire lock
159         std::scoped_lock _l(getLock());
160 
161         mInputDeviceInfos = args.inputDeviceInfos;
162         pointerDisplayChange = updatePointerControllersLocked();
163     } // release lock
164 
165     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
166     mNextListener.notify(args);
167 }
168 
notifyKey(const NotifyKeyArgs & args)169 void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
170     fadeMouseCursorOnKeyPress(args);
171     mNextListener.notify(args);
172 }
173 
notifyMotion(const NotifyMotionArgs & args)174 void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
175     NotifyMotionArgs newArgs = processMotion(args);
176 
177     mNextListener.notify(newArgs);
178 }
179 
fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs & args)180 void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
181     if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
182         return;
183     }
184     // Meta state for these keys is ignored for dismissing cursor while typing
185     constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
186             AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
187     if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
188         // Do not fade if any other meta state is active
189         return;
190     }
191     if (!mPolicy.isInputMethodConnectionActive()) {
192         return;
193     }
194 
195     std::scoped_lock _l(getLock());
196     ui::LogicalDisplayId targetDisplay = args.displayId;
197     if (targetDisplay == ui::LogicalDisplayId::INVALID) {
198         targetDisplay = mCurrentFocusedDisplay;
199     }
200     auto it = mMousePointersByDisplay.find(targetDisplay);
201     if (it != mMousePointersByDisplay.end()) {
202         mPolicy.notifyMouseCursorFadedOnTyping();
203         it->second->fade(PointerControllerInterface::Transition::GRADUAL);
204     }
205 }
206 
processMotion(const NotifyMotionArgs & args)207 NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
208     NotifyMotionArgs newArgs(args);
209     PointerDisplayChange pointerDisplayChange;
210     { // acquire lock
211         std::scoped_lock _l(getLock());
212         if (isFromMouse(args.source, args.pointerProperties[0].toolType)) {
213             newArgs = processMouseEventLocked(args);
214             pointerDisplayChange = calculatePointerDisplayChangeToNotify();
215         } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) {
216             newArgs = processTouchpadEventLocked(args);
217             pointerDisplayChange = calculatePointerDisplayChangeToNotify();
218         } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) {
219             processDrawingTabletEventLocked(args);
220         } else if (mStylusPointerIconEnabled &&
221                    isStylusHoverEvent(args.source, args.pointerProperties, args.action)) {
222             processStylusHoverEventLocked(args);
223         } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
224             processTouchscreenAndStylusEventLocked(args);
225         }
226     } // release lock
227 
228     if (pointerDisplayChange) {
229         // pointer display may have changed if mouse crossed display boundary
230         notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
231     }
232     return newArgs;
233 }
234 
processMouseEventLocked(const NotifyMotionArgs & args)235 NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
236     if (args.getPointerCount() != 1) {
237         LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
238                    << args.dump();
239     }
240 
241     mMouseDevices.emplace(args.deviceId);
242     auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
243     NotifyMotionArgs newArgs(args);
244     newArgs.displayId = displayId;
245 
246     if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
247         // This is an absolute mouse device that knows about the location of the cursor on the
248         // display, so set the cursor position to the specified location.
249         const auto position = pc.getPosition();
250         const float deltaX = args.xCursorPosition - position.x;
251         const float deltaY = args.yCursorPosition - position.y;
252         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
253         newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
254         pc.setPosition(args.xCursorPosition, args.yCursorPosition);
255     } else {
256         // This is a relative mouse, so move the cursor by the specified amount.
257         processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
258     }
259     // Note displayId may have changed if the cursor moved to a different display
260     if (canUnfadeOnDisplay(newArgs.displayId)) {
261         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
262     }
263     return newArgs;
264 }
265 
processTouchpadEventLocked(const NotifyMotionArgs & args)266 NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
267     mMouseDevices.emplace(args.deviceId);
268     auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
269 
270     NotifyMotionArgs newArgs(args);
271     newArgs.displayId = displayId;
272     if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
273         // This is a movement of the mouse pointer.
274         processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
275     } else {
276         // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
277         const auto position = pc.getPosition();
278         for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
279             newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
280                                                   args.pointerCoords[i].getX() + position.x);
281             newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
282                                                   args.pointerCoords[i].getY() + position.y);
283         }
284         newArgs.xCursorPosition = position.x;
285         newArgs.yCursorPosition = position.y;
286     }
287 
288     // Note displayId may have changed if the cursor moved to a different display
289     if (canUnfadeOnDisplay(newArgs.displayId)) {
290         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
291     }
292     return newArgs;
293 }
294 
processPointerDeviceMotionEventLocked(NotifyMotionArgs & newArgs,PointerControllerInterface & pc)295 void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
296                                                                  PointerControllerInterface& pc) {
297     const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
298     const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
299     vec2 filteredDelta =
300             filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
301                                                       newArgs.displayId);
302     vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
303     if (InputFlags::connectedDisplaysCursorEnabled() &&
304         (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
305         handleUnconsumedDeltaLocked(pc, unconsumedDelta);
306         // pointer may have moved to a different viewport
307         newArgs.displayId = pc.getDisplayId();
308     }
309 
310     const auto position = pc.getPosition();
311     newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
312     newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
313     newArgs.xCursorPosition = position.x;
314     newArgs.yCursorPosition = position.y;
315 }
316 
handleUnconsumedDeltaLocked(PointerControllerInterface & pc,const vec2 & unconsumedDelta)317 void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
318                                                        const vec2& unconsumedDelta) {
319     // Display topology is in rotated coordinate space and Pointer controller returns and expects
320     // values in the un-rotated coordinate space. So we need to transform delta and cursor position
321     // back to the rotated coordinate space to lookup adjacent display in the display topology.
322     const auto& sourceDisplayTransform = pc.getDisplayTransform();
323     const vec2 rotatedUnconsumedDelta =
324             transformWithoutTranslation(sourceDisplayTransform, unconsumedDelta);
325     const vec2 cursorPosition = pc.getPosition();
326     const vec2 rotatedCursorPosition = sourceDisplayTransform.transform(cursorPosition);
327 
328     // To find out the boundary that cursor is crossing we are checking delta in x and y direction
329     // respectively. This prioritizes x direction over y.
330     // In practise, majority of cases we only have non-zero values in either x or y coordinates,
331     // except sometimes near the corners.
332     // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
333     // the destination display for the same reason.
334     DisplayTopologyPosition sourceBoundary;
335     float cursorOffset = 0.0f;
336     if (rotatedUnconsumedDelta.x > 0) {
337         sourceBoundary = DisplayTopologyPosition::RIGHT;
338         cursorOffset = rotatedCursorPosition.y;
339     } else if (rotatedUnconsumedDelta.x < 0) {
340         sourceBoundary = DisplayTopologyPosition::LEFT;
341         cursorOffset = rotatedCursorPosition.y;
342     } else if (rotatedUnconsumedDelta.y > 0) {
343         sourceBoundary = DisplayTopologyPosition::BOTTOM;
344         cursorOffset = rotatedCursorPosition.x;
345     } else {
346         sourceBoundary = DisplayTopologyPosition::TOP;
347         cursorOffset = rotatedCursorPosition.x;
348     }
349 
350     const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId();
351     std::optional<std::pair<const DisplayViewport*, float /*offset*/>> destination =
352             findDestinationDisplayLocked(sourceDisplayId, sourceBoundary, cursorOffset);
353     if (!destination.has_value()) {
354         // No matching adjacent display
355         return;
356     }
357 
358     const DisplayViewport& destinationViewport = *destination->first;
359     const float destinationOffset = destination->second;
360     if (mMousePointersByDisplay.find(destinationViewport.displayId) !=
361         mMousePointersByDisplay.end()) {
362         LOG(FATAL) << "A cursor already exists on destination display"
363                    << destinationViewport.displayId;
364     }
365     mCurrentMouseDisplayId = destinationViewport.displayId;
366     auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
367     pcNode.key() = destinationViewport.displayId;
368     mMousePointersByDisplay.insert(std::move(pcNode));
369 
370     // Before updating the viewport and moving the cursor to appropriate location in the destination
371     // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the
372     // center of the display in any intermediate frames.
373     pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
374     pc.setDisplayViewport(destinationViewport);
375     vec2 destinationPosition =
376             calculatePositionOnDestinationViewport(destinationViewport, destinationOffset,
377                                                    sourceBoundary);
378 
379     // Transform position back to un-rotated coordinate space before sending it to controller
380     destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
381                                                                        destinationPosition.y);
382     pc.setPosition(destinationPosition.x, destinationPosition.y);
383     pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
384 }
385 
processDrawingTabletEventLocked(const android::NotifyMotionArgs & args)386 void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
387     if (args.displayId == ui::LogicalDisplayId::INVALID) {
388         return;
389     }
390 
391     if (args.getPointerCount() != 1) {
392         LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
393                      << args.dump();
394     }
395 
396     // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
397     auto [it, controllerAdded] =
398             mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
399                                                        getMouseControllerConstructor(
400                                                                args.displayId));
401     if (controllerAdded) {
402         onControllerAddedOrRemovedLocked();
403     }
404 
405     PointerControllerInterface& pc = *it->second;
406 
407     const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
408     const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
409     pc.setPosition(x, y);
410     if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
411         // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
412         //   immediately by a DOWN event.
413         pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
414         pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
415     } else if (canUnfadeOnDisplay(args.displayId)) {
416         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
417     }
418 }
419 
420 /**
421  * When screen is touched, fade the mouse pointer on that display. We only call fade for
422  * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
423  * mouse device keeps moving and unfades the cursor.
424  * For touch events, we do not need to populate the cursor position.
425  */
processTouchscreenAndStylusEventLocked(const NotifyMotionArgs & args)426 void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
427     if (!args.displayId.isValid()) {
428         return;
429     }
430 
431     if (const auto it = mMousePointersByDisplay.find(args.displayId);
432         it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
433         it->second->fade(PointerControllerInterface::Transition::GRADUAL);
434     }
435 
436     if (!mShowTouchesEnabled) {
437         return;
438     }
439 
440     // Get the touch pointer controller for the device, or create one if it doesn't exist.
441     auto [it, controllerAdded] =
442             mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
443     if (controllerAdded) {
444         onControllerAddedOrRemovedLocked();
445     }
446 
447     PointerControllerInterface& pc = *it->second;
448 
449     const PointerCoords* coords = args.pointerCoords.data();
450     const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
451     const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
452     std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
453     BitSet32 idBits;
454     if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL &&
455         maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
456         for (size_t i = 0; i < args.getPointerCount(); i++) {
457             if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
458                 continue;
459             }
460             uint32_t id = args.pointerProperties[i].id;
461             idToIndex[id] = i;
462             idBits.markBit(id);
463         }
464     }
465     // The PointerController already handles setting spots per-display, so
466     // we do not need to manually manage display changes for touch spots for now.
467     pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
468 }
469 
processStylusHoverEventLocked(const NotifyMotionArgs & args)470 void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
471     if (!args.displayId.isValid()) {
472         return;
473     }
474 
475     if (args.getPointerCount() != 1) {
476         LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
477                      << args.dump();
478     }
479 
480     // Fade the mouse pointer on the display if there is one when the stylus starts hovering.
481     if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) {
482         if (const auto it = mMousePointersByDisplay.find(args.displayId);
483             it != mMousePointersByDisplay.end()) {
484             it->second->fade(PointerControllerInterface::Transition::GRADUAL);
485         }
486     }
487 
488     // Get the stylus pointer controller for the device, or create one if it doesn't exist.
489     auto [it, controllerAdded] =
490             mStylusPointersByDevice.try_emplace(args.deviceId,
491                                                 getStylusControllerConstructor(args.displayId));
492     if (controllerAdded) {
493         onControllerAddedOrRemovedLocked();
494     }
495 
496     PointerControllerInterface& pc = *it->second;
497 
498     const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
499     const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
500     pc.setPosition(x, y);
501     if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
502         // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
503         //   immediately by a DOWN event.
504         pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
505         pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER
506                                                  : PointerIconStyle::TYPE_NOT_SPECIFIED);
507     } else if (canUnfadeOnDisplay(args.displayId)) {
508         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
509     }
510 }
511 
notifySwitch(const NotifySwitchArgs & args)512 void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
513     mNextListener.notify(args);
514 }
515 
notifySensor(const NotifySensorArgs & args)516 void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
517     mNextListener.notify(args);
518 }
519 
notifyVibratorState(const NotifyVibratorStateArgs & args)520 void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
521     mNextListener.notify(args);
522 }
523 
notifyDeviceReset(const NotifyDeviceResetArgs & args)524 void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
525     processDeviceReset(args);
526 
527     mNextListener.notify(args);
528 }
529 
processDeviceReset(const NotifyDeviceResetArgs & args)530 void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
531     std::scoped_lock _l(getLock());
532     mTouchPointersByDevice.erase(args.deviceId);
533     mStylusPointersByDevice.erase(args.deviceId);
534     mDrawingTabletPointersByDevice.erase(args.deviceId);
535     onControllerAddedOrRemovedLocked();
536 }
537 
onControllerAddedOrRemovedLocked()538 void PointerChoreographer::onControllerAddedOrRemovedLocked() {
539     bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
540             !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
541 
542     // PointerChoreographer uses Listener's lock which is already held by caller
543     base::ScopedLockAssertion assumeLocked(mWindowInfoListener->mLock);
544 
545     if (requireListener && !mIsWindowInfoListenerRegistered) {
546         mIsWindowInfoListenerRegistered = true;
547         mWindowInfoListener->setInitialDisplayInfosLocked(mRegisterListener(mWindowInfoListener));
548         onPrivacySensitiveDisplaysChangedLocked(
549                 mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
550     } else if (!requireListener && mIsWindowInfoListenerRegistered) {
551         mIsWindowInfoListenerRegistered = false;
552         mUnregisterListener(mWindowInfoListener);
553     } else if (requireListener) {
554         // controller may have been added to an existing privacy sensitive display, we need to
555         // update all controllers again
556         onPrivacySensitiveDisplaysChangedLocked(
557                 mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
558     }
559 }
560 
onPrivacySensitiveDisplaysChangedLocked(const std::unordered_set<ui::LogicalDisplayId> & privacySensitiveDisplays)561 void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
562         const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
563     for (auto& [_, pc] : mTouchPointersByDevice) {
564         pc->clearSkipScreenshotFlags();
565         for (auto displayId : privacySensitiveDisplays) {
566             pc->setSkipScreenshotFlagForDisplay(displayId);
567         }
568     }
569 
570     for (auto& [displayId, pc] : mMousePointersByDisplay) {
571         if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
572             pc->setSkipScreenshotFlagForDisplay(displayId);
573         } else {
574             pc->clearSkipScreenshotFlags();
575         }
576     }
577 
578     for (auto* pointerControllerByDevice :
579          {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
580         for (auto& [_, pc] : *pointerControllerByDevice) {
581             auto displayId = pc->getDisplayId();
582             if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
583                 pc->setSkipScreenshotFlagForDisplay(displayId);
584             } else {
585                 pc->clearSkipScreenshotFlags();
586             }
587         }
588     }
589 }
590 
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)591 void PointerChoreographer::notifyPointerCaptureChanged(
592         const NotifyPointerCaptureChangedArgs& args) {
593     if (args.request.isEnable()) {
594         std::scoped_lock _l(getLock());
595         for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
596             mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
597         }
598     }
599     mNextListener.notify(args);
600 }
601 
setDisplayTopology(const DisplayTopologyGraph & displayTopologyGraph)602 void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
603     PointerDisplayChange pointerDisplayChange;
604     { // acquire lock
605         std::scoped_lock _l(getLock());
606         mTopology = displayTopologyGraph;
607 
608         // make primary display default mouse display, if it was not set or
609         // the existing display was removed
610         if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
611             mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
612             mCurrentMouseDisplayId = mTopology.primaryDisplayId;
613             pointerDisplayChange = updatePointerControllersLocked();
614         }
615     } // release lock
616 
617     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
618 }
619 
dump(std::string & dump)620 void PointerChoreographer::dump(std::string& dump) {
621     std::scoped_lock _l(getLock());
622 
623     dump += "PointerChoreographer:\n";
624     dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
625                          mShowTouchesEnabled ? "true" : "false");
626     dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
627                          mStylusPointerIconEnabled ? "true" : "false");
628     dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
629                          mPointerMotionFilterEnabled ? "true" : "false");
630 
631     dump += INDENT "MousePointerControllers:\n";
632     for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
633         std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
634         dump += INDENT + displayId.toString() + " : " + pointerControllerDump;
635     }
636     dump += INDENT "TouchPointerControllers:\n";
637     for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
638         std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
639         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
640     }
641     dump += INDENT "StylusPointerControllers:\n";
642     for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
643         std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
644         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
645     }
646     dump += INDENT "DrawingTabletControllers:\n";
647     for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
648         std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
649         dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
650     }
651     dump += INDENT "DisplayTopologyGraph:\n";
652     dump += addLinePrefix(mTopology.dump(), INDENT2);
653     dump += "\n";
654 }
655 
findViewportByIdLocked(ui::LogicalDisplayId displayId) const656 const DisplayViewport* PointerChoreographer::findViewportByIdLocked(
657         ui::LogicalDisplayId displayId) const {
658     for (auto& viewport : mViewports) {
659         if (viewport.displayId == displayId) {
660             return &viewport;
661         }
662     }
663     return nullptr;
664 }
665 
getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const666 ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
667         ui::LogicalDisplayId associatedDisplayId) const {
668     if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
669         if (associatedDisplayId.isValid()) {
670             return associatedDisplayId;
671         }
672         return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
673                                                 : ui::LogicalDisplayId::DEFAULT;
674     }
675     // Associated display is not included in the topology, return this associated display.
676     if (associatedDisplayId.isValid() &&
677         mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
678         return associatedDisplayId;
679     }
680     if (mCurrentMouseDisplayId.isValid()) {
681         return mCurrentMouseDisplayId;
682     }
683     if (mTopology.primaryDisplayId.isValid()) {
684         return mTopology.primaryDisplayId;
685     }
686     return ui::LogicalDisplayId::DEFAULT;
687 }
688 
689 std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId)690 PointerChoreographer::ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) {
691     const ui::LogicalDisplayId displayId = getTargetMouseDisplayLocked(associatedDisplayId);
692 
693     auto it = mMousePointersByDisplay.find(displayId);
694     if (it == mMousePointersByDisplay.end()) {
695         it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
696                      .first;
697         onControllerAddedOrRemovedLocked();
698     }
699 
700     return {displayId, *it->second};
701 }
702 
findInputDeviceLocked(DeviceId deviceId)703 InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
704     auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
705                            [deviceId](const auto& info) { return info.getId() == deviceId; });
706     return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
707 }
708 
canUnfadeOnDisplay(ui::LogicalDisplayId displayId)709 bool PointerChoreographer::canUnfadeOnDisplay(ui::LogicalDisplayId displayId) {
710     return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
711 }
712 
getLock() const713 std::mutex& PointerChoreographer::getLock() const {
714     return mWindowInfoListener->mLock;
715 }
716 
updatePointerControllersLocked()717 PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
718     std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
719     std::set<DeviceId> touchDevicesToKeep;
720     std::set<DeviceId> stylusDevicesToKeep;
721     std::set<DeviceId> drawingTabletDevicesToKeep;
722 
723     // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
724     // new PointerControllers if necessary.
725     for (const auto& info : mInputDeviceInfos) {
726         if (!info.isEnabled()) {
727             // If device is disabled, we should not keep it, and should not show pointer for
728             // disabled mouse device.
729             continue;
730         }
731         const uint32_t sources = info.getSources();
732         const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
733 
734         if (isMouseOrTouchpad(sources) || isKnownMouse) {
735             const ui::LogicalDisplayId displayId =
736                     getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
737             mouseDisplaysToKeep.insert(displayId);
738             // For mice, show the cursor immediately when the device is first connected or
739             // when it moves to a new display.
740             auto [mousePointerIt, isNewMousePointer] =
741                     mMousePointersByDisplay.try_emplace(displayId,
742                                                         getMouseControllerConstructor(displayId));
743             if (isNewMousePointer) {
744                 onControllerAddedOrRemovedLocked();
745             }
746 
747             mMouseDevices.emplace(info.getId());
748             if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
749                 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
750             }
751         }
752         if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
753             info.getAssociatedDisplayId().isValid()) {
754             touchDevicesToKeep.insert(info.getId());
755         }
756         if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
757             info.getAssociatedDisplayId().isValid()) {
758             stylusDevicesToKeep.insert(info.getId());
759         }
760         if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
761             info.getAssociatedDisplayId().isValid()) {
762             drawingTabletDevicesToKeep.insert(info.getId());
763         }
764     }
765 
766     // Remove PointerControllers no longer needed.
767     std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
768         return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
769     });
770     std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
771         return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
772     });
773     std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
774         return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
775     });
776     std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
777         return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
778     });
779     std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(getLock()) {
780         return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
781                             [id](const auto& info) { return info.getId() == id; }) ==
782                 mInputDeviceInfos.end();
783     });
784 
785     onControllerAddedOrRemovedLocked();
786 
787     // Check if we need to notify the policy if there's a change on the pointer display ID.
788     return calculatePointerDisplayChangeToNotify();
789 }
790 
791 PointerChoreographer::PointerDisplayChange
calculatePointerDisplayChangeToNotify()792 PointerChoreographer::calculatePointerDisplayChangeToNotify() {
793     ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
794     vec2 cursorPosition = {0, 0};
795     if (const auto it =
796                 mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId));
797         it != mMousePointersByDisplay.end()) {
798         const auto& pointerController = it->second;
799         // Use the displayId from the pointerController, because it accurately reflects whether
800         // the viewport has been added for that display. Otherwise, we would have to check if
801         // the viewport exists separately.
802         displayIdToNotify = pointerController->getDisplayId();
803         cursorPosition = pointerController->getPosition();
804     }
805     if (mNotifiedPointerDisplayId == displayIdToNotify) {
806         return {};
807     }
808     mNotifiedPointerDisplayId = displayIdToNotify;
809     return {{displayIdToNotify, cursorPosition}};
810 }
811 
setDefaultMouseDisplayId(ui::LogicalDisplayId displayId)812 void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
813     if (InputFlags::connectedDisplaysCursorEnabled()) {
814         // In connected displays scenario, default mouse display will only be updated from topology.
815         return;
816     }
817     PointerDisplayChange pointerDisplayChange;
818 
819     { // acquire lock
820         std::scoped_lock _l(getLock());
821 
822         mCurrentMouseDisplayId = displayId;
823         pointerDisplayChange = updatePointerControllersLocked();
824     } // release lock
825 
826     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
827 }
828 
setDisplayViewports(const std::vector<DisplayViewport> & viewports)829 void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
830     PointerDisplayChange pointerDisplayChange;
831 
832     { // acquire lock
833         std::scoped_lock _l(getLock());
834         for (const auto& viewport : viewports) {
835             const ui::LogicalDisplayId displayId = viewport.displayId;
836             if (const auto it = mMousePointersByDisplay.find(displayId);
837                 it != mMousePointersByDisplay.end()) {
838                 it->second->setDisplayViewport(viewport);
839             }
840             for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
841                 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
842                 if (info && info->getAssociatedDisplayId() == displayId) {
843                     stylusPointerController->setDisplayViewport(viewport);
844                 }
845             }
846             for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
847                 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
848                 if (info && info->getAssociatedDisplayId() == displayId) {
849                     drawingTabletController->setDisplayViewport(viewport);
850                 }
851             }
852         }
853         mViewports = viewports;
854         pointerDisplayChange = calculatePointerDisplayChangeToNotify();
855     } // release lock
856 
857     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
858 }
859 
getViewportForPointerDevice(ui::LogicalDisplayId associatedDisplayId)860 std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
861         ui::LogicalDisplayId associatedDisplayId) {
862     std::scoped_lock _l(getLock());
863     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
864     if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
865         return *viewport;
866     }
867     return std::nullopt;
868 }
869 
getMouseCursorPosition(ui::LogicalDisplayId displayId)870 vec2 PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
871     std::scoped_lock _l(getLock());
872     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
873     if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
874         it != mMousePointersByDisplay.end()) {
875         return it->second->getPosition();
876     }
877     return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
878 }
879 
setShowTouchesEnabled(bool enabled)880 void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
881     PointerDisplayChange pointerDisplayChange;
882 
883     { // acquire lock
884         std::scoped_lock _l(getLock());
885         if (mShowTouchesEnabled == enabled) {
886             return;
887         }
888         mShowTouchesEnabled = enabled;
889         pointerDisplayChange = updatePointerControllersLocked();
890     } // release lock
891 
892     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
893 }
894 
setStylusPointerIconEnabled(bool enabled)895 void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
896     PointerDisplayChange pointerDisplayChange;
897 
898     { // acquire lock
899         std::scoped_lock _l(getLock());
900         if (mStylusPointerIconEnabled == enabled) {
901             return;
902         }
903         mStylusPointerIconEnabled = enabled;
904         pointerDisplayChange = updatePointerControllersLocked();
905     } // release lock
906 
907     notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
908 }
909 
setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> icon,ui::LogicalDisplayId displayId,DeviceId deviceId)910 bool PointerChoreographer::setPointerIcon(
911         std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
912         ui::LogicalDisplayId displayId, DeviceId deviceId) {
913     std::scoped_lock _l(getLock());
914     if (deviceId < 0) {
915         LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
916         return false;
917     }
918     const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
919     if (!info) {
920         LOG(WARNING) << "No input device info found for id " << deviceId
921                      << ". Cannot set pointer icon.";
922         return false;
923     }
924     const uint32_t sources = info->getSources();
925 
926     if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
927         auto it = mDrawingTabletPointersByDevice.find(deviceId);
928         if (it != mDrawingTabletPointersByDevice.end()) {
929             setIconForController(icon, *it->second);
930             return true;
931         }
932     }
933     if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
934         auto it = mStylusPointersByDevice.find(deviceId);
935         if (it != mStylusPointersByDevice.end()) {
936             if (mShowTouchesEnabled) {
937                 // If an app doesn't override the icon for the hovering stylus, show the hover icon.
938                 auto* style = std::get_if<PointerIconStyle>(&icon);
939                 if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) {
940                     *style = PointerIconStyle::TYPE_SPOT_HOVER;
941                 }
942             }
943             setIconForController(icon, *it->second);
944             return true;
945         }
946     }
947     if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
948         auto it = mMousePointersByDisplay.find(displayId);
949         if (it != mMousePointersByDisplay.end()) {
950             setIconForController(icon, *it->second);
951             return true;
952         } else {
953             LOG(WARNING) << "No mouse pointer controller found for display " << displayId
954                          << ", device " << deviceId << ".";
955             return false;
956         }
957     }
958     LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
959                  << ".";
960     return false;
961 }
962 
setPointerIconVisibility(ui::LogicalDisplayId displayId,bool visible)963 void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
964     std::scoped_lock lock(getLock());
965     if (visible) {
966         mDisplaysWithPointersHidden.erase(displayId);
967         // We do not unfade the icons here, because we don't know when the last event happened.
968         return;
969     }
970 
971     mDisplaysWithPointersHidden.emplace(displayId);
972 
973     // Hide any icons that are currently visible on the display.
974     if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
975         const auto& [_, controller] = *it;
976         controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
977     }
978     for (const auto& [_, controller] : mStylusPointersByDevice) {
979         if (controller->getDisplayId() == displayId) {
980             controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
981         }
982     }
983 }
984 
setFocusedDisplay(ui::LogicalDisplayId displayId)985 void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
986     std::scoped_lock lock(getLock());
987     mCurrentFocusedDisplay = displayId;
988 }
989 
setAccessibilityPointerMotionFilterEnabled(bool enabled)990 void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
991     std::scoped_lock _l(getLock());
992     mPointerMotionFilterEnabled = enabled;
993 }
994 
getMouseControllerConstructor(ui::LogicalDisplayId displayId)995 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
996         ui::LogicalDisplayId displayId) {
997     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
998             [this, displayId]() REQUIRES(getLock()) {
999                 auto pc = mPolicy.createPointerController(
1000                         PointerControllerInterface::ControllerType::MOUSE);
1001                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
1002                     pc->setDisplayViewport(*viewport);
1003                 }
1004                 return pc;
1005             };
1006     return ConstructorDelegate(std::move(ctor));
1007 }
1008 
getStylusControllerConstructor(ui::LogicalDisplayId displayId)1009 PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
1010         ui::LogicalDisplayId displayId) {
1011     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
1012             [this, displayId]() REQUIRES(getLock()) {
1013                 auto pc = mPolicy.createPointerController(
1014                         PointerControllerInterface::ControllerType::STYLUS);
1015                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
1016                     pc->setDisplayViewport(*viewport);
1017                 }
1018                 return pc;
1019             };
1020     return ConstructorDelegate(std::move(ctor));
1021 }
1022 
1023 std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,const DisplayTopologyPosition sourceBoundary,int32_t sourceCursorOffsetPx) const1024 PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
1025                                                    const DisplayTopologyPosition sourceBoundary,
1026                                                    int32_t sourceCursorOffsetPx) const {
1027     const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
1028     if (sourceNode == mTopology.graph.end()) {
1029         // Topology is likely out of sync with viewport info, wait for it to be updated
1030         LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
1031         return std::nullopt;
1032     }
1033     for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) {
1034         if (adjacentDisplay.position != sourceBoundary) {
1035             continue;
1036         }
1037         const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId);
1038         if (adjacentViewport == nullptr) {
1039             // Topology is likely out of sync with viewport info, wait for them to be updated
1040             LOG(WARNING) << "Cannot find viewport for adjacent display "
1041                          << adjacentDisplay.displayId << "of source display " << sourceDisplayId;
1042             continue;
1043         }
1044         // As displays can have different densities we need to do all calculations in
1045         // density-independent-pixels a.k.a. dp values.
1046         const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId);
1047         const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId);
1048         const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity);
1049         const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP ||
1050                         sourceBoundary == DisplayTopologyPosition::BOTTOM
1051                 ? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft)
1052                 : (adjacentViewport->logicalBottom - adjacentViewport->logicalTop);
1053         const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity);
1054         // Target position must be within target display boundary.
1055         // Cursor should also be able to cross displays when only display corners are touching and
1056         // there may be zero overlapping pixels. To accommodate this we have margin of one pixel
1057         // around the end of the overlapping edge.
1058         if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp &&
1059             sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) {
1060             const int destinationOffsetPx =
1061                     dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity);
1062             return std::make_pair(adjacentViewport, destinationOffsetPx);
1063         }
1064     }
1065     return std::nullopt;
1066 }
1067 
filterPointerMotionForAccessibilityLocked(const vec2 & current,const vec2 & delta,const ui::LogicalDisplayId & displayId)1068 vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
1069         const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
1070     if (!mPointerMotionFilterEnabled) {
1071         return delta;
1072     }
1073     std::optional<vec2> filterResult =
1074             mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
1075     if (!filterResult.has_value()) {
1076         // Disable filter when there's any error.
1077         mPointerMotionFilterEnabled = false;
1078         return delta;
1079     }
1080     return *filterResult;
1081 }
1082 
1083 // --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
1084 
onWindowInfosChanged(const gui::WindowInfosUpdate & windowInfosUpdate)1085 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
1086         const gui::WindowInfosUpdate& windowInfosUpdate) {
1087     std::scoped_lock _l(mLock);
1088     if (mPointerChoreographer == nullptr) {
1089         return;
1090     }
1091     auto newPrivacySensitiveDisplays =
1092             getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
1093 
1094     // PointerChoreographer uses Listener's lock.
1095     base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
1096     if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
1097         mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
1098         mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
1099     }
1100 }
1101 
setInitialDisplayInfosLocked(const std::vector<gui::WindowInfo> & windowInfos)1102 void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
1103         const std::vector<gui::WindowInfo>& windowInfos) {
1104     mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
1105 }
1106 
1107 std::unordered_set<ui::LogicalDisplayId /*displayId*/>
getPrivacySensitiveDisplaysLocked()1108 PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplaysLocked() {
1109     return mPrivacySensitiveDisplays;
1110 }
1111 
1112 void PointerChoreographer::PointerChoreographerDisplayInfoListener::
onPointerChoreographerDestroyed()1113         onPointerChoreographerDestroyed() {
1114     std::scoped_lock _l(mLock);
1115     mPointerChoreographer = nullptr;
1116 }
1117 
1118 } // namespace android
1119