Hide touch indicators on mirrored displays if a secure window is present

Utilise ISurfaceComposerClient::eSkipScreenshot to remove the tap
indicators from mirrored displays when a secure window is present.

End-to-end test will be added in an upcoming CL.

Test: manual test & atest PointerChoreographerTest PointerControllerTest
Bug: 325252005

Change-Id: I72a77b1a1b2c02a5e94f05e67d0cd39588086c81
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index f9dc5fa..933a33e 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -272,7 +272,10 @@
     if (it == mLocked.spotControllers.end()) {
         mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
     }
-    mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
+    bool skipScreenshot = mLocked.displaysToSkipScreenshot.find(displayId) !=
+            mLocked.displaysToSkipScreenshot.end();
+    mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits,
+                                                   skipScreenshot);
 }
 
 void PointerController::clearSpots() {
@@ -352,6 +355,17 @@
     mCursorController.setCustomPointerIcon(icon);
 }
 
+void PointerController::setSkipScreenshot(int32_t displayId, bool skip) {
+    if (!mEnabled) return;
+
+    std::scoped_lock lock(getLock());
+    if (skip) {
+        mLocked.displaysToSkipScreenshot.insert(displayId);
+    } else {
+        mLocked.displaysToSkipScreenshot.erase(displayId);
+    }
+}
+
 void PointerController::doInactivityTimeout() {
     fade(Transition::GRADUAL);
 }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 6ee5707..d76ca5d 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -67,6 +67,7 @@
     void clearSpots() override;
     void updatePointerIcon(PointerIconStyle iconId) override;
     void setCustomPointerIcon(const SpriteIcon& icon) override;
+    void setSkipScreenshot(int32_t displayId, bool skip) override;
 
     virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void doInactivityTimeout();
@@ -115,6 +116,7 @@
 
         std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
+        std::unordered_set<int32_t /* displayId */> displaysToSkipScreenshot;
     } mLocked GUARDED_BY(getLock());
 
     class DisplayInfoListener : public gui::WindowInfosListener {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index a63453d..0baa929 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -129,7 +129,7 @@
             update.state.surfaceVisible = false;
             update.state.surfaceControl =
                     obtainSurface(update.state.surfaceWidth, update.state.surfaceHeight,
-                                  update.state.displayId);
+                                  update.state.displayId, update.state.skipScreenshot);
             if (update.state.surfaceControl != NULL) {
                 update.surfaceChanged = surfaceChanged = true;
             }
@@ -209,7 +209,7 @@
               (update.state.dirty &
                (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER |
                 DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID | DIRTY_ICON_STYLE |
-                DIRTY_DRAW_DROP_SHADOW))))) {
+                DIRTY_DRAW_DROP_SHADOW | DIRTY_SKIP_SCREENSHOT))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -260,6 +260,14 @@
                 t.setLayer(update.state.surfaceControl, surfaceLayer);
             }
 
+            if (wantSurfaceVisibleAndDrawn &&
+                (becomingVisible || (update.state.dirty & DIRTY_SKIP_SCREENSHOT))) {
+                int32_t flags =
+                        update.state.skipScreenshot ? ISurfaceComposerClient::eSkipScreenshot : 0;
+                t.setFlags(update.state.surfaceControl, flags,
+                           ISurfaceComposerClient::eSkipScreenshot);
+            }
+
             if (becomingVisible) {
                 t.show(update.state.surfaceControl);
 
@@ -332,8 +340,8 @@
     }
 }
 
-sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height,
-                                                   int32_t displayId) {
+sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height, int32_t displayId,
+                                                   bool hideOnMirrored) {
     ensureSurfaceComposerClient();
 
     const sp<SurfaceControl> parent = mParentSurfaceProvider(displayId);
@@ -341,11 +349,13 @@
         ALOGE("Failed to get the parent surface for pointers on display %d", displayId);
     }
 
+    int32_t createFlags = ISurfaceComposerClient::eHidden | ISurfaceComposerClient::eCursorWindow;
+    if (hideOnMirrored) {
+        createFlags |= ISurfaceComposerClient::eSkipScreenshot;
+    }
     const sp<SurfaceControl> surfaceControl =
             mSurfaceComposerClient->createSurface(String8("Sprite"), width, height,
-                                                  PIXEL_FORMAT_RGBA_8888,
-                                                  ISurfaceComposerClient::eHidden |
-                                                          ISurfaceComposerClient::eCursorWindow,
+                                                  PIXEL_FORMAT_RGBA_8888, createFlags,
                                                   parent ? parent->getHandle() : nullptr);
     if (surfaceControl == nullptr || !surfaceControl->isValid()) {
         ALOGE("Error creating sprite surface.");
@@ -474,6 +484,15 @@
     }
 }
 
+void SpriteController::SpriteImpl::setSkipScreenshot(bool skip) {
+    AutoMutex _l(mController.mLock);
+
+    if (mLocked.state.skipScreenshot != skip) {
+        mLocked.state.skipScreenshot = skip;
+        invalidateLocked(DIRTY_SKIP_SCREENSHOT);
+    }
+}
+
 void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
     bool wasDirty = mLocked.state.dirty;
     mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 35776e9..4e4ba65 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -96,6 +96,10 @@
 
     /* Sets the id of the display where the sprite should be shown. */
     virtual void setDisplayId(int32_t displayId) = 0;
