Merge "Refactor axis initialization logic"
diff --git a/data/etc/android.software.opengles.deqp.level-2022-03-01.xml b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..0a11835
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+     dEQP tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+    <feature name="android.software.opengles.deqp.level" version="132514561" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
new file mode 100644
index 0000000..8deebc0
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2022-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+     tests associated with date 2022-03-01 (0x07E60301). -->
+<permissions>
+    <feature name="android.software.vulkan.deqp.level" version="132514561" />
+</permissions>
diff --git a/include/input/Input.h b/include/input/Input.h
index e1cacac..ddff144 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -382,7 +382,6 @@
     // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
     // axes, however the window scaling will not.
     void scale(float globalScale, float windowXScale, float windowYScale);
-    void applyOffset(float xOffset, float yOffset);
 
     void transform(const ui::Transform& transform);
 
@@ -572,7 +571,7 @@
 
     inline float getYOffset() const { return mTransform.ty(); }
 
-    inline ui::Transform getTransform() const { return mTransform; }
+    inline const ui::Transform& getTransform() const { return mTransform; }
 
     int getSurfaceRotation() const;
 
@@ -590,7 +589,7 @@
 
     void setCursorPosition(float x, float y);
 
-    ui::Transform getRawTransform() const { return mRawTransform; }
+    inline const ui::Transform& getRawTransform() const { return mRawTransform; }
 
     static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index e5dcdea..ec3587b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -66,6 +66,7 @@
     host_supported: true,
     srcs: [
         ":guiconstants_aidl",
+        ":inputconstants_aidl",
         "android/gui/DisplayInfo.aidl",
         "android/gui/FocusRequest.aidl",
         "android/gui/InputApplicationInfo.aidl",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 85a4b67..34faf87 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -164,8 +164,7 @@
     mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
-    BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width,
-             mSize.height, mFormat, mTransformHint);
+    BQA_LOGV("BLASTBufferQueue created");
 }
 
 BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
@@ -182,14 +181,14 @@
     BQA_LOGE("Applying pending transactions on dtor %d",
              static_cast<uint32_t>(mPendingTransactions.size()));
     SurfaceComposerClient::Transaction t;
-    for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
-        t.merge(std::move(transaction));
-    }
-    t.apply();
+    mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+    t.setApplyToken(mApplyToken).apply();
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
                               int32_t format, SurfaceComposerClient::Transaction* outTransaction) {
+    LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
+
     std::unique_lock _lock{mMutex};
     if (mFormat != format) {
         mFormat = format;
@@ -197,21 +196,20 @@
     }
 
     SurfaceComposerClient::Transaction t;
-    const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
+    const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
     bool applyTransaction = false;
 
     // Always update the native object even though they might have the same layer handle, so we can
     // get the updated transform hint from WM.
     mSurfaceControl = surface;
-    if (mSurfaceControl != nullptr) {
-        if (setBackpressureFlag) {
-            t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
-                       layer_state_t::eEnableBackpressure);
-            applyTransaction = true;
-        }
-        mTransformHint = mSurfaceControl->getTransformHint();
-        mBufferItemConsumer->setTransformHint(mTransformHint);
+    if (surfaceControlChanged) {
+        BQA_LOGD("Updating SurfaceControl without recreating BBQ");
+        t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
+                   layer_state_t::eEnableBackpressure);
+        applyTransaction = true;
     }
+    mTransformHint = mSurfaceControl->getTransformHint();
+    mBufferItemConsumer->setTransformHint(mTransformHint);
     BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format,
              mTransformHint);
 
@@ -225,11 +223,9 @@
             mSize = mRequestedSize;
             SurfaceComposerClient::Transaction* destFrameTransaction =
                     (outTransaction) ? outTransaction : &t;
-            if (mSurfaceControl != nullptr) {
-                destFrameTransaction->setDestinationFrame(mSurfaceControl,
-                                                          Rect(0, 0, newSize.getWidth(),
-                                                               newSize.getHeight()));
-            }
+            destFrameTransaction->setDestinationFrame(mSurfaceControl,
+                                                      Rect(0, 0, newSize.getWidth(),
+                                                           newSize.getHeight()));
             applyTransaction = true;
         }
     }
@@ -640,7 +636,7 @@
 
     // add to shadow queue
     mNumFrameAvailable++;
-    if (mWaitForTransactionCallback && mNumFrameAvailable == 2) {
+    if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) {
         acquireAndReleaseBuffer();
     }
     ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -717,7 +713,7 @@
 // of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
 bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
     int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
-    return mNumAcquired == maxAcquiredBuffers;
+    return mNumAcquired >= maxAcquiredBuffers;
 }
 
 class BBQSurface : public Surface {
@@ -991,4 +987,54 @@
     return mLastAcquiredFrameNumber;
 }
 
