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