+
+    /* Sets the flag to hide sprite on mirrored displays.
+     * This will add ISurfaceComposerClient::eSkipScreenshot flag to the sprite. */
+    virtual void setSkipScreenshot(bool skip) = 0;
 };
 
 /*
@@ -152,6 +156,7 @@
         DIRTY_DISPLAY_ID = 1 << 7,
         DIRTY_ICON_STYLE = 1 << 8,
         DIRTY_DRAW_DROP_SHADOW = 1 << 9,
+        DIRTY_SKIP_SCREENSHOT = 1 << 10,
     };
 
     /* Describes the state of a sprite.
@@ -182,6 +187,7 @@
         int32_t surfaceHeight;
         bool surfaceDrawn;
         bool surfaceVisible;
+        bool skipScreenshot;
 
         inline bool wantSurfaceVisible() const {
             return visible && alpha > 0.0f && icon.isValid();
@@ -209,6 +215,7 @@
         virtual void setAlpha(float alpha);
         virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
         virtual void setDisplayId(int32_t displayId);
+        virtual void setSkipScreenshot(bool skip);
 
         inline const SpriteState& getStateLocked() const {
             return mLocked.state;
@@ -272,7 +279,8 @@
     void doDisposeSurfaces();
 
     void ensureSurfaceComposerClient();
-    sp<SurfaceControl> obtainSurface(int32_t width, int32_t height, int32_t displayId);
+    sp<SurfaceControl> obtainSurface(int32_t width, int32_t height, int32_t displayId,
+                                     bool hideOnMirrored);
 };
 
 } // namespace android
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index 99952aa..530d541 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -40,12 +40,13 @@
 // --- Spot ---
 
 void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float newX, float newY,
-                                             int32_t displayId) {
+                                             int32_t displayId, bool skipScreenshot) {
     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
     sprite->setAlpha(alpha);
     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
     sprite->setPosition(newX, newY);
     sprite->setDisplayId(displayId);
+    sprite->setSkipScreenshot(skipScreenshot);
     x = newX;
     y = newY;
 
@@ -84,7 +85,7 @@
 }
 
 void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
-                                   BitSet32 spotIdBits) {
+                                   BitSet32 spotIdBits, bool skipScreenshot) {
 #if DEBUG_SPOT_UPDATES
     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
@@ -116,7 +117,7 @@
             spot = createAndAddSpotLocked(id, mLocked.displaySpots);
         }
 
-        spot->updateSprite(&icon, x, y, mDisplayId);
+        spot->updateSprite(&icon, x, y, mDisplayId, skipScreenshot);
     }
 
     for (Spot* spot : mLocked.displaySpots) {
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
index 5bbc75d..608653c 100644
--- a/libs/input/TouchSpotController.h
+++ b/libs/input/TouchSpotController.h
@@ -32,7 +32,7 @@
     TouchSpotController(int32_t displayId, PointerControllerContext& context);
     ~TouchSpotController();
     void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
-                  BitSet32 spotIdBits);
+                  BitSet32 spotIdBits, bool skipScreenshot);
     void clearSpots();
 
     void reloadSpotResources();
@@ -59,7 +59,8 @@
                 y(0.0f),
                 mLastIcon(nullptr) {}
 
-        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
+        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId,
+                          bool skipScreenshot);
         void dump(std::string& out, const char* prefix = "") const;
 
     private:
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a1bb5b3..fcf226c 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -372,6 +372,45 @@
             << "The pointer display changes to invalid when PointerController is destroyed.";
 }
 
+TEST_F(PointerControllerTest, updatesSkipScreenshotFlagForTouchSpots) {
+    ensureDisplayViewportIsSet();
+
+    PointerCoords testSpotCoords;
+    testSpotCoords.clear();
+    testSpotCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+    testSpotCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+    BitSet32 testIdBits;
+    testIdBits.markBit(0);
+    std::array<uint32_t, MAX_POINTER_ID + 1> testIdToIndex;
+
+    sp<MockSprite> testSpotSprite(new NiceMock<MockSprite>);
+
+    // By default sprite is not marked secure
+    EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testSpotSprite));
+    EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
+
+    // Update spots to sync state with sprite
+    mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
+                                 ADISPLAY_ID_DEFAULT);
+    testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
+
+    // Marking the display to skip screenshot should update sprite as well
+    mPointerController->setSkipScreenshot(ADISPLAY_ID_DEFAULT, true);
+    EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
+
+    // Update spots to sync state with sprite
+    mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
+                                 ADISPLAY_ID_DEFAULT);
+    testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
+
+    // Reset flag and verify again
+    mPointerController->setSkipScreenshot(ADISPLAY_ID_DEFAULT, false);
+    EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
+    mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
+                                 ADISPLAY_ID_DEFAULT);
+    testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
+}
+
 class PointerControllerWindowInfoListenerTest : public Test {};
 
 TEST_F(PointerControllerWindowInfoListenerTest,
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
index 013b79c..0867221 100644
--- a/libs/input/tests/mocks/MockSprite.h
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -34,6 +34,7 @@
     MOCK_METHOD(void, setAlpha, (float), (override));
     MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
     MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+    MOCK_METHOD(void, setSkipScreenshot, (bool), (override));
 };
 
 }  // namespace android