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