Merge "Fix build on ToT Clang" into main
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index eae844c..3758b65 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -197,7 +197,9 @@
                     && currentValue < sBinderProxyCountHighWatermark
                     && ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] {
                 sTrackingMap[trackedUid] |= WARNING_REACHED_MASK;
-                if (sWarningCallback) sWarningCallback(trackedUid);
+                if (sWarningCallback) {
+                    *postTask = [=]() { sWarningCallback(trackedUid); };
+                }
             } else if (currentValue >= sBinderProxyCountHighWatermark) {
                 ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
                       getuid(), trackedUid, trackedValue);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 95a5da2..ab44957 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -94,6 +94,9 @@
     },
     {
       "name": "libbinder_rpc_unstable_bindgen_test"
+    },
+    {
+      "name": "binderCacheUnitTest"
     }
   ],
   "presubmit-large": [
@@ -133,9 +136,6 @@
     {
       "name": "binder_sdk_test",
       "host": true
-    },
-    {
-      "name": "binderCacheUnitTest"
     }
   ],
   "imports": [
diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
index 75f8753..9bac386 100644
--- a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
+++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
@@ -16,6 +16,8 @@
 
 package android.content.pm;
 
+import android.content.pm.StagedApexInfo;
+
 /**
  * This event is designed for notification to native code listener about
  * any changes to set of apex packages staged for installation on next boot.
@@ -23,5 +25,5 @@
  * @hide
  */
 parcelable ApexStagedEvent {
-  @utf8InCpp String[] stagedApexModuleNames;
+  StagedApexInfo[] stagedApexInfos;
 }
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 3ddfefa..0f0be2f 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -135,13 +135,7 @@
     void unregisterStagedApexObserver(in IStagedApexObserver observer);
 
     /**
-     * Get APEX module names of all APEX that are staged ready for installation
+     * Get information of staged APEXes.
      */
-    @utf8InCpp String[] getStagedApexModuleNames();
-
-    /**
-     * Get information of APEX which is staged ready for installation.
-     * Returns null if no such APEX is found.
-     */
-    @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
+    StagedApexInfo[] getStagedApexInfos();
 }
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index 949835b..8f7ad30 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -22,6 +22,7 @@
  *
  * @hide
  */
+@JavaDerive(equals=true)
 parcelable StagedApexInfo {
   @utf8InCpp String moduleName;
   @utf8InCpp String diskImagePath;
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index c671eed..0b4f196 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,14 @@
         return callParcel("writeParcelableVector",
                           [&]() { return parcel->writeParcelableVector(v); });
     }
+
+    status_t read(const Parcel& parcel, std::vector<bool>* v) const {
+        return callParcel("readBoolVector", [&]() { return parcel.readBoolVector(v); });
+    }
+    status_t write(Parcel* parcel, const std::vector<bool>& v) const {
+        return callParcel("writeBoolVector", [&]() { return parcel->writeBoolVector(v); });
+    }
+
     status_t read(const Parcel& parcel, float* f) const {
         return callParcel("readFloat", [&]() { return parcel.readFloat(f); });
     }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 8b0dda3..28a3f65 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -731,6 +731,9 @@
         "liblog",
         "libutils",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 0aa678d..849dc7c 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -40,6 +40,8 @@
 #include <sys/eventfd.h>
 #include <sys/prctl.h>
 
+#include <gmock/gmock.h>
+
 using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
 using android::binder::unique_fd;
 
@@ -222,6 +224,7 @@
         SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
         ReturnsNoMemory,
         LogicalNot,
+        LogicalNotVector,
         ModifyEnum,
         IncrementFlattenable,
         IncrementLightFlattenable,
@@ -249,6 +252,7 @@
 
     // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
     virtual status_t logicalNot(bool a, bool* notA) const = 0;
+    virtual status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const = 0;
     virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
     virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
     virtual status_t increment(const TestLightFlattenable& a,
@@ -288,7 +292,14 @@
     }
     status_t logicalNot(bool a, bool* notA) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
-        return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+        using Signature = status_t (ISafeInterfaceTest::*)(bool, bool*) const;
+        return callRemote<Signature>(Tag::LogicalNot, a, notA);
+    }
+    status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>&,
+                                                           std::vector<bool>*) const;
+        return callRemote<Signature>(Tag::LogicalNotVector, a, notA);
     }
     status_t modifyEnum(TestEnum a, TestEnum* b) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
@@ -406,6 +417,14 @@
         *notA = !a;
         return NO_ERROR;
     }