+void BLASTBufferQueue::abandon() {
+    std::unique_lock _lock{mMutex};
+    // flush out the shadow queue
+    while (mNumFrameAvailable > 0) {
+        acquireAndReleaseBuffer();
+    }
+
+    // Clear submitted buffer states
+    mNumAcquired = 0;
+    mSubmitted.clear();
+    mPendingRelease.clear();
+
+    if (!mPendingTransactions.empty()) {
+        BQA_LOGD("Applying pending transactions on abandon %d",
+                 static_cast<uint32_t>(mPendingTransactions.size()));
+        SurfaceComposerClient::Transaction t;
+        mergePendingTransactions(&t, std::numeric_limits<uint64_t>::max() /* frameNumber */);
+        t.setApplyToken(mApplyToken).apply();
+    }
+
+    // Clear sync states
+    if (mWaitForTransactionCallback) {
+        BQA_LOGD("mWaitForTransactionCallback cleared");
+        mWaitForTransactionCallback = false;
+    }
+
+    if (mSyncTransaction != nullptr) {
+        BQA_LOGD("mSyncTransaction cleared mAcquireSingleBuffer=%s",
+                 mAcquireSingleBuffer ? "true" : "false");
+        mSyncTransaction = nullptr;
+        mAcquireSingleBuffer = false;
+    }
+
+    // abandon buffer queue
+    if (mBufferItemConsumer != nullptr) {
+        mBufferItemConsumer->abandon();
+        mBufferItemConsumer->setFrameAvailableListener(nullptr);
+        mBufferItemConsumer->setBufferFreedListener(nullptr);
+        mBufferItemConsumer->setBlastBufferQueue(nullptr);
+    }
+    mBufferItemConsumer = nullptr;
+    mConsumer = nullptr;
+    mProducer = nullptr;
+}
+
+bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const {
+    std::unique_lock _lock{mMutex};
+    return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
+}
+
 } // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index aafa5e4..cf04ec8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1104,7 +1104,8 @@
     }
     if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
         (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
-        (mask & layer_state_t::eEnableBackpressure)) {
+        (mask & layer_state_t::eEnableBackpressure) ||
+        (mask & layer_state_t::eLayerIsDisplayDecoration)) {
         s->what |= layer_state_t::eFlagsChanged;
     }
     s->flags &= ~mask;
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index e92be01..8d356aa 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -47,6 +47,10 @@
     return inputFeatures.test(Feature::SPY);
 }
 
+bool WindowInfo::interceptsStylus() const {
+    return inputFeatures.test(Feature::INTERCEPTS_STYLUS);
+}
+
 bool WindowInfo::overlaps(const WindowInfo* other) const {
     return frameLeft < other->frameRight && frameRight > other->frameLeft &&
             frameTop < other->frameBottom && frameBottom > other->frameTop;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 8a2f392..8b2310d 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -82,6 +82,7 @@
         return mProducer;
     }
     sp<Surface> getSurface(bool includeSurfaceControlHandle);
+    bool isSameSurfaceControl(const sp<SurfaceControl>& surfaceControl) const;
 
     void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
     void onFrameReplaced(const BufferItem& item) override;
@@ -109,6 +110,7 @@
 
     uint32_t getLastTransformHint() const;
     uint64_t getLastAcquiredFrameNum();
+    void abandon();
 
     virtual ~BLASTBufferQueue();
 
@@ -145,15 +147,15 @@
     std::string mQueuedBufferTrace;
     sp<SurfaceControl> mSurfaceControl;
 
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     std::condition_variable mCallbackCV;
 
     // BufferQueue internally allows 1 more than
     // the max to be acquired
     int32_t mMaxAcquiredBuffers = 1;
 
-    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
-    int32_t mNumAcquired GUARDED_BY(mMutex);
+    int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
+    int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
 
     // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
     // buffer or the buffer has been presented and a new buffer is ready to be presented.
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b01eed4..a0d3162 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -109,6 +109,7 @@
         // set. This blocks the client until all the buffers have been presented. If the buffers
         // have presentation timestamps, then we may drop buffers.
         eEnableBackpressure = 0x100, // ENABLE_BACKPRESSURE
+        eLayerIsDisplayDecoration = 0x200,  // DISPLAY_DECORATION
     };
 
     enum {
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 808afe4..2bfaec8 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/gui/TouchOcclusionMode.h>
+#include <android/os/IInputConstants.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <ftl/Flags.h>
@@ -131,13 +132,23 @@
         ftl_last = FIRST_SYSTEM_WINDOW + 15
     };
 
+    // This is a conversion of os::IInputConstants::InputFeature to an enum backed by an unsigned
+    // type. This indicates that they are flags, so it can be used with ftl/enum.h.
     enum class Feature : uint32_t {
-        DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
-        NO_INPUT_CHANNEL = 1u << 1,
-        DISABLE_USER_ACTIVITY = 1u << 2,
-        DROP_INPUT = 1u << 3,
-        DROP_INPUT_IF_OBSCURED = 1u << 4,
-        SPY = 1u << 5,
+        // clang-format off
+        NO_INPUT_CHANNEL =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::NO_INPUT_CHANNEL),
+        DISABLE_USER_ACTIVITY =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DISABLE_USER_ACTIVITY),
+        DROP_INPUT =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT),
+        DROP_INPUT_IF_OBSCURED =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::DROP_INPUT_IF_OBSCURED),
+        SPY =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::SPY),
+        INTERCEPTS_STYLUS =
+                static_cast<uint32_t>(os::IInputConstants::InputFeature::INTERCEPTS_STYLUS),
+        // clang-format on
     };
 
     /* These values are filled in by the WM and passed through SurfaceFlinger
@@ -218,6 +229,8 @@
 
     bool isSpy() const;
 
+    bool interceptsStylus() const;
+
     bool overlaps(const WindowInfo* other) const;
 
     bool operator==(const WindowInfo& inputChannel) const;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 48b8621..42a32f3 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -1069,6 +1069,94 @@
             checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
 
+// This test will currently fail because the old surfacecontrol will steal the last presented buffer
+// until the old surface control is destroyed. This is not necessarily a bug but to document a
+// limitation with the update API and to test any changes to make the api more robust. The current
+// approach for the client is to recreate the blastbufferqueue when the surfacecontrol updates.
+TEST_F(BLASTBufferQueueTest, DISABLED_DisconnectProducerTest) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    std::vector<sp<SurfaceControl>> surfaceControls;
+    sp<IGraphicBufferProducer> igbProducer;
+    for (int i = 0; i < 10; i++) {
+        sp<SurfaceControl> sc =
+                mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888,
+                                       ISurfaceComposerClient::eFXSurfaceBufferState,
+                                       /*parent*/ nullptr);
+        Transaction()
+                .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply(true);
+        surfaceControls.push_back(sc);
+        adapter.update(sc, mDisplayWidth, mDisplayHeight);
+
+        setUpProducer(adapter, igbProducer);
+        Transaction next;
+        queueBuffer(igbProducer, 0, 255, 0, 0);
+        queueBuffer(igbProducer, 0, 0, 255, 0);
+        adapter.setSyncTransaction(&next, false);
+        queueBuffer(igbProducer, 255, 0, 0, 0);
+
+        CallbackHelper transactionCallback;
+        next.addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+                .apply();
+
+        CallbackData callbackData;
+        transactionCallback.getCallbackData(&callbackData);
+        // capture screen and verify that it is red
+        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_NO_FATAL_FAILURE(
+                checkScreenCapture(255, 0, 0,
+                                   {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+        igbProducer->disconnect(NATIVE_WINDOW_API_CPU);
+    }
+}
+
+// See DISABLED_DisconnectProducerTest
+TEST_F(BLASTBufferQueueTest, DISABLED_UpdateSurfaceControlTest) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    std::vector<sp<SurfaceControl>> surfaceControls;
+    sp<IGraphicBufferProducer> igbProducer;
+    for (int i = 0; i < 10; i++) {
+        sp<SurfaceControl> sc =
+                mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888,
+                                       ISurfaceComposerClient::eFXSurfaceBufferState,
+                                       /*parent*/ nullptr);
+        Transaction()
+                .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply(true);
+        surfaceControls.push_back(sc);
+        adapter.update(sc, mDisplayWidth, mDisplayHeight);
+        setUpProducer(adapter, igbProducer);
+
+        Transaction next;
+        queueBuffer(igbProducer, 0, 255, 0, 0);
+        queueBuffer(igbProducer, 0, 0, 255, 0);
+        adapter.setSyncTransaction(&next, false);
+        queueBuffer(igbProducer, 255, 0, 0, 0);
+
+        CallbackHelper transactionCallback;
+        next.addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+                .apply();
+
+        CallbackData callbackData;
+        transactionCallback.getCallbackData(&callbackData);
+        // capture screen and verify that it is red
+        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_NO_FATAL_FAILURE(
+                checkScreenCapture(255, 0, 0,
+                                   {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+    }
+}
+
 class TestProducerListener : public BnProducerListener {
 public:
     sp<IGraphicBufferProducer> mIgbp;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index f0b97a7..84dba84 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -344,11 +344,6 @@
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
 }
 
-void PointerCoords::applyOffset(float xOffset, float yOffset) {
-    setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
-    setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
-}
-
 #ifdef __linux__
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
     bits = parcel->readInt64();
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 474a1e4..63bf23d 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -53,4 +53,53 @@
      * set of flags, including in input/Input.h and in android/input.h.
      */
     const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
+
+    @Backing(type="int")
+    enum InputFeature {
+        /**
+         * Does not construct an input channel for this window.  The channel will therefore
+         * be incapable of receiving input.
+         */
+        NO_INPUT_CHANNEL = 0x00000002,
+
+        /**
+         * When this window has focus, does not call user activity for all input events so
+         * the application will have to do it itself.  Should only be used by
+         * the keyguard and phone app.
+         *
+         * Should only be used by the keyguard and phone app.
+         */
+        DISABLE_USER_ACTIVITY = 0x00000004,
+
+        /**
+         * Internal flag used to indicate that input should be dropped on this window.
+         */
+        DROP_INPUT = 0x00000008,
+
+        /**
+         * Internal flag used to indicate that input should be dropped on this window if this window
+         * is obscured.
+         */
+        DROP_INPUT_IF_OBSCURED = 0x00000010,
+
+        /**
+         * An input spy window. This window will receive all pointer events within its touchable
+         * area, but will will not stop events from being sent to other windows below it in z-order.
+         * An input event will be dispatched to all spy windows above the top non-spy window at the
+         * event's coordinates.
+         */
+        SPY = 0x00000020,
+
+        /**
+         * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
+         * to receive events from a stylus device within its touchable region. All other pointer
+         * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
+         *
+         * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
+         * not set.
+         *
+         * The window must be a trusted overlay to use this input feature.
+         */
+        INTERCEPTS_STYLUS = 0x00000040,
+    }
 }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 936ecf9..8046bbe 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -214,7 +214,7 @@
                          int32_t edgeFlags, float xPrecision, float yPrecision,
                          float xCursorPosition, float yCursorPosition, nsecs_t downTime,
                          uint32_t pointerCount, const PointerProperties* pointerProperties,
-                         const PointerCoords* pointerCoords, float xOffset, float yOffset)
+                         const PointerCoords* pointerCoords)
       : EventEntry(id, Type::MOTION, eventTime, policyFlags),
         deviceId(deviceId),
         source(source),
@@ -235,9 +235,6 @@
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
-        if (xOffset || yOffset) {
-            this->pointerCoords[i].applyOffset(xOffset, yOffset);
-        }
     }
 }
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 477781a..0f79296 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -184,8 +184,7 @@
                 int32_t metaState, int32_t buttonState, MotionClassification classification,
                 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
                 float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
-                const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-                float xOffset, float yOffset);
+                const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
     std::string getDescription() const override;
 
     ~MotionEntry() override;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6ff2450..ff2e4a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -378,7 +378,7 @@
                                           motionEntry.yPrecision, motionEntry.xCursorPosition,
                                           motionEntry.yCursorPosition, motionEntry.downTime,
                                           motionEntry.pointerCount, motionEntry.pointerProperties,
-                                          pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */);
+                                          pointerCoords.data());
 
     if (motionEntry.injectionState) {
         combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -506,12 +506,6 @@
     return true;
 }
 
-vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
-    const vec2 transformedXy = transform.transform(x, y);
-    const vec2 transformedOrigin = transform.transform(0, 0);
-    return transformedXy - transformedOrigin;
-}
-
 // Returns true if the event type passed as argument represents a user activity.
 bool isUserActivityEvent(const EventEntry& eventEntry) {
     switch (eventEntry.type) {
@@ -530,12 +524,14 @@
 }
 
 // Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y,
+                          bool isStylus) {
     if (windowInfo.displayId != displayId || !windowInfo.visible) {
         return false;
     }
     const auto flags = windowInfo.flags;
-    if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
+    const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();
+    if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
         return false;
     }
     const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
@@ -546,6 +542,12 @@
     return true;
 }
 
+bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
+    return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
+            (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+             entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -939,8 +941,10 @@
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = static_cast<int32_t>(
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+
+        const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
         sp<WindowInfoHandle> touchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, nullptr);
+                findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
         if (touchedWindowHandle != nullptr &&
             touchedWindowHandle->getApplicationToken() !=
                     mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1045,6 +1049,7 @@
 
 sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                 int32_t y, TouchState* touchState,
+                                                                bool isStylus,
                                                                 bool addOutsideTargets,
                                                                 bool ignoreDragWindow) {
     if (addOutsideTargets && touchState == nullptr) {
@@ -1058,7 +1063,7 @@
         }
 
         const WindowInfo& info = *windowHandle->getInfo();
-        if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y)) {
+        if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
             return windowHandle;
         }
 
@@ -1070,16 +1075,15 @@
     return nullptr;
 }
 
-std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(int32_t displayId,
-                                                                                 int32_t x,
-                                                                                 int32_t y) const {
+std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
+        int32_t displayId, int32_t x, int32_t y, bool isStylus) const {
     // Traverse windows from front to back and gather the touched spy windows.
     std::vector<sp<WindowInfoHandle>> spyWindows;
     const auto& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         const WindowInfo& info = *windowHandle->getInfo();
 
-        if (!windowAcceptsTouchAt(info, displayId, x, y)) {
+        if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
             continue;
         }
         if (!info.isSpy()) {
@@ -2062,8 +2066,9 @@
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+        const bool isStylus = isPointerFromStylus(entry, pointerIndex);
         newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
-                                                           isDown /*addOutsideTargets*/);
+                                                           isStylus, isDown /*addOutsideTargets*/);
 
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
@@ -2100,7 +2105,7 @@
         }
 
         std::vector<sp<WindowInfoHandle>> newTouchedWindows =
-                findTouchedSpyWindowsAtLocked(displayId, x, y);
+                findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
         if (newTouchedWindowHandle != nullptr) {
             // Process the foreground window first so that it is the first to receive the event.
             newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2213,9 +2218,11 @@
             const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
             const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
+            const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
             sp<WindowInfoHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
-            newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+            newTouchedWindowHandle =
+                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
 
             // Drop touch events if requested by input feature
             if (newTouchedWindowHandle != nullptr &&
@@ -2477,8 +2484,12 @@
 }
 
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+    // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until we
+    // have an explicit reason to support it.
+    constexpr bool isStylus = false;
+
     const sp<WindowInfoHandle> dropWindow =
-            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
+            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus,
                                       false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
@@ -2511,8 +2522,12 @@
             return;
         }
 
+        // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until
+        // we have an explicit reason to support it.
+        constexpr bool isStylus = false;
+
         const sp<WindowInfoHandle> hoverWindowHandle =
-                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
+                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, isStylus,
                                           false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
         // enqueue drag exit if needed.
         if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
@@ -3815,7 +3830,7 @@
                                           originalMotionEntry.xCursorPosition,
                                           originalMotionEntry.yCursorPosition,
                                           originalMotionEntry.downTime, splitPointerCount,
-                                          splitPointerProperties, splitPointerCoords, 0, 0);
+                                          splitPointerProperties, splitPointerCoords);
 
     if (originalMotionEntry.injectionState) {
         splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -4041,7 +4056,7 @@
                                               args->xPrecision, args->yPrecision,
                                               args->xCursorPosition, args->yCursorPosition,
                                               args->downTime, args->pointerCount,
-                                              args->pointerProperties, args->pointerCoords, 0, 0);
+                                              args->pointerProperties, args->pointerCoords);
 
         if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
             IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
@@ -4271,10 +4286,8 @@
                                                   motionEvent.getRawXCursorPosition(),
                                                   motionEvent.getRawYCursorPosition(),
                                                   motionEvent.getDownTime(), uint32_t(pointerCount),
-                                                  pointerProperties, samplePointerCoords,
-                                                  motionEvent.getXOffset(),
-                                                  motionEvent.getYOffset());
-            transformMotionEntryForInjectionLocked(*injectedEntry);
+                                                  pointerProperties, samplePointerCoords);
+            transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
             injectedEntries.push(std::move(injectedEntry));
             for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
@@ -4293,9 +4306,9 @@
                                                       motionEvent.getRawYCursorPosition(),
                                                       motionEvent.getDownTime(),
                                                       uint32_t(pointerCount), pointerProperties,
-                                                      samplePointerCoords, motionEvent.getXOffset(),
-                                                      motionEvent.getYOffset());
-                transformMotionEntryForInjectionLocked(*nextInjectedEntry);
+                                                      samplePointerCoords);
+                transformMotionEntryForInjectionLocked(*nextInjectedEntry,
+                                                       motionEvent.getTransform());
                 injectedEntries.push(std::move(nextInjectedEntry));
             }
             break;
@@ -4459,35 +4472,28 @@
     }
 }
 
-void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
-    const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
-    if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
-        return;
-    }
-
+void InputDispatcher::transformMotionEntryForInjectionLocked(
+        MotionEntry& entry, const ui::Transform& injectedTransform) const {
     // Input injection works in the logical display coordinate space, but the input pipeline works
     // display space, so we need to transform the injected events accordingly.
     const auto it = mDisplayInfos.find(entry.displayId);
     if (it == mDisplayInfos.end()) return;
-    const auto& transformToDisplay = it->second.transform.inverse();
+    const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
 
     for (uint32_t i = 0; i < entry.pointerCount; i++) {
         PointerCoords& pc = entry.pointerCoords[i];
-        const auto xy = isRelativeMouseEvent
-                ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
-                : transformToDisplay.transform(pc.getXYValue());
-        pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
-        pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+        // Make a copy of the injected coords. We cannot change them in place because some of them
+        // are interdependent (for example, X coordinate might depend on the Y coordinate).
+        PointerCoords injectedCoords = entry.pointerCoords[i];
 
-        // Axes with relative values never represent points on a screen, so they should never have
-        // translation applied. If a device does not report relative values, these values are always
-        // 0, and will remain unaffected by the following operation.
-        const auto rel =
-                transformWithoutTranslation(transformToDisplay,
-                                            pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
-                                            pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
-        pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
-        pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
+        BitSet64 bits(injectedCoords.bits);
+        while (!bits.isEmpty()) {
+            const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
+            const float value =
+                    MotionEvent::calculateTransformedAxisValue(axis, entry.source,
+                                                               transformToDisplay, injectedCoords);
+            pc.setAxisValue(axis, value);
+        }
     }
 }
 
@@ -4685,15 +4691,27 @@
         ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
     }
 
-    // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+    // Check preconditions for new input windows
     for (const sp<WindowInfoHandle>& window : windowInfoHandles) {
-        const bool noInputWindow =
-                window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+        const WindowInfo& info = *window->getInfo();
+
+        // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
+        const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
         if (noInputWindow && window->getToken() != nullptr) {
             ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
                   window->getName().c_str());
             window->releaseChannel();
         }
+
+        // Ensure all spy windows are trusted overlays
+        LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.trustedOverlay,
+                            "%s has feature SPY, but is not a trusted overlay.",
+                            window->getName().c_str());
+
+        // Ensure all stylus interceptors are trusted overlays
+        LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.trustedOverlay,
+                            "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.",
+                            window->getName().c_str());
     }
 
     // Copy old handles for release if they are no longer present.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index fb9c81b..ee50ec5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -234,16 +234,12 @@
     // to transfer focus to a new application.
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
-    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                 int32_t y, TouchState* touchState,
-                                                                 bool addOutsideTargets = false,
-                                                                 bool ignoreDragWindow = false)
-            REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
+            int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
+            bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
 
-    std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(int32_t displayId,
-                                                                                  int32_t x,
-                                                                                  int32_t y) const
-            REQUIRES(mLock);
+    std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
+            int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
 
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
@@ -287,7 +283,9 @@
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
     void setInjectionResult(EventEntry& entry,
                             android::os::InputEventInjectionResult injectionResult);
-    void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
+    void transformMotionEntryForInjectionLocked(MotionEntry&,
+                                                const ui::Transform& injectedTransform) const
+            REQUIRES(mLock);
 
     std::condition_variable mInjectionSyncFinished;
     void incrementPendingForegroundDispatches(EventEntry& entry);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 3bb0bc9..ad3c615 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -296,8 +296,7 @@
                                                   memento.yPrecision, memento.xCursorPosition,
                                                   memento.yCursorPosition, memento.downTime,
                                                   memento.pointerCount, memento.pointerProperties,
-                                                  memento.pointerCoords, 0 /*xOffset*/,
-                                                  0 /*yOffset*/));
+                                                  memento.pointerCoords));
         }
     }
     return events;
@@ -349,8 +348,7 @@
                                                   AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                                   memento.yPrecision, memento.xCursorPosition,
                                                   memento.yCursorPosition, memento.downTime,
-                                                  pointerCount, pointerProperties, pointerCoords,
-                                                  0 /*xOffset*/, 0 /*yOffset*/));
+                                                  pointerCount, pointerProperties, pointerCoords));
         }
 
         memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ba70929..0814bc2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1003,6 +1003,7 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = displayId;
+        mInfo.trustedOverlay = false;
     }
 
     sp<FakeWindowHandle> clone(
@@ -1054,7 +1055,9 @@
 
     void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
 
-    void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; }
+    void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
+
+    void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; }
 
     void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
         mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
@@ -2183,6 +2186,33 @@
     secondWindow->assertNoEvents();
 }
 
+// Ensure that when a MotionEvent that has a custom transform is injected, the post-transformed
+// event should be treated as being in the logical display space.
+TEST_F(InputDispatcherDisplayProjectionTest, InjectionWithTransformInLogicalDisplaySpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+    ui::Transform injectedEventTransform;
+    injectedEventTransform.set(matrix);
+    const vec2 expectedPoint{75, 55}; // The injected point in the logical display space.
+    const vec2 untransformedPoint = injectedEventTransform.inverse().transform(expectedPoint);
+
+    MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                .displayId(ADISPLAY_ID_DEFAULT)
+                                .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                                .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                                 .x(untransformedPoint.x)
+                                                 .y(untransformedPoint.y))
+                                .build();
+    event.transform(matrix);
+
+    injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT);
+
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) {
     auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
 
@@ -6297,6 +6327,7 @@
         sp<FakeWindowHandle> spy =
                 new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
         spy->setInputFeatures(WindowInfo::Feature::SPY);
+        spy->setTrustedOverlay(true);
         spy->addFlags(flags);
         return spy;
     }
@@ -6315,6 +6346,16 @@
 };
 
 /**
+ * Adding a spy window that is not a trusted overlay causes Dispatcher to abort.
+ */
+TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) {
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    spy->setTrustedOverlay(false);
+    ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}),
+                 ".* not a trusted overlay");
+}
+
+/**
  * Input injection into a display with a spy window but no foreground windows should succeed.
  */
 TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
@@ -6474,28 +6515,6 @@
 }
 
 /**
- * When configured to block untrusted touches, events will not be dispatched to windows below a spy
- * window if it is not a trusted overly.
- */
-TEST_F(InputDispatcherSpyWindowTest, BlockUntrustedTouches) {
-    mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
-
-    auto window = createForeground();
-    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
-    window->setOwnerInfo(111, 111);
-    spy->setOwnerInfo(222, 222);
-    spy->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
-
-    // Inject an event outside the spy window's frame and touchable region.
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
-            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-    spy->consumeMotionDown();
-    window->assertNoEvents();
-}
-
-/**
  * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to
  * any other windows - including other spy windows - will also be cancelled.
  */
@@ -6600,4 +6619,97 @@
     spyRight->consumeMotionDown();
 }
 
+class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
+public:
+    std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
+        std::shared_ptr<FakeApplicationHandle> overlayApplication =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> overlay =
+                new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window",
+                                     ADISPLAY_ID_DEFAULT);
+        overlay->setFocusable(false);
+        overlay->setOwnerInfo(111, 111);
+        overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH);
+        overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
+        overlay->setTrustedOverlay(true);
+
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window =
+                new FakeWindowHandle(application, mDispatcher, "Application window",
+                                     ADISPLAY_ID_DEFAULT);
+        window->setFocusable(true);
+        window->setOwnerInfo(222, 222);
+        window->setFlags(WindowInfo::Flag::SPLIT_TOUCH);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+        setFocusedWindow(window);
+        window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+        return {std::move(overlay), std::move(window)};
+    }
+
+    void sendFingerEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{20, 20}});
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+
+    void sendStylusEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{30, 40}});
+        motionArgs.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+};
+
+TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setTrustedOverlay(false);
+    // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort.
+    ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}),
+                 ".* not a trusted overlay");
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    window->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+    window->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index e26c763..d61a4cb 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -295,6 +295,9 @@
         compositionState->compositionType =
                 aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
         return;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+        compositionState->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
     } else {
         // Normal buffer layers
         compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 5543ad5..08dd22d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -39,6 +39,8 @@
 
 #include "DisplayHardware/PowerAdvisor.h"
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
 namespace android::compositionengine::impl {
 
 std::shared_ptr<Display> createDisplay(
@@ -252,7 +254,7 @@
     const auto& hwc = getCompositionEngine().getHwComposer();
     if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
         return hwc.hasDisplayCapability(*halDisplayId,
-                                        hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+                                        DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
 
     return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 94ad6c3..545e2a2 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -685,12 +685,12 @@
 
         case Composition::DEVICE:
         case Composition::SOLID_COLOR:
-        case Composition::DISPLAY_DECORATION:
             result = (to == Composition::CLIENT);
             break;
 
         case Composition::CURSOR:
         case Composition::SIDEBAND:
+        case Composition::DISPLAY_DECORATION:
             result = (to == Composition::CLIENT || to == Composition::DEVICE);
             break;
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 88f00a5..8558a80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -634,6 +634,7 @@
  */
 
 using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
 TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfGpuDisplay) {
     EXPECT_CALL(mHwComposer, hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM))
@@ -646,7 +647,7 @@
 TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
     EXPECT_CALL(mHwComposer,
                 hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
-                                     hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
+                                     DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
             .WillOnce(Return(true));
     EXPECT_TRUE(mDisplay->getSkipColorTransform());
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 0f174db..a590e2a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -43,7 +43,9 @@
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
+    MOCK_CONST_METHOD2(hasDisplayCapability,
+                       bool(HalDisplayId,
+                            aidl::android::hardware::graphics::composer3::DisplayCapability));
 
     MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
     MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index d97a399..1091a75 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -917,15 +917,14 @@
 }
 
 Error AidlComposer::getDisplayCapabilities(Display display,
-                                           std::vector<DisplayCapability>* outCapabilities) {
-    std::vector<AidlDisplayCapability> capabilities;
-    const auto status =
-            mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
+                                           std::vector<AidlDisplayCapability>* outCapabilities) {
+    const auto status = mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display),
+                                                                    outCapabilities);
     if (!status.isOk()) {
         ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+        outCapabilities->clear();
         return static_cast<Error>(status.getServiceSpecificError());
     }
-    *outCapabilities = translate<DisplayCapability>(capabilities);
     return Error::NONE;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 777b295..5c41982 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -38,6 +38,7 @@
 #include <android/hardware/graphics/composer3/ComposerClientWriter.h>
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -182,8 +183,10 @@
     Error setDisplayBrightness(Display display, float brightness) override;
 
     // Composer HAL 2.4
-    Error getDisplayCapabilities(Display display,
-                                 std::vector<DisplayCapability>* outCapabilities) override;
+    Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) override;
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index a6a1e6f..f491a00 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,7 @@
 #include <utils/StrongPointer.h>
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -64,7 +65,6 @@
 using V2_4::IComposerClient;
 using V2_4::VsyncPeriodChangeTimeline;
 using V2_4::VsyncPeriodNanos;
-using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
@@ -207,8 +207,10 @@
     virtual Error setDisplayBrightness(Display display, float brightness) = 0;
 
     // Composer HAL 2.4
-    virtual Error getDisplayCapabilities(Display display,
-                                         std::vector<DisplayCapability>* outCapabilities) = 0;
+    virtual Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) = 0;
     virtual V2_4::Error getDisplayConnectionType(
             Display display, IComposerClient::DisplayConnectionType* outType) = 0;
     virtual V2_4::Error getDisplayVsyncPeriod(Display display,
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 3dcea61..548d839 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -40,6 +40,7 @@
 #include "ComposerHal.h"
 
 using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 
 namespace android {
 
@@ -285,15 +286,15 @@
     return Error::NONE;
 }
 
-bool Display::hasCapability(hal::DisplayCapability capability) const {
+bool Display::hasCapability(DisplayCapability capability) const {
     std::scoped_lock lock(mDisplayCapabilitiesMutex);
     if (mDisplayCapabilities) {
         return mDisplayCapabilities->count(capability) > 0;
     }
 
-    ALOGW("Can't query capability %d."
+    ALOGW("Can't query capability %s."
           " Display Capabilities were not queried from HWC yet",
-          static_cast<int>(capability));
+          to_string(capability).c_str());
 
     return false;
 }
@@ -458,14 +459,14 @@
 
     if (mode == PowerMode::ON) {
         std::call_once(mDisplayCapabilityQueryFlag, [this]() {
-            std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+            std::vector<DisplayCapability> tmpCapabilities;
             auto error =
                     static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
             if (error == Error::NONE) {
                 std::scoped_lock lock(mDisplayCapabilitiesMutex);
                 mDisplayCapabilities.emplace();
                 for (auto capability : tmpCapabilities) {
-                    mDisplayCapabilities->emplace(static_cast<DisplayCapability>(capability));
+                    mDisplayCapabilities->emplace(capability);
                 }
             } else if (error == Error::UNSUPPORTED) {
                 std::scoped_lock lock(mDisplayCapabilitiesMutex);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c2ebd45..731d7f6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -37,6 +37,7 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 namespace android {
 
@@ -82,7 +83,8 @@
     virtual hal::HWDisplayId getId() const = 0;
     virtual bool isConnected() const = 0;
     virtual void setConnected(bool connected) = 0; // For use by Device only
-    virtual bool hasCapability(hal::DisplayCapability) const = 0;
+    virtual bool hasCapability(
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
     virtual bool isVsyncPeriodSwitchSupported() const = 0;
     virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
 
@@ -224,7 +226,8 @@
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
     void setConnected(bool connected) override; // For use by Device only
-    bool hasCapability(hal::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex);
+    bool hasCapability(aidl::android::hardware::graphics::composer3::DisplayCapability)
+            const override EXCLUDES(mDisplayCapabilitiesMutex);
     bool isVsyncPeriodSwitchSupported() const override;
     void onLayerDestroyed(hal::HWLayerId layerId) override;
 
@@ -253,8 +256,9 @@
 
     mutable std::mutex mDisplayCapabilitiesMutex;
     std::once_flag mDisplayCapabilityQueryFlag;
-    std::optional<std::unordered_set<hal::DisplayCapability>> mDisplayCapabilities
-            GUARDED_BY(mDisplayCapabilitiesMutex);
+    std::optional<
+            std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>>
+            mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex);
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3b4fff0..546e677 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -182,8 +182,9 @@
     return mCapabilities.count(capability) > 0;
 }
 
-bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
-                                      hal::DisplayCapability capability) const {
+bool HWComposer::hasDisplayCapability(
+        HalDisplayId displayId,
+        aidl::android::hardware::graphics::composer3::DisplayCapability capability) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability);
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b7f1064..69adfcd 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -44,6 +44,7 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 namespace android {
 
@@ -110,7 +111,9 @@
                                               DisplayIdentificationData* outData) const = 0;
 
     virtual bool hasCapability(hal::Capability) const = 0;
-    virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
+    virtual bool hasDisplayCapability(
+            HalDisplayId,
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
 
     virtual size_t getMaxVirtualDisplayCount() const = 0;
     virtual size_t getMaxVirtualDisplayDimension() const = 0;
@@ -267,7 +270,9 @@
                                       DisplayIdentificationData* outData) const override;
 
     bool hasCapability(hal::Capability) const override;
-    bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
+    bool hasDisplayCapability(
+            HalDisplayId,
+            aidl::android::hardware::graphics::composer3::DisplayCapability) const override;
 
     size_t getMaxVirtualDisplayCount() const override;
     size_t getMaxVirtualDisplayDimension() const override;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 7236868..e33dc0f 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -21,6 +21,7 @@
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
 #define ERROR_HAS_CHANGES 5
 
@@ -55,7 +56,6 @@
 using ContentType = IComposerClient::ContentType;
 using Capability = IComposer::Capability;
 using ClientTargetProperty = IComposerClient::ClientTargetProperty;
-using DisplayCapability = IComposerClient::DisplayCapability;
 using DisplayRequest = IComposerClient::DisplayRequest;
 using DisplayType = IComposerClient::DisplayType;
 using HWConfigId = V2_1::Config;
@@ -118,6 +118,29 @@
     }
 }
 
+inline std::string to_string(
+        aidl::android::hardware::graphics::composer3::DisplayCapability displayCapability) {
+    switch (displayCapability) {
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::INVALID:
+            return "Invalid";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::
+                SKIP_CLIENT_COLOR_TRANSFORM:
+            return "SkipColorTransform";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::DOZE:
+            return "Doze";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::BRIGHTNESS:
+            return "Brightness";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::PROTECTED_CONTENTS:
+            return "ProtectedContents";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::AUTO_LOW_LATENCY_MODE:
+            return "AutoLowLatencyMode";
+        case aidl::android::hardware::graphics::composer3::DisplayCapability::SUSPEND:
+            return "Suspend";
+        default:
+            return "Unknown";
+    }
+}
+
 inline std::string to_string(hardware::graphics::composer::hal::V2_4::Error error) {
     // 5 is reserved for historical reason, during validation 5 means has changes.
     if (ERROR_HAS_CHANGES == static_cast<int32_t>(error)) {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 96f4496..7946002 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -35,6 +35,8 @@
 #include <algorithm>
 #include <cinttypes>
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
 namespace android {
 
 using hardware::hidl_handle;
@@ -1020,6 +1022,15 @@
 
 // Composer HAL 2.4
 
+namespace {
+template <typename T>
+void copyCapabilities(const T& tmpCaps, std::vector<DisplayCapability>* outCapabilities) {
+    outCapabilities->resize(tmpCaps.size());
+    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
+}
+} // anonymous namespace
+
 Error HidlComposer::getDisplayCapabilities(Display display,
                                            std::vector<DisplayCapability>* outCapabilities) {
     if (!mClient_2_3) {
@@ -1034,7 +1045,7 @@
                                                     if (error != V2_4::Error::NONE) {
                                                         return;
                                                     }
-                                                    *outCapabilities = tmpCaps;
+                                                    copyCapabilities(tmpCaps, outCapabilities);
                                                 });
     } else {
         mClient_2_3
@@ -1044,9 +1055,7 @@
                         return;
                     }
 
-                    outCapabilities->resize(tmpCaps.size());
-                    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
-                                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
+                    copyCapabilities(tmpCaps, outCapabilities);
                 });
     }
 
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 8190c17..3b62fe0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -70,7 +70,6 @@
 using V2_4::IComposerClient;
 using V2_4::VsyncPeriodChangeTimeline;
 using V2_4::VsyncPeriodNanos;
-using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
@@ -293,8 +292,10 @@
     Error setDisplayBrightness(Display display, float brightness) override;
 
     // Composer HAL 2.4
-    Error getDisplayCapabilities(Display display,
-                                 std::vector<DisplayCapability>* outCapabilities) override;
+    Error getDisplayCapabilities(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
+                    outCapabilities) override;
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5b78314..89f3bd6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -140,6 +140,8 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+
 #define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
 
 #define ON_MAIN_THREAD(expr)                                       \
@@ -160,6 +162,8 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
+
 namespace android {
 
 using namespace std::string_literals;
@@ -269,6 +273,7 @@
 enum Permission {
     ACCESS_SURFACE_FLINGER = 0x1,
     ROTATE_SURFACE_FLINGER = 0x2,
+    INTERNAL_SYSTEM_WINDOW = 0x4,
 };
 
 struct IdleTimerConfig {
@@ -316,6 +321,7 @@
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
 const String16 sDump("android.permission.DUMP");
 const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
 
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
@@ -358,6 +364,14 @@
             PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
 }
 
+bool callingThreadHasInternalSystemWindowAccess() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+        PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
+}
+
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mPid(getpid()),
@@ -1086,7 +1100,7 @@
 
     info->autoLowLatencyModeSupported =
             getHwComposer().hasDisplayCapability(*displayId,
-                                                 hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+                                                 DisplayCapability::AUTO_LOW_LATENCY_MODE);
     std::vector<hal::ContentType> types;
     getHwComposer().getSupportedContentTypes(*displayId, &types);
     info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
@@ -1651,8 +1665,7 @@
     if (!displayId) {
         return NAME_NOT_FOUND;
     }
-    *outSupport =
-            getHwComposer().hasDisplayCapability(*displayId, hal::DisplayCapability::BRIGHTNESS);
+    *outSupport = getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::BRIGHTNESS);
     return NO_ERROR;
 }
 
@@ -3812,6 +3825,10 @@
         permissions |= Permission::ROTATE_SURFACE_FLINGER;
     }
 
+    if (callingThreadHasInternalSystemWindowAccess()) {
+        permissions |= Permission::INTERNAL_SYSTEM_WINDOW;
+    }
+
     if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
         (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
         ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
@@ -4149,8 +4166,15 @@
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eFlagsChanged) {
-        if (layer->setFlags(s.flags, s.mask))
-            flags |= eTraversalNeeded;
+        auto changedFlags = s.flags;
+        if (changedFlags & layer_state_t::eLayerIsDisplayDecoration) {
+            if ((permissions & Permission::INTERNAL_SYSTEM_WINDOW) == 0) {
+                changedFlags &= ~layer_state_t::eLayerIsDisplayDecoration;
+                ALOGE("Attempt to use eLayerIsDisplayDecoration without permission "
+                      "INTERNAL_SYSTEM_WINDOW!");
+            }
+        }
+        if (layer->setFlags(changedFlags, s.mask)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 89a517f..f1e9b31 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -324,7 +324,9 @@
     template <typename Case>
     static void setupPreconditionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                .WillOnce(DoAll(SetArgPointee<1>(
+                                        std::vector<aidl::android::hardware::graphics::composer3::
+                                                            DisplayCapability>({})),
                                 Return(Error::NONE)));
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 0a3437a..69ac26e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -66,9 +66,9 @@
 using testing::Return;
 using testing::SetArgPointee;
 
+using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using hal::ColorMode;
 using hal::Connection;
-using hal::DisplayCapability;
 using hal::DisplayType;
 using hal::Error;
 using hal::Hdr;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index a1aacc0..8c51313 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -121,7 +121,10 @@
     MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
                  Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
     MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
-    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+    MOCK_METHOD2(
+            getDisplayCapabilities,
+            Error(Display,
+                  std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*));
     MOCK_METHOD2(getDisplayConnectionType,
                  V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
     MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index b43bc0e..821fc8f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -30,7 +30,9 @@
     MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
     MOCK_METHOD(bool, isConnected, (), (const, override));
     MOCK_METHOD(void, setConnected, (bool), (override));
-    MOCK_METHOD(bool, hasCapability, (hal::DisplayCapability), (const, override));
+    MOCK_METHOD(bool, hasCapability,
+                (aidl::android::hardware::graphics::composer3::DisplayCapability),
+                (const, override));
     MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
     MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));