Merge "SF: call onFrameBegin before scheduling the next frame" into main
diff --git a/include/input/Input.h b/include/input/Input.h
index 19f4ab3..00757a7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,6 +26,7 @@
 #ifdef __linux__
 #include <android/os/IInputConstants.h>
 #endif
+#include <android/os/PointerIconType.h>
 #include <math.h>
 #include <stdint.h>
 #include <ui/Transform.h>
@@ -1213,43 +1214,41 @@
  *
  * Due to backwards compatibility and public api constraints, this is a duplicate (but type safe)
  * definition of PointerIcon.java.
- *
- * TODO(b/235023317) move this definition to an aidl and statically assign to the below java public
- * api values.
- *
- * WARNING: Keep these definitions in sync with
- * frameworks/base/core/java/android/view/PointerIcon.java
  */
 enum class PointerIconStyle : int32_t {
-    TYPE_CUSTOM = -1,
-    TYPE_NULL = 0,
-    TYPE_NOT_SPECIFIED = 1,
-    TYPE_ARROW = 1000,
-    TYPE_CONTEXT_MENU = 1001,
-    TYPE_HAND = 1002,
-    TYPE_HELP = 1003,
-    TYPE_WAIT = 1004,
-    TYPE_CELL = 1006,
-    TYPE_CROSSHAIR = 1007,
-    TYPE_TEXT = 1008,
-    TYPE_VERTICAL_TEXT = 1009,
-    TYPE_ALIAS = 1010,
-    TYPE_COPY = 1011,
-    TYPE_NO_DROP = 1012,
-    TYPE_ALL_SCROLL = 1013,
-    TYPE_HORIZONTAL_DOUBLE_ARROW = 1014,
-    TYPE_VERTICAL_DOUBLE_ARROW = 1015,
-    TYPE_TOP_RIGHT_DOUBLE_ARROW = 1016,
-    TYPE_TOP_LEFT_DOUBLE_ARROW = 1017,
-    TYPE_ZOOM_IN = 1018,
-    TYPE_ZOOM_OUT = 1019,
-    TYPE_GRAB = 1020,
-    TYPE_GRABBING = 1021,
-    TYPE_HANDWRITING = 1022,
+    TYPE_CUSTOM = static_cast<int32_t>(::android::os::PointerIconType::CUSTOM),
+    TYPE_NULL = static_cast<int32_t>(::android::os::PointerIconType::TYPE_NULL),
+    TYPE_NOT_SPECIFIED = static_cast<int32_t>(::android::os::PointerIconType::NOT_SPECIFIED),
+    TYPE_ARROW = static_cast<int32_t>(::android::os::PointerIconType::ARROW),
+    TYPE_CONTEXT_MENU = static_cast<int32_t>(::android::os::PointerIconType::CONTEXT_MENU),
+    TYPE_HAND = static_cast<int32_t>(::android::os::PointerIconType::HAND),
+    TYPE_HELP = static_cast<int32_t>(::android::os::PointerIconType::HELP),
+    TYPE_WAIT = static_cast<int32_t>(::android::os::PointerIconType::WAIT),
+    TYPE_CELL = static_cast<int32_t>(::android::os::PointerIconType::CELL),
+    TYPE_CROSSHAIR = static_cast<int32_t>(::android::os::PointerIconType::CROSSHAIR),
+    TYPE_TEXT = static_cast<int32_t>(::android::os::PointerIconType::TEXT),
+    TYPE_VERTICAL_TEXT = static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_TEXT),
+    TYPE_ALIAS = static_cast<int32_t>(::android::os::PointerIconType::ALIAS),
+    TYPE_COPY = static_cast<int32_t>(::android::os::PointerIconType::COPY),
+    TYPE_NO_DROP = static_cast<int32_t>(::android::os::PointerIconType::NO_DROP),
+    TYPE_ALL_SCROLL = static_cast<int32_t>(::android::os::PointerIconType::ALL_SCROLL),
+    TYPE_HORIZONTAL_DOUBLE_ARROW =
+            static_cast<int32_t>(::android::os::PointerIconType::HORIZONTAL_DOUBLE_ARROW),
+    TYPE_VERTICAL_DOUBLE_ARROW =
+            static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_DOUBLE_ARROW),
+    TYPE_TOP_RIGHT_DOUBLE_ARROW =
+            static_cast<int32_t>(::android::os::PointerIconType::TOP_RIGHT_DOUBLE_ARROW),
+    TYPE_TOP_LEFT_DOUBLE_ARROW =
+            static_cast<int32_t>(::android::os::PointerIconType::TOP_LEFT_DOUBLE_ARROW),
+    TYPE_ZOOM_IN = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_IN),
+    TYPE_ZOOM_OUT = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_OUT),
+    TYPE_GRAB = static_cast<int32_t>(::android::os::PointerIconType::GRAB),
+    TYPE_GRABBING = static_cast<int32_t>(::android::os::PointerIconType::GRABBING),
+    TYPE_HANDWRITING = static_cast<int32_t>(::android::os::PointerIconType::HANDWRITING),
 
-    TYPE_SPOT_HOVER = 2000,
-    TYPE_SPOT_TOUCH = 2001,
-    TYPE_SPOT_ANCHOR = 2002,
+    TYPE_SPOT_HOVER = static_cast<int32_t>(::android::os::PointerIconType::SPOT_HOVER),
+    TYPE_SPOT_TOUCH = static_cast<int32_t>(::android::os::PointerIconType::SPOT_TOUCH),
+    TYPE_SPOT_ANCHOR = static_cast<int32_t>(::android::os::PointerIconType::SPOT_ANCHOR),
 };
 
 } // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 3278c23..fed590c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
         "android/os/InputConfig.aidl",
+        "android/os/PointerIconType.aidl",
     ],
 }
 
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index e836a4c..1df88dd 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -201,7 +201,7 @@
             // device starts to speed up, but avoids producing noisy predictions as it slows down.
             break;
         }
-        if (input_flags::enable_jerk_prediction_pruning()) {
+        if (input_flags::enable_prediction_pruning_via_jerk_thresholding()) {
             // TODO(b/266747654): Stop predictions if confidence is < some threshold
             // Arbitrarily high pruning index, will correct once jerk thresholding is implemented.
             const size_t upperBoundPredictionIndex = std::numeric_limits<size_t>::max();
diff --git a/libs/input/android/os/PointerIconType.aidl b/libs/input/android/os/PointerIconType.aidl
new file mode 100644
index 0000000..f244c62
--- /dev/null
+++ b/libs/input/android/os/PointerIconType.aidl
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Represents an icon that can be used as a mouse pointer.
+ * Please look at frameworks/base/core/java/android/view/PointerIcon.java for the detailed
+ * explanation of each constant.
+ * @hide
+ */
+@Backing(type="int")
+enum PointerIconType {
+    CUSTOM                  = -1,
+    TYPE_NULL               = 0,
+    NOT_SPECIFIED           = 1,
+    ARROW                   = 1000,
+    CONTEXT_MENU            = 1001,
+    HAND                    = 1002,
+    HELP                    = 1003,
+    WAIT                    = 1004,
+    CELL                    = 1006,
+    CROSSHAIR               = 1007,
+    TEXT                    = 1008,
+    VERTICAL_TEXT           = 1009,
+    ALIAS                   = 1010,
+    COPY                    = 1011,
+    NO_DROP                 = 1012,
+    ALL_SCROLL              = 1013,
+    HORIZONTAL_DOUBLE_ARROW = 1014,
+    VERTICAL_DOUBLE_ARROW   = 1015,
+    TOP_RIGHT_DOUBLE_ARROW  = 1016,
+    TOP_LEFT_DOUBLE_ARROW   = 1017,
+    ZOOM_IN                 = 1018,
+    ZOOM_OUT                = 1019,
+    GRAB                    = 1020,
+    GRABBING                = 1021,
+    HANDWRITING             = 1022,
+
+    SPOT_HOVER              = 2000,
+    SPOT_TOUCH              = 2001,
+    SPOT_ANCHOR             = 2002,
+}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index c1f5e2a..e041c51 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -130,8 +130,10 @@
 }
 
 flag {
-  name: "enable_jerk_prediction_pruning"
+  name: "enable_prediction_pruning_via_jerk_thresholding"
   namespace: "input"
   description: "Enable prediction pruning based on jerk thresholds."
   bug: "266747654"
+  is_fixed_read_only: true
+
 }
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index e76a4c3..aa18713 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -23,6 +23,7 @@
 
 #include <log/log_main.h>
 #include <sync/sync.h>
+#include <utils/Trace.h>
 
 namespace android::renderengine::skia {
 
@@ -33,6 +34,12 @@
     info->unref();
 }
 
+std::unique_ptr<SkiaGpuContext> GaneshVkRenderEngine::createContext(
+        VulkanInterface& vulkanInterface) {
+    return SkiaGpuContext::MakeVulkan_Ganesh(vulkanInterface.getGaneshBackendContext(),
+                                             mSkSLCacheMonitor);
+}
+
 void GaneshVkRenderEngine::waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) {
     if (fenceFd.get() < 0) return;
 
@@ -51,11 +58,18 @@
     context->grDirectContext()->wait(1, &beSemaphore, kDeleteAfterWait);
 }
 
-base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context) {
+base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context,
+                                                     sk_sp<SkSurface> dstSurface) {
     sk_sp<GrDirectContext> grContext = context->grDirectContext();
+    {
+        ATRACE_NAME("flush surface");
+        // TODO: Investigate feasibility of combining this "surface flush" into the "context flush"
+        // below.
+        context->grDirectContext()->flush(dstSurface.get());
+    }
+
     VulkanInterface& vi = getVulkanInterface(isProtected());
     VkSemaphore semaphore = vi.createExportableSemaphore();
-
     GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore);
 
     GrFlushInfo flushInfo;
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
index 90e2487..5940d04 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -27,8 +27,9 @@
 protected:
     GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
 
+    std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
     void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
-    base::unique_fd flushAndSubmit(SkiaGpuContext* context) override;
+    base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
 };
 
 } // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index f10c98d..61369ae 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -337,8 +337,14 @@
     }
 }
 
-base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context) {
-    base::unique_fd drawFence = flush();
+base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context,
+                                                   sk_sp<SkSurface> dstSurface) {
+    sk_sp<GrDirectContext> grContext = context->grDirectContext();
+    {
+        ATRACE_NAME("flush surface");
+        grContext->flush(dstSurface.get());
+    }
+    base::unique_fd drawFence = flushGL();
 
     bool requireSync = drawFence.get() < 0;
     if (requireSync) {
@@ -346,8 +352,7 @@
     } else {
         ATRACE_BEGIN("Submit(sync=false)");
     }
-    bool success =
-            context->grDirectContext()->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
+    bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
     ATRACE_END();
     if (!success) {
         ALOGE("Failed to flush RenderEngine commands");
@@ -394,7 +399,7 @@
     return true;
 }
 
-base::unique_fd SkiaGLRenderEngine::flush() {
+base::unique_fd SkiaGLRenderEngine::flushGL() {
     ATRACE_CALL();
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 2e22478..bd177e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -63,7 +63,7 @@
     bool supportsProtectedContentImpl() const override;
     bool useProtectedContextImpl(GrProtected isProtected) override;
     void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
-    base::unique_fd flushAndSubmit(SkiaGpuContext* context) override;
+    base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
     void appendBackendSpecificInfoToDump(std::string& result) override;
 
 private:
@@ -71,7 +71,7 @@
                        EGLSurface placeholder, EGLContext protectedContext,
                        EGLSurface protectedPlaceholder);
     bool waitGpuFence(base::borrowed_fd fenceFd);
-    base::unique_fd flush();
+    base::unique_fd flushGL();
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
                                        EGLContext shareContext,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index c12c119..2484650 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -90,7 +90,7 @@
 
 // Debugging settings
 static const bool kPrintLayerSettings = false;
-static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
+static const bool kGaneshFlushAfterEveryLayer = kPrintLayerSettings;
 static constexpr bool kEnableLayerBrightening = true;
 
 } // namespace
@@ -665,8 +665,8 @@
     validateOutputBufferUsage(buffer->getBuffer());
 
     auto context = getActiveContext();
-    LOG_ALWAYS_FATAL_IF(context->isAbandoned(), "Context is abandoned/device lost at start of %s",
-                        __func__);
+    LOG_ALWAYS_FATAL_IF(context->isAbandonedOrDeviceLost(),
+                        "Context is abandoned/device lost at start of %s", __func__);
 
     // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
     DeferTextureCleanup dtc(mTextureCleanupMgr);
@@ -1122,21 +1122,21 @@
         } else {
             canvas->drawRect(bounds.rect(), paint);
         }
-        if (kFlushAfterEveryLayer) {
+        if (kGaneshFlushAfterEveryLayer) {
             ATRACE_NAME("flush surface");
+            // No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired
+            // in Graphite, then a graphite::Recording would need to be snapped and tracked for each
+            // layer, which is likely possible but adds non-trivial complexity (in both bookkeeping
+            // and refactoring).
             skgpu::ganesh::Flush(activeSurface);
         }
     }
 
     surfaceAutoSaveRestore.restore();
     mCapture->endCapture();
-    {
-        ATRACE_NAME("flush surface");
-        LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
-        skgpu::ganesh::Flush(activeSurface);
-    }
 
-    auto drawFence = sp<Fence>::make(flushAndSubmit(context));
+    LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+    auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
 
     if (ATRACE_ENABLED()) {
         static gui::FenceMonitor sMonitor("RE Completion");
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index a39adbe..d7b4910 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -89,7 +89,8 @@
     virtual bool supportsProtectedContentImpl() const = 0;
     virtual bool useProtectedContextImpl(GrProtected isProtected) = 0;
     virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) = 0;
-    virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) = 0;
+    virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context,
+                                           sk_sp<SkSurface> dstSurface) = 0;
     virtual void appendBackendSpecificInfoToDump(std::string& result) = 0;
 
     size_t getMaxTextureSize() const override final;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 473fa0a..3715859 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -112,12 +112,9 @@
     sSetupVulkanInterface();
 
     SkiaRenderEngine::Contexts contexts;
-    contexts.first = SkiaGpuContext::MakeVulkan_Ganesh(sVulkanInterface.getGaneshBackendContext(),
-                                                       mSkSLCacheMonitor);
+    contexts.first = createContext(sVulkanInterface);
     if (supportsProtectedContentImpl()) {
-        contexts.second = SkiaGpuContext::MakeVulkan_Ganesh(sProtectedContentVulkanInterface
-                                                                    .getGaneshBackendContext(),
-                                                            mSkSLCacheMonitor);
+        contexts.second = createContext(sProtectedContentVulkanInterface);
     }
 
     return contexts;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 5fd911f..371b812 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -71,9 +71,11 @@
     };
 
 protected:
+    virtual std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) = 0;
     // Redeclare parent functions that Ganesh vs. Graphite subclasses must implement.
     virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override = 0;
-    virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) override = 0;
+    virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context,
+                                           sk_sp<SkSurface> dstSurface) override = 0;
 
     SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
 
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index 87f00e4..e3fec19 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -93,7 +93,7 @@
     return mGrContext->maxTextureSize();
 };
 
-bool GaneshGpuContext::isAbandoned() {
+bool GaneshGpuContext::isAbandonedOrDeviceLost() {
     return mGrContext->abandoned();
 }
 
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h
index ec0162d..d815d15 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.h
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.h
@@ -36,7 +36,7 @@
 
     size_t getMaxRenderTargetSize() const override;
     size_t getMaxTextureSize() const override;
-    bool isAbandoned() override;
+    bool isAbandonedOrDeviceLost() override;
     void setResourceCacheLimit(size_t maxResourceBytes) override;
 
     void finishRenderingAndAbandonContext() override;
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index ddf1642..8222e62 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -68,7 +68,7 @@
      */
     virtual sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) = 0;
 
-    virtual bool isAbandoned() = 0;
+    virtual bool isAbandonedOrDeviceLost() = 0;
     virtual size_t getMaxRenderTargetSize() const = 0;
     virtual size_t getMaxTextureSize() const = 0;
     virtual void setResourceCacheLimit(size_t maxResourceBytes) = 0;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 2df86a0..e331e8d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -7157,4 +7157,11 @@
     return false;
 }
 
+void InputDispatcher::setInputMethodConnectionIsActive(bool isActive) {
+    std::scoped_lock _l(mLock);
+    if (mTracer) {
+        mTracer->setInputMethodConnectionIsActive(isActive);
+    }
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index edca63e..3579a67 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -158,6 +158,8 @@
     bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
                            int32_t pointerId) override;
 
+    void setInputMethodConnectionIsActive(bool isActive) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 7c440e8..6b9262c 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -236,6 +236,11 @@
      */
     virtual bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
                                    int32_t pointerId) = 0;
+
+    /*
+     * Notify the dispatcher that the state of the input method connection changed.
+     */
+    virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 55ed5c6..1d4d11c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -61,10 +61,10 @@
                           e.downTime,  e.flags,     e.repeatCount, type};
 }
 
-void writeEventToBackend(const TracedEvent& event, const TracedEventArgs args,
+void writeEventToBackend(const TracedEvent& event, const TracedEventMetadata metadata,
                          InputTracingBackendInterface& backend) {
-    std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e, args); },
-                       [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e, args); }},
+    std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e, metadata); },
+                       [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e, metadata); }},
                event);
 }
 
@@ -126,22 +126,21 @@
     const InputTargetInfo& targetInfo = getTargetInfo(target);
     if (eventState->isEventProcessingComplete) {
         // Disallow adding new targets after eventProcessingComplete() is called.
-        if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+        if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
             LOG(FATAL) << __func__ << ": Cannot add new target after eventProcessingComplete";
         }
         return;
     }
     if (isDerivedCookie(cookie)) {
         // Disallow adding new targets from a derived cookie.
-        if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+        if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
             LOG(FATAL) << __func__ << ": Cannot add new target from a derived cookie";
         }
         return;
     }
 
-    eventState->targets.emplace(targetInfo.uid);
-    eventState->isSecure |= targetInfo.isSecureWindow;
-    // TODO(b/210460522): Set events as sensitive when the IME connection is active.
+    eventState->metadata.targets.emplace(targetInfo.uid);
+    eventState->metadata.isSecure |= targetInfo.isSecureWindow;
 }
 
 void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
@@ -177,9 +176,7 @@
         // is dispatched, such as in the case of key fallback events. To account for these cases,
         // derived events can be traced after the processing is complete for the original event.
         const auto& event = eventState->events.back();
-        const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
-                                        .targets = eventState->targets};
-        writeEventToBackend(event, std::move(traceArgs), *mBackend);
+        writeEventToBackend(event, eventState->metadata, *mBackend);
     }
     return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
 }
@@ -206,7 +203,7 @@
                 << ": Failed to find a previously traced event that matches the dispatched event";
     }
 
-    if (eventState->targets.count(dispatchEntry.targetUid) == 0) {
+    if (eventState->metadata.targets.count(dispatchEntry.targetUid) == 0) {
         LOG(FATAL) << __func__ << ": Event is being dispatched to UID that it is not targeting";
     }
 
@@ -226,9 +223,7 @@
                                                 /*hmac=*/{},
                                                 resolvedKeyRepeatCount};
     if (eventState->isEventProcessingComplete) {
-        const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
-                                        .targets = eventState->targets};
-        mBackend->traceWindowDispatch(std::move(windowDispatchArgs), std::move(traceArgs));
+        mBackend->traceWindowDispatch(std::move(windowDispatchArgs), eventState->metadata);
     } else {
         eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs));
     }
@@ -246,10 +241,11 @@
 // --- InputTracer::EventState ---
 
 void InputTracer::EventState::onEventProcessingComplete() {
+    metadata.isImeConnectionActive = tracer.mIsImeConnectionActive;
+
     // Write all of the events known so far to the trace.
     for (const auto& event : events) {
-        const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
-        writeEventToBackend(event, traceArgs, *tracer.mBackend);
+        writeEventToBackend(event, metadata, *tracer.mBackend);
     }
     // Write all pending dispatch args to the trace.
     for (const auto& windowDispatchArgs : pendingDispatchArgs) {
@@ -263,8 +259,7 @@
                        << ": Failed to find a previously traced event that matches the dispatched "
                           "event";
         }
-        const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
-        tracer.mBackend->traceWindowDispatch(windowDispatchArgs, std::move(traceArgs));
+        tracer.mBackend->traceWindowDispatch(windowDispatchArgs, metadata);
     }
     pendingDispatchArgs.clear();
 
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 717bc1f..ab175be 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -48,9 +48,13 @@
     std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&,
                                                              const EventTrackerInterface&) override;
     void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) override;
+    void setInputMethodConnectionIsActive(bool isActive) override {
+        mIsImeConnectionActive = isActive;
+    }
 
 private:
     std::unique_ptr<InputTracingBackendInterface> mBackend;
+    bool mIsImeConnectionActive{false};
 
     // The state of a tracked event, shared across all events derived from the original event.
     struct EventState {
@@ -64,10 +68,8 @@
         bool isEventProcessingComplete{false};
         // A queue to hold dispatch args from being traced until event processing is complete.
         std::vector<const WindowDispatchArgs> pendingDispatchArgs;
-        // True if the event is targeting at least one secure window;
-        bool isSecure{false};
-        // The list of all possible UIDs that this event could be targeting.
-        std::set<gui::Uid> targets;
+        // The metadata should not be modified after event processing is complete.
+        TracedEventMetadata metadata{};
     };
 
     // Get the event state associated with a tracking cookie.
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index 609d10c..af6eefb 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -103,6 +103,11 @@
      * provided.
      */
     virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) = 0;
+
+    /**
+     * Notify that the state of the input method connection changed.
+     */
+    virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
 };
 
 } // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 3ff7fab..2b45e3a 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -92,11 +92,13 @@
 using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>;
 
 /** Additional information about an input event being traced. */
-struct TracedEventArgs {
+struct TracedEventMetadata {
     // True if the event is targeting at least one secure window.
     bool isSecure;
     // The list of possible UIDs that this event could be targeting.
     std::set<gui::Uid> targets;
+    // True if the there was an active input method connection while this event was processed.
+    bool isImeConnectionActive;
 };
 
 /** Additional information about an input event being dispatched to a window. */
@@ -121,13 +123,13 @@
     virtual ~InputTracingBackendInterface() = default;
 
     /** Trace a KeyEvent. */
-    virtual void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) = 0;
+    virtual void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) = 0;
 
     /** Trace a MotionEvent. */
-    virtual void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) = 0;
+    virtual void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) = 0;
 
     /** Trace an event being sent to a window. */
-    virtual void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) = 0;
+    virtual void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) = 0;
 };
 
 } // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index b76bec3..9c39743 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -98,10 +98,10 @@
 }
 
 TraceLevel PerfettoBackend::InputEventDataSource::resolveTraceLevel(
-        const TracedEventArgs& args) const {
+        const TracedEventMetadata& metadata) const {
     // Check for matches with the rules in the order that they are defined.
     for (const auto& rule : mConfig.rules) {
-        if (ruleMatches(rule, args)) {
+        if (ruleMatches(rule, metadata)) {
             return rule.level;
         }
     }
@@ -110,17 +110,23 @@
 }
 
 bool PerfettoBackend::InputEventDataSource::ruleMatches(const TraceRule& rule,
-                                                        const TracedEventArgs& args) const {
+                                                        const TracedEventMetadata& metadata) const {
     // By default, a rule will match all events. Return early if the rule does not match.
 
     // Match the event if it is directed to a secure window.
-    if (rule.matchSecure.has_value() && *rule.matchSecure != args.isSecure) {
+    if (rule.matchSecure.has_value() && *rule.matchSecure != metadata.isSecure) {
+        return false;
+    }
+
+    // Match the event if it was processed while there was an active InputMethod connection.
+    if (rule.matchImeConnectionActive.has_value() &&
+        *rule.matchImeConnectionActive != metadata.isImeConnectionActive) {
         return false;
     }
 
     // Match the event if all of its target packages are explicitly allowed in the "match all" list.
     if (!rule.matchAllPackages.empty() &&
-        !std::all_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+        !std::all_of(metadata.targets.begin(), metadata.targets.end(), [&](const auto& uid) {
             return isPermanentlyAllowed(uid) ||
                     std::any_of(rule.matchAllPackages.begin(), rule.matchAllPackages.end(),
                                 [&](const auto& pkg) { return uid == mUidMap->at(pkg); });
@@ -130,7 +136,7 @@
 
     // Match the event if any of its target packages are allowed in the "match any" list.
     if (!rule.matchAnyPackages.empty() &&
-        !std::any_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+        !std::any_of(metadata.targets.begin(), metadata.targets.end(), [&](const auto& uid) {
             return std::any_of(rule.matchAnyPackages.begin(), rule.matchAnyPackages.end(),
                                [&](const auto& pkg) { return uid == mUidMap->at(pkg); });
         })) {
@@ -166,14 +172,14 @@
 }
 
 void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event,
-                                       const TracedEventArgs& args) {
+                                       const TracedEventMetadata& metadata) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto dataSource = ctx.GetDataSourceLocked();
         dataSource->initializeUidMap(mGetPackageUid);
         if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
             return;
         }
-        const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+        const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
         if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
             return;
         }
@@ -186,14 +192,15 @@
     });
 }
 
-void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, const TracedEventArgs& args) {
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event,
+                                    const TracedEventMetadata& metadata) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto dataSource = ctx.GetDataSourceLocked();
         dataSource->initializeUidMap(mGetPackageUid);
         if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
             return;
         }
-        const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+        const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
         if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
             return;
         }
@@ -207,14 +214,14 @@
 }
 
 void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
-                                          const TracedEventArgs& args) {
+                                          const TracedEventMetadata& metadata) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto dataSource = ctx.GetDataSourceLocked();
         dataSource->initializeUidMap(mGetPackageUid);
         if (!dataSource->getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH)) {
             return;
         }
-        const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+        const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
         if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
             return;
         }
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index af1c6b7..e945066 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -54,9 +54,9 @@
     explicit PerfettoBackend(GetPackageUid);
     ~PerfettoBackend() override = default;
 
-    void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) override;
-    void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) override;
-    void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) override;
+    void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) override;
+    void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) override;
+    void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) override;
 
 private:
     // Implementation of the perfetto data source.
@@ -72,13 +72,13 @@
         void initializeUidMap(GetPackageUid);
         bool shouldIgnoreTracedInputEvent(const EventType&) const;
         inline ftl::Flags<TraceFlag> getFlags() const { return mConfig.flags; }
-        TraceLevel resolveTraceLevel(const TracedEventArgs&) const;
+        TraceLevel resolveTraceLevel(const TracedEventMetadata&) const;
 
     private:
         const int32_t mInstanceId;
         TraceConfig mConfig;
 
-        bool ruleMatches(const TraceRule&, const TracedEventArgs&) const;
+        bool ruleMatches(const TraceRule&, const TracedEventMetadata&) const;
 
         std::optional<std::map<std::string, gui::Uid>> mUidMap;
     };
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
index 1614789..77d09cb 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -54,25 +54,25 @@
 
 template <typename Backend>
 void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event,
-                                                const TracedEventArgs& traceArgs) {
+                                                const TracedEventMetadata& metadata) {
     std::scoped_lock lock(mLock);
-    mQueue.emplace_back(event, traceArgs);
+    mQueue.emplace_back(event, metadata);
     mThreadWakeCondition.notify_all();
 }
 
 template <typename Backend>
 void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event,
-                                             const TracedEventArgs& traceArgs) {
+                                             const TracedEventMetadata& metadata) {
     std::scoped_lock lock(mLock);
-    mQueue.emplace_back(event, traceArgs);
+    mQueue.emplace_back(event, metadata);
     mThreadWakeCondition.notify_all();
 }
 
 template <typename Backend>
 void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
-                                                   const TracedEventArgs& traceArgs) {
+                                                   const TracedEventMetadata& metadata) {
     std::scoped_lock lock(mLock);
-    mQueue.emplace_back(dispatchArgs, traceArgs);
+    mQueue.emplace_back(dispatchArgs, metadata);
     mThreadWakeCondition.notify_all();
 }
 
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
index 3bfc72b..650a87e 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -38,9 +38,9 @@
     ThreadedBackend(Backend&& innerBackend);
     ~ThreadedBackend() override;
 
-    void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) override;
-    void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) override;
-    void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) override;
+    void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) override;
+    void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) override;
+    void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) override;
 
 private:
     std::mutex mLock;
@@ -49,7 +49,7 @@
     Backend mBackend;
     using TraceEntry =
             std::pair<std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>,
-                      TracedEventArgs>;
+                      TracedEventMetadata>;
     std::vector<TraceEntry> mQueue GUARDED_BY(mLock);
 
     // InputThread stops when its destructor is called. Initialize it last so that it is the
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 4d8ffb6..782c84a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -507,10 +507,7 @@
     }
 
     // Touchscreens and touchpad devices.
-    static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
-            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
-    if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
-        classes.test(InputDeviceClass::TOUCH_MT)) {
+    if (classes.test(InputDeviceClass::TOUCHPAD) && classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index 069b50d..b46055e 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -162,7 +162,7 @@
 // --- FakeInputTracingBackend ---
 
 void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event,
-                                            const trace::TracedEventArgs&) {
+                                            const trace::TracedEventMetadata&) {
     {
         std::scoped_lock lock(mTrace->mLock);
         mTrace->mTracedEvents.emplace(event.id, event);
@@ -171,7 +171,7 @@
 }
 
 void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event,
-                                               const trace::TracedEventArgs&) {
+                                               const trace::TracedEventMetadata&) {
     {
         std::scoped_lock lock(mTrace->mLock);
         mTrace->mTracedEvents.emplace(event.id, event);
@@ -180,7 +180,7 @@
 }
 
 void FakeInputTracingBackend::traceWindowDispatch(const trace::WindowDispatchArgs& args,
-                                                  const trace::TracedEventArgs&) {
+                                                  const trace::TracedEventMetadata&) {
     {
         std::scoped_lock lock(mTrace->mLock);
         mTrace->mTracedWindowDispatches.push_back(args);
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index ab05d6b..cd4b507 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -82,11 +82,12 @@
 private:
     std::shared_ptr<VerifyingTrace> mTrace;
 
-    void traceKeyEvent(const trace::TracedKeyEvent& entry, const trace::TracedEventArgs&) override;
+    void traceKeyEvent(const trace::TracedKeyEvent& entry,
+                       const trace::TracedEventMetadata&) override;
     void traceMotionEvent(const trace::TracedMotionEvent& entry,
-                          const trace::TracedEventArgs&) override;
+                          const trace::TracedEventMetadata&) override;
     void traceWindowDispatch(const trace::WindowDispatchArgs& entry,
-                             const trace::TracedEventArgs&) override;
+                             const trace::TracedEventMetadata&) override;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 620edca..c2251a8 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,6 +27,7 @@
 
 #include "LayerFE.h"
 #include "SurfaceFlinger.h"
+#include "common/FlagManager.h"
 #include "ui/FenceResult.h"
 #include "ui/LayerStack.h"
 
@@ -80,6 +81,16 @@
 
 LayerFE::LayerFE(const std::string& name) : mName(name) {}
 
+LayerFE::~LayerFE() {
+    // Ensures that no promise is left unfulfilled before the LayerFE is destroyed.
+    // An unfulfilled promise could occur when a screenshot is attempted, but the
+    // render area is invalid and there is no memory for the capture result.
+    if (FlagManager::getInstance().ce_fence_promise() &&
+        mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+        setReleaseFence(Fence::NO_FENCE);
+    }
+}
+
 const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const {
     return mSnapshot.get();
 }
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 019fa22..658f949 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -36,6 +36,7 @@
 class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
 public:
     LayerFE(const std::string& name);
+    virtual ~LayerFE();
 
     // compositionengine::LayerFE overrides
     const compositionengine::LayerFECompositionState* getCompositionState() const override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 005ec05..c83d81f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -663,7 +663,13 @@
 
 void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
                                    nsecs_t now, LayerHistory::LayerUpdateType updateType) {
-    if (pacesetterSelectorPtr()->canSwitch()) {
+    const auto& selectorPtr = pacesetterSelectorPtr();
+    // Skip recording layer history on LayerUpdateType::SetFrameRate for MRR devices when the
+    // dVRR vote types are guarded (disabled) for MRR. This is to avoid activity when setting dVRR
+    // vote types.
+    if (selectorPtr->canSwitch() &&
+        (updateType != LayerHistory::LayerUpdateType::SetFrameRate ||
+         layerProps.setFrameRateVote.isVoteValidForMrr(selectorPtr->isVrrDevice()))) {
         mLayerHistory.record(id, layerProps, presentTime, now, updateType);
     }
 }
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index b3c1f6b..0b47924 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -427,6 +427,7 @@
         mRateMap[idealPeriod()] = {idealPeriod(), 0};
     }
 
+    mTimelines.clear();
     clearTimestamps();
 }
 
@@ -553,10 +554,23 @@
         mLastTimestampIndex = 0;
     }
 
-    mTimelines.clear();
-    mLastCommittedVsync = TimePoint::fromNs(0);
     mIdealPeriod = Period::fromNs(idealPeriod());
-    mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
+    if (mTimelines.empty()) {
+        mLastCommittedVsync = TimePoint::fromNs(0);
+        mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
+    } else {
+        while (mTimelines.size() > 1) {
+            mTimelines.pop_front();
+        }
+        mTimelines.front().setRenderRate(mRenderRateOpt);
+        // set mLastCommittedVsync to a valid vsync but don't commit too much in the future
+        const auto vsyncOpt = mTimelines.front().nextAnticipatedVSyncTimeFrom(
+            getVSyncPredictionModelLocked(),
+            /* minFramePeriodOpt */ std::nullopt,
+            snapToVsync(mClock->now()), MissedVsync{},
+            /* lastVsyncOpt */ std::nullopt);
+        mLastCommittedVsync = *vsyncOpt;
+    }
 }
 
 bool VSyncPredictor::needsMoreSamples() const {
@@ -588,6 +602,7 @@
     if (mRenderRateOpt &&
         mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
                 mClock->now()) {
+        ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase");
         mTimelines.clear();
         mLastCommittedVsync = TimePoint::fromNs(0);
         mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3ed1d41..a661292 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -83,7 +83,7 @@
     };
 
     struct MissedVsync {
-        TimePoint vsync;
+        TimePoint vsync = TimePoint::fromNs(0);
         Duration fixup = Duration::fromNs(0);
     };
 
@@ -97,7 +97,7 @@
         std::optional<TimePoint> validUntil() const { return mValidUntil; }
         bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
         void shiftVsyncSequence(Duration phase);
-        void setRenderRate(Fps renderRate) { mRenderRateOpt = renderRate; }
+        void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
 
     private:
         nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 0a5cde3..a8fd6b7 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -19,7 +19,7 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
+ } # ce_fence_promise
 
 flag {
   name: "dont_skip_on_early_ro2"