+    status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+        ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+        notA->clear();
+        for (bool value : a) {
+            notA->push_back(!value);
+        }
+        return NO_ERROR;
+    }
     status_t modifyEnum(TestEnum a, TestEnum* b) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
         *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
@@ -513,7 +532,13 @@
                 return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
             }
             case ISafeInterfaceTest::Tag::LogicalNot: {
-                return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+                using Signature = status_t (ISafeInterfaceTest::*)(bool a, bool* notA) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
+            }
+            case ISafeInterfaceTest::Tag::LogicalNotVector: {
+                using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>& a,
+                                                                   std::vector<bool>* notA) const;
+                return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
             }
             case ISafeInterfaceTest::Tag::ModifyEnum: {
                 return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
@@ -639,6 +664,15 @@
     ASSERT_EQ(!b, notB);
 }
 
+TEST_F(SafeInterfaceTest, TestLogicalNotVector) {
+    const std::vector<bool> a = {true, false, true};
+    std::vector<bool> notA;
+    status_t result = mSafeInterfaceTest->logicalNot(a, &notA);
+    ASSERT_EQ(NO_ERROR, result);
+    std::vector<bool> expected = {false, true, false};
+    ASSERT_THAT(notA, testing::ContainerEq(expected));
+}
+
 TEST_F(SafeInterfaceTest, TestModifyEnum) {
     const TestEnum a = TestEnum::INITIAL;
     TestEnum b = TestEnum::INVALID;
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 931c311..f19b908 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -19,10 +19,9 @@
 mod handle;
 mod surface;
 
-pub use handle::NativeHandle;
-pub use surface::Surface;
-
 pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use handle::NativeHandle;
+pub use surface::{buffer::Buffer, Surface};
 
 use binder::{
     binder_impl::{BorrowedParcel, UnstructuredParcelable},
diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs
index 9eddfcd..ed52471 100644
--- a/libs/nativewindow/rust/src/surface.rs
+++ b/libs/nativewindow/rust/src/surface.rs
@@ -14,6 +14,8 @@
 
 //! Rust wrapper for `ANativeWindow` and related types.
 
+pub(crate) mod buffer;
+
 use binder::{
     binder_impl::{BorrowedParcel, UnstructuredParcelable},
     impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
@@ -21,17 +23,18 @@
     StatusCode,
 };
 use bitflags::bitflags;
+use buffer::Buffer;
 use nativewindow_bindgen::{
     ADataSpace, AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire,
     ANativeWindow_getBuffersDataSpace, ANativeWindow_getBuffersDefaultDataSpace,
-    ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth,
+    ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_lock,
     ANativeWindow_readFromParcel, ANativeWindow_release, ANativeWindow_setBuffersDataSpace,
     ANativeWindow_setBuffersGeometry, ANativeWindow_setBuffersTransform,
-    ANativeWindow_writeToParcel,
+    ANativeWindow_unlockAndPost, ANativeWindow_writeToParcel, ARect,
 };
 use std::error::Error;
 use std::fmt::{self, Debug, Display, Formatter};
-use std::ptr::{null_mut, NonNull};
+use std::ptr::{self, null_mut, NonNull};
 
 /// Wrapper around an opaque C `ANativeWindow`.
 #[derive(PartialEq, Eq)]
@@ -153,6 +156,43 @@
             Ok(ADataSpace(data_space))
         }
     }
+
+    /// Locks the window's next drawing surface for writing, and returns it.
+    pub fn lock(&mut self, bounds: Option<&mut ARect>) -> Result<Buffer, ErrorCode> {
+        let mut buffer = buffer::EMPTY;
+        // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+        // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+        // and we have not yet released it. The other pointers must be valid because the come from
+        // references, and aren't retained after the function returns.
+        let status = unsafe {
+            ANativeWindow_lock(
+                self.0.as_ptr(),
+                &mut buffer,
+                bounds.map(ptr::from_mut).unwrap_or(null_mut()),
+            )
+        };
+        if status != 0 {
+            return Err(ErrorCode(status));
+        }
+
+        Ok(Buffer::new(buffer, self))
+    }
+
+    /// Unlocks the window's drawing surface which was previously locked, posting the new buffer to
+    /// the display.
+    ///
+    /// This shouldn't be called directly but via the [`Buffer`], hence is not public here.
+    fn unlock_and_post(&mut self) -> Result<(), ErrorCode> {
+        // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+        // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+        // and we have not yet released it.
+        let status = unsafe { ANativeWindow_unlockAndPost(self.0.as_ptr()) };
+        if status == 0 {
+            Ok(())
+        } else {
+            Err(ErrorCode(status))
+        }
+    }
 }
 
 impl Drop for Surface {
diff --git a/libs/nativewindow/rust/src/surface/buffer.rs b/libs/nativewindow/rust/src/surface/buffer.rs
new file mode 100644
index 0000000..a2d74d4
--- /dev/null
+++ b/libs/nativewindow/rust/src/surface/buffer.rs
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 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.
+
+use super::{ErrorCode, Surface};
+use nativewindow_bindgen::{AHardwareBuffer_Format, ANativeWindow_Buffer};
+use std::ptr::null_mut;
+
+/// An empty `ANativeWindow_Buffer`.
+pub const EMPTY: ANativeWindow_Buffer = ANativeWindow_Buffer {
+    width: 0,
+    height: 0,
+    stride: 0,
+    format: 0,
+    bits: null_mut(),
+    reserved: [0; 6],
+};
+
+/// Rust wrapper for `ANativeWindow_Buffer`, representing a locked buffer from a [`Surface`].
+pub struct Buffer<'a> {
+    /// The wrapped `ANativeWindow_Buffer`.
+    pub buffer: ANativeWindow_Buffer,
+    surface: &'a mut Surface,
+}
+
+impl<'a> Buffer<'a> {
+    pub(crate) fn new(buffer: ANativeWindow_Buffer, surface: &'a mut Surface) -> Self {
+        Self { buffer, surface }
+    }
+
+    /// Unlocks the window's drawing surface which was previously locked to create this buffer,
+    /// posting the buffer to the display.
+    pub fn unlock_and_post(self) -> Result<(), ErrorCode> {
+        self.surface.unlock_and_post()
+    }
+
+    /// The number of pixels that are shown horizontally.
+    pub fn width(&self) -> i32 {
+        self.buffer.width
+    }
+
+    /// The number of pixels that are shown vertically.
+    pub fn height(&self) -> i32 {
+        self.buffer.height
+    }
+
+    /// The number of pixels that a line in the buffer takes in memory.
+    ///
+    /// This may be greater than the width.
+    pub fn stride(&self) -> i32 {
+        self.buffer.stride
+    }
+
+    /// The pixel format of the buffer.
+    pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> {
+        self.buffer.format.try_into().map_err(|_| ErrorCode(self.buffer.format))
+    }
+}
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index 4a0889f..568d348 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -32,7 +32,8 @@
         CANCEL_POINTER_EVENTS = 1,
         CANCEL_NON_POINTER_EVENTS = 2,
         CANCEL_FALLBACK_EVENTS = 3,
-        ftl_last = CANCEL_FALLBACK_EVENTS,
+        CANCEL_HOVER_EVENTS = 4,
+        ftl_last = CANCEL_HOVER_EVENTS
     };
 
     // The criterion to use to determine which events should be canceled.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 2161e09..7eb7e36 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -746,7 +746,8 @@
             }
             touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
         }
-        touchedWindow.addHoveringPointer(entry.deviceId, pointer);
+        const auto [x, y] = resolveTouchedPosition(entry);
+        touchedWindow.addHoveringPointer(entry.deviceId, pointer, x, y);
         if (canReceiveForegroundTouches(*newWindow->getInfo())) {
             touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
         }
@@ -873,6 +874,8 @@
             return {false, true};
         case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
             return {false, true};
+        case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+            return {true, false};
     }
 }
 
@@ -2511,7 +2514,8 @@
 
             if (isHoverAction) {
                 // The "windowHandle" is the target of this hovering pointer.
-                tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer);
+                tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer, x,
+                                                          y);
             }
 
             // Set target flags.
@@ -5437,6 +5441,31 @@
         }
     }
 
+    // Check if the hovering should stop because the window is no longer eligible to receive it
+    // (for example, if the touchable region changed)
+    if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+        TouchState& state = it->second;
+        for (TouchedWindow& touchedWindow : state.windows) {
+            std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
+                    [this, displayId, &touchedWindow](const PointerProperties& properties, float x,
+                                                      float y) REQUIRES(mLock) {
+                        const bool isStylus = properties.toolType == ToolType::STYLUS;
+                        const ui::Transform displayTransform = getTransformLocked(displayId);
+                        const bool stillAcceptsTouch =
+                                windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
+                                                     displayId, x, y, isStylus, displayTransform);
+                        return !stillAcceptsTouch;
+                    });
+
+            for (DeviceId deviceId : erasedDevices) {
+                CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
+                                           "WindowInfo changed", traceContext.getTracker());
+                options.deviceId = deviceId;
+                synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
+            }
+        }
+    }
+
     // Release information for windows that are no longer present.
     // This ensures that unused input channels are released promptly.
     // Otherwise, they might stick around until the window handle is destroyed
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index dfbe02f..e283fc3 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -638,6 +638,8 @@
             return memento.source & AINPUT_SOURCE_CLASS_POINTER;
         case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
             return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+        case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+            return memento.hovering;
         default:
             return false;
     }
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 0c9ad3c..2bf63be 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -112,17 +112,18 @@
 }
 
 void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
-                                            DeviceId deviceId, const PointerProperties& pointer) {
+                                            DeviceId deviceId, const PointerProperties& pointer,
+                                            float x, float y) {
     for (TouchedWindow& touchedWindow : windows) {
         if (touchedWindow.windowHandle == windowHandle) {
-            touchedWindow.addHoveringPointer(deviceId, pointer);
+            touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
             return;
         }
     }
 
     TouchedWindow touchedWindow;
     touchedWindow.windowHandle = windowHandle;
-    touchedWindow.addHoveringPointer(deviceId, pointer);
+    touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
     windows.push_back(touchedWindow);
 }
 
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9d4bb3d..3fbe584 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -49,7 +49,8 @@
             DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
             std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
     void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                    DeviceId deviceId, const PointerProperties& pointer);
+                                    DeviceId deviceId, const PointerProperties& pointer, float x,
+                                    float y);
     void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
     void clearHoveringPointers(DeviceId deviceId);
 
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 1f86f66..fa5be1a 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -36,6 +36,13 @@
                         }) != pointers.end();
 }
 
+bool hasPointerId(const std::vector<TouchedWindow::HoveringPointer>& pointers, int32_t pointerId) {
+    return std::find_if(pointers.begin(), pointers.end(),
+                        [&pointerId](const TouchedWindow::HoveringPointer& pointer) {
+                            return pointer.properties.id == pointerId;
+                        }) != pointers.end();
+}
+
 } // namespace
 
 bool TouchedWindow::hasHoveringPointers() const {
@@ -78,16 +85,18 @@
     return hasPointerId(state.hoveringPointers, pointerId);
 }
 
-void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) {
-    std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& properties,
+                                       float x, float y) {
+    std::vector<HoveringPointer>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
     const size_t initialSize = hoveringPointers.size();
-    std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) {
-        return properties.id == pointer.id;
+    std::erase_if(hoveringPointers, [&properties](const HoveringPointer& pointer) {
+        return pointer.properties.id == properties.id;
     });
     if (hoveringPointers.size() != initialSize) {
-        LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this;
+        LOG(ERROR) << __func__ << ": " << properties << ", device " << deviceId << " was in "
+                   << *this;
     }
-    hoveringPointers.push_back(pointer);
+    hoveringPointers.push_back({properties, x, y});
 }
 
 Result<void> TouchedWindow::addTouchingPointers(DeviceId deviceId,
@@ -173,8 +182,8 @@
                 return true;
             }
         }
-        for (const PointerProperties& properties : state.hoveringPointers) {
-            if (properties.toolType == ToolType::STYLUS) {
+        for (const HoveringPointer& pointer : state.hoveringPointers) {
+            if (pointer.properties.toolType == ToolType::STYLUS) {
                 return true;
             }
         }
@@ -270,8 +279,8 @@
     }
     DeviceState& state = stateIt->second;
 
-    std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) {
-        return properties.id == pointerId;
+    std::erase_if(state.hoveringPointers, [&pointerId](const HoveringPointer& pointer) {
+        return pointer.properties.id == pointerId;
     });
 
     if (!state.hasPointers()) {
@@ -279,6 +288,22 @@
     }
 }
 
+std::vector<DeviceId> TouchedWindow::eraseHoveringPointersIf(
+        std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition) {
+    std::vector<DeviceId> erasedDevices;
+    for (auto& [deviceId, state] : mDeviceStates) {
+        std::erase_if(state.hoveringPointers, [&](const HoveringPointer& pointer) {
+            if (condition(pointer.properties, pointer.x, pointer.y)) {
+                erasedDevices.push_back(deviceId);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    return erasedDevices;
+}
+
 void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) {
     const auto stateIt = mDeviceStates.find(deviceId);
     if (stateIt == mDeviceStates.end()) {
@@ -312,6 +337,11 @@
     return out;
 }
 
+std::ostream& operator<<(std::ostream& out, const TouchedWindow::HoveringPointer& pointer) {
+    out << pointer.properties << " at (" << pointer.x << ", " << pointer.y << ")";
+    return out;
+}
+
 std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) {
     out << window.dump();
     return out;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4f0ad16..c38681e 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -38,7 +38,7 @@
     bool hasHoveringPointers() const;
     bool hasHoveringPointers(DeviceId deviceId) const;
     bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
-    void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer);
+    void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer, float x, float y);
     void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
 
     // Touching
@@ -69,6 +69,15 @@
     void clearHoveringPointers(DeviceId deviceId);
     std::string dump() const;
 
+    struct HoveringPointer {
+        PointerProperties properties;
+        float x;
+        float y;
+    };
+
+    std::vector<DeviceId> eraseHoveringPointersIf(
+            std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition);
+
 private:
     struct DeviceState {
         std::vector<PointerProperties> touchingPointers;
@@ -78,7 +87,7 @@
         // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
         // scenario.
         std::optional<nsecs_t> downTimeInTarget;
-        std::vector<PointerProperties> hoveringPointers;
+        std::vector<HoveringPointer> hoveringPointers;
 
         bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); };
     };
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 73ab0da..48930ef 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1923,6 +1923,99 @@
     window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL));
 }
 
+/**
+ * Two windows: a trusted overlay and a regular window underneath. Both windows are visible.
+ * Mouse is hovered, and the hover event should only go to the overlay.
+ * However, next, the touchable region of the trusted overlay shrinks. The mouse position hasn't
+ * changed, but the cursor would now end up hovering above the regular window underneatch.
+ * If the mouse is now clicked, this would generate an ACTION_DOWN event, which would go to the
+ * regular window. However, the trusted overlay is also watching for outside touch.
+ * The trusted overlay should get two events:
+ * 1) The ACTION_OUTSIDE event, since the click is now not inside its touchable region
+ * 2) The HOVER_EXIT event, since the mouse pointer is no longer hovering inside this window
+ *
+ * This test reproduces a crash where there is an overlap between dispatch modes for the trusted
+ * overlay touch target, since the event is causing both an ACTION_OUTSIDE, and as a HOVER_EXIT.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlay) {
+    std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+                                                              ui::LogicalDisplayId::DEFAULT);
+    overlay->setTrustedOverlay(true);
+    overlay->setWatchOutsideTouch(true);
+    overlay->setFrame(Rect(0, 0, 200, 200));
+
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+                                                             ui::LogicalDisplayId::DEFAULT);
+    window->setFrame(Rect(0, 0, 200, 200));
+
+    mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+    // Hover the mouse into the overlay
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+                                      .build());
+    overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+    // the regular window as the touch target
+    overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+    mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+    // Now we can click with the mouse. The click should go into the regular window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+                                      .build());
+    overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+    overlay->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+    window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
+ * Similar to above, but also has a spy on top that also catches the HOVER
+ * events. Also, instead of ACTION_DOWN, we are continuing to send the hovering
+ * stream to ensure that the spy receives hover events correctly.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlayWithSpy) {
+    std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> spyWindow =
+            sp<FakeWindowHandle>::make(app, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT);
+    spyWindow->setFrame(Rect(0, 0, 200, 200));
+    spyWindow->setTrustedOverlay(true);
+    spyWindow->setSpy(true);
+    sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+                                                              ui::LogicalDisplayId::DEFAULT);
+    overlay->setTrustedOverlay(true);
+    overlay->setWatchOutsideTouch(true);
+    overlay->setFrame(Rect(0, 0, 200, 200));
+
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+                                                             ui::LogicalDisplayId::DEFAULT);
+    window->setFrame(Rect(0, 0, 200, 200));
+
+    mDispatcher->onWindowInfosChanged(
+            {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+    // Hover the mouse into the overlay
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+                                      .build());
+    spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+    overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+    // the regular window as the touch target
+    overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+    mDispatcher->onWindowInfosChanged(
+            {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+    // Now we can click with the mouse. The click should go into the regular window
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+                                      .build());
+    spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+    overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
 using InputDispatcherMultiDeviceTest = InputDispatcherTest;
 
 /**