Add method to mark displays secure to hide pointer indicators
In this CL adds a method to mark some displays secure, it will be used
to allow pointer controller to hide touch indicators from the mirrored
displays.
Test: atest PointerChoreographerTest
Bug: 325252005
Change-Id: Iafe7fbc62de39f9fc40119238d7404b97068516a
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 4dc2737..a3d0c2b 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -17,7 +17,12 @@
#define LOG_TAG "PointerChoreographer"
#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#if defined(__ANDROID__)
+#include <gui/SurfaceComposerClient.h>
+#endif
#include <input/PrintTools.h>
+#include <unordered_set>
#include "PointerChoreographer.h"
@@ -25,6 +30,10 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+static const bool HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS =
+ input_flags::hide_pointer_indicators_for_secure_windows();
+
namespace {
bool isFromMouse(const NotifyMotionArgs& args) {
@@ -96,6 +105,14 @@
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false) {}
+PointerChoreographer::~PointerChoreographer() {
+ std::scoped_lock _l(mLock);
+ if (mWindowInfoListener == nullptr) {
+ return;
+ }
+ mWindowInfoListener->onPointerChoreographerDestroyed();
+}
+
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
PointerDisplayChange pointerDisplayChange;
@@ -231,6 +248,7 @@
auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
getMouseControllerConstructor(
args.displayId));
+ // TODO (b/325252005): Add handing for drawing tablets mouse pointer controller
PointerControllerInterface& pc = *it->second;
@@ -268,7 +286,11 @@
}
// Get the touch pointer controller for the device, or create one if it doesn't exist.
- auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+ auto [it, controllerAdded] =
+ mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+ if (controllerAdded) {
+ onControllerAddedOrRemoved();
+ }
PointerControllerInterface& pc = *it->second;
@@ -306,6 +328,7 @@
auto [it, _] =
mStylusPointersByDevice.try_emplace(args.deviceId,
getStylusControllerConstructor(args.displayId));
+ // TODO (b/325252005): Add handing for stylus pointer controller
PointerControllerInterface& pc = *it->second;
@@ -345,6 +368,31 @@
mTouchPointersByDevice.erase(args.deviceId);
mStylusPointersByDevice.erase(args.deviceId);
mDrawingTabletPointersByDevice.erase(args.deviceId);
+ onControllerAddedOrRemoved();
+}
+
+void PointerChoreographer::onControllerAddedOrRemoved() {
+ if (!HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS) {
+ return;
+ }
+ bool requireListener = !mTouchPointersByDevice.empty();
+ // TODO (b/325252005): Update for other types of pointer controllers
+
+ if (requireListener && mWindowInfoListener == nullptr) {
+ mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
+ auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
+ std::vector<android::gui::DisplayInfo>{});
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener,
+ &initialInfo);
+#endif
+ onWindowInfosChangedLocked(initialInfo.first);
+ } else if (!requireListener && mWindowInfoListener != nullptr) {
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener);
+#endif
+ mWindowInfoListener = nullptr;
+ }
}
void PointerChoreographer::notifyPointerCaptureChanged(
@@ -358,6 +406,12 @@
mNextListener.notify(args);
}
+void PointerChoreographer::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>& windowInfos) {
+ std::scoped_lock _l(mLock);
+ onWindowInfosChangedLocked(windowInfos);
+}
+
void PointerChoreographer::dump(std::string& dump) {
std::scoped_lock _l(mLock);
@@ -410,6 +464,7 @@
if (it == mMousePointersByDisplay.end()) {
it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
.first;
+ // TODO (b/325252005): Add handing for mouse pointer controller
}
return {displayId, *it->second};
@@ -450,6 +505,8 @@
auto [mousePointerIt, isNewMousePointer] =
mMousePointersByDisplay.try_emplace(displayId,
getMouseControllerConstructor(displayId));
+ // TODO (b/325252005): Add handing for mouse pointer controller
+
mMouseDevices.emplace(info.getId());
if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -488,6 +545,8 @@
mInputDeviceInfos.end();
});
+ onControllerAddedOrRemoved();
+
// Check if we need to notify the policy if there's a change on the pointer display ID.
return calculatePointerDisplayChangeToNotify();
}
@@ -652,6 +711,31 @@
return false;
}
+void PointerChoreographer::onWindowInfosChangedLocked(
+ const std::vector<android::gui::WindowInfo>& windowInfos) {
+ // Mark all spot controllers secure on displays containing secure windows and
+ // remove secure flag from others if required
+ std::unordered_set<int32_t> privacySensitiveDisplays;
+ std::unordered_set<int32_t> allDisplayIds;
+ for (const auto& windowInfo : windowInfos) {
+ allDisplayIds.insert(windowInfo.displayId);
+ if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
+ privacySensitiveDisplays.insert(windowInfo.displayId);
+ }
+ }
+
+ for (auto& it : mTouchPointersByDevice) {
+ auto& pc = it.second;
+ for (int32_t displayId : allDisplayIds) {
+ pc->setSkipScreenshot(displayId,
+ privacySensitiveDisplays.find(displayId) !=
+ privacySensitiveDisplays.end());
+ }
+ }
+ // TODO (b/325252005): update skip screenshot flag for other types of pointer controllers
+}
+
void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
std::scoped_lock lock(mLock);
if (visible) {
@@ -702,4 +786,18 @@
return ConstructorDelegate(std::move(ctor));
}
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
+ const gui::WindowInfosUpdate& windowInfosUpdate) {
+ std::scoped_lock _l(mListenerLock);
+ if (mPointerChoreographer != nullptr) {
+ mPointerChoreographer->onWindowInfosChanged(windowInfosUpdate.windowInfos);
+ }
+}
+
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::
+ onPointerChoreographerDestroyed() {
+ std::scoped_lock _l(mListenerLock);
+ mPointerChoreographer = nullptr;
+}
+
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index a3c210e..b29d9cd 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -21,6 +21,7 @@
#include "PointerChoreographerPolicyInterface.h"
#include <android-base/thread_annotations.h>
+#include <gui/WindowInfosListener.h>
#include <type_traits>
namespace android {
@@ -83,7 +84,7 @@
public:
explicit PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface&);
- ~PointerChoreographer() override = default;
+ ~PointerChoreographer() override;
void setDefaultMouseDisplayId(int32_t displayId) override;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
@@ -106,6 +107,9 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ // Public because it's also used by tests to simulate the WindowInfosListener callback
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos);
+
void dump(std::string& dump) override;
private:
@@ -127,6 +131,22 @@
void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processDeviceReset(const NotifyDeviceResetArgs& args);
+ void onControllerAddedOrRemoved() REQUIRES(mLock);
+ void onWindowInfosChangedLocked(const std::vector<android::gui::WindowInfo>& windowInfos)
+ REQUIRES(mLock);
+
+ class PointerChoreographerDisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
+ : mPointerChoreographer(pc){};
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
+ void onPointerChoreographerDestroyed();
+
+ private:
+ std::mutex mListenerLock;
+ PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+ };
+ sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener GUARDED_BY(mLock);
using ControllerConstructor =
ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index c44486f..6b26e81 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -143,6 +143,11 @@
/* Sets the custom pointer icon for mice or styluses. */
virtual void setCustomPointerIcon(const SpriteIcon& icon) = 0;
+
+ /* Sets the flag to skip screenshot of the pointer indicators on the display matching the
+ * provided displayId.
+ */
+ virtual void setSkipScreenshot(int32_t displayId, bool skip) = 0;
};
} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index dc199e2..28d4b67 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -76,6 +76,14 @@
mCustomIconStyle = icon.style;
}
+void FakePointerController::setSkipScreenshot(int32_t displayId, bool skip) {
+ if (skip) {
+ mDisplaysToSkipScreenshot.insert(displayId);
+ } else {
+ mDisplaysToSkipScreenshot.erase(displayId);
+ }
+};
+
void FakePointerController::assertViewportSet(int32_t displayId) {
ASSERT_TRUE(mDisplayId);
ASSERT_EQ(displayId, mDisplayId);
@@ -117,6 +125,14 @@
ASSERT_EQ(std::nullopt, mCustomIconStyle);
}
+void FakePointerController::assertIsHiddenOnMirroredDisplays(int32_t displayId, bool isHidden) {
+ if (isHidden) {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end());
+ } else {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end());
+ }
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 536b447..b5b982e 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -21,6 +21,7 @@
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <utils/BitSet.h>
+#include <unordered_set>
namespace android {
@@ -45,6 +46,7 @@
void setDisplayViewport(const DisplayViewport& viewport) override;
void updatePointerIcon(PointerIconStyle iconId) override;
void setCustomPointerIcon(const SpriteIcon& icon) override;
+ void setSkipScreenshot(int32_t displayId, bool skip) override;
void fade(Transition) override;
void assertViewportSet(int32_t displayId);
@@ -55,6 +57,7 @@
void assertPointerIconNotSet();
void assertCustomPointerIconSet(PointerIconStyle iconId);
void assertCustomPointerIconNotSet();
+ void assertIsHiddenOnMirroredDisplays(int32_t displayId, bool isHidden);
bool isPointerShown();
private:
@@ -77,6 +80,7 @@
std::optional<PointerIconStyle> mCustomIconStyle;
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+ std::unordered_set<int32_t> mDisplaysToSkipScreenshot;
};
} // namespace android
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 11c6b7e..d81f8a0 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -15,7 +15,8 @@
*/
#include "../PointerChoreographer.h"
-
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <deque>
#include <vector>
@@ -27,6 +28,8 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
using ControllerType = PointerControllerInterface::ControllerType;
using testing::AllOf;
@@ -1587,6 +1590,76 @@
firstMousePc->assertPointerIconNotSet();
}
+TEST_F_WITH_FLAGS(PointerChoreographerTest, HidesTouchSpotsOnMirroredDisplaysForSecureWindow,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch events to create PointerController
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // By default touch indicators should not be hidden
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // adding secure window on display should set flag to hide pointer indicators on corresponding
+ // mirrored display
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/true);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // removing the secure window should reset the state
+ windowInfo.inputConfig.clear(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY);
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+}
+
+TEST_F_WITH_FLAGS(PointerChoreographerTest,
+ DoesNotHidesTouchSpotsOnMirroredDisplaysForInvisibleWindow,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch events to create PointerController
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // By default touch indicators should not be hidden
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // adding secure but hidden window on display should still not set flag to hide pointer
+ // indicators
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_VISIBLE;
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+}
+
TEST_P(StylusTestFixture, SetsPointerIconForStylus) {
const auto& [name, source, controllerType] = GetParam();
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 7898126..2602ebb 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -289,6 +289,7 @@
void setDisplayViewport(const DisplayViewport& displayViewport) override {}
void updatePointerIcon(PointerIconStyle iconId) override {}
void setCustomPointerIcon(const SpriteIcon& icon) override {}
+ void setSkipScreenshot(int32_t displayId, bool skip) override{};
std::string dump() override { return ""; }
};