Merge changes from topic "libbinder.stable.abi" into sc-dev

* changes:
  [libbinder] Enumerate symbols exported from libbinder
  [libbinder] Disable Sampling PGO for x86, x86_64
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 204953c..cc0434d 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -292,8 +292,8 @@
     }
 }
 
-static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));
+static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, mode)));
     if (fd.get() < 0) {
         if (errno != EEXIST) {
             PLOG(ERROR) << "Failed to create profile " << profile;
@@ -310,7 +310,7 @@
     return fd;
 }
 
-static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {
+static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags, mode_t mode) {
     // Do not follow symlinks when opening a profile:
     //   - primary profiles should not contain symlinks in their paths
     //   - secondary dex paths should have been already resolved and validated
@@ -320,7 +320,7 @@
     // Reference profiles and snapshots are created on the fly; so they might not exist beforehand.
     unique_fd fd;
     if ((flags & O_CREAT) != 0) {
-        fd = create_profile(uid, profile, flags);
+        fd = create_profile(uid, profile, flags, mode);
     } else {
         fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags)));
     }
@@ -336,6 +336,16 @@
             PLOG(ERROR) << "Failed to open profile " << profile;
         }
         return invalid_unique_fd();
+    } else {
+        // If we just create the file we need to set its mode because on Android
+        // open has a mask that only allows owner access.
+        if ((flags & O_CREAT) != 0) {
+            if (fchmod(fd.get(), mode) != 0) {
+                PLOG(ERROR) << "Could not set mode " << std::hex << mode << std::dec
+                        << " on profile" << profile;
+                // Not a terminal failure.
+            }
+        }
     }
 
     return fd;
@@ -345,20 +355,29 @@
         const std::string& location, bool is_secondary_dex) {
     std::string profile = create_current_profile_path(user, package_name, location,
             is_secondary_dex);
-    return open_profile(uid, profile, O_RDONLY);
+    return open_profile(uid, profile, O_RDONLY, /*mode=*/ 0);
 }
 
 static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
         const std::string& location, bool read_write, bool is_secondary_dex) {
     std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
-    return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+    return open_profile(
+        uid,
+        profile,
+        read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+        S_IRUSR | S_IWUSR | S_IRGRP);  // so that ART can also read it when apps run.
 }
 
 static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
         const std::string& location, bool read_write, bool is_secondary_dex) {
     std::string profile_path = create_reference_profile_path(package_name, location,
                                                              is_secondary_dex);
-    unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+    unique_fd ufd = open_profile(
+        uid,
+        profile_path,
+        read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
+        S_IRUSR | S_IWUSR | S_IRGRP);  // so that ART can also read it when apps run.
+
     return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
         clear_profile(path);
     });
@@ -367,7 +386,7 @@
 static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
         const std::string& location) {
     std::string profile = create_snapshot_profile_path(package_name, location);
-    return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
+    return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC,  S_IRUSR | S_IWUSR);
 }
 
 static void open_profile_files(uid_t uid, const std::string& package_name,
@@ -2484,7 +2503,7 @@
     for (size_t i = 0; i < profiles.size(); )  {
         std::vector<unique_fd> profiles_fd;
         for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
-            unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
+            unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY, /*mode=*/ 0);
             if (fd.get() >= 0) {
                 profiles_fd.push_back(std::move(fd));
             }
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index e272025..216347e 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -919,7 +919,7 @@
             return;
         }
 
-        // Check that the snapshot was created witht he expected acess flags.
+        // Check that the snapshot was created with the expected access flags.
         CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG);
 
         // The snapshot should be equivalent to the merge of profiles.
@@ -962,8 +962,8 @@
             return;
         }
 
-        // Check that the snapshot was created witht he expected acess flags.
-        CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+        // Check that the snapshot was created with the expected access flags.
+        CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0640 | S_IFREG);
 
         // The snapshot should be equivalent to the merge of profiles.
         std::string ref_profile_content = ref_profile_ + ".expected";
diff --git a/include/input/Input.h b/include/input/Input.h
index d4defa8..e8678d2 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -24,6 +24,9 @@
  */
 
 #include <android/input.h>
+#ifdef __linux__
+#include <android/os/IInputConstants.h>
+#endif
 #include <math.h>
 #include <stdint.h>
 #include <ui/Transform.h>
@@ -219,7 +222,16 @@
     POLICY_FLAG_GESTURE = 0x00000008,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
+#ifdef __linux__
+    POLICY_FLAG_INPUTFILTER_TRUSTED = android::os::IInputConstants::POLICY_FLAG_INPUTFILTER_TRUSTED,
 
+    POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
+            android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+#else
+    POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000,
+
+    POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
+#endif
     /* These flags are set by the input dispatcher. */
 
     // Indicates that the input event was injected.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7f0324a..1955104 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -318,6 +318,8 @@
         const std::string& name, InputDeviceConfigurationFileType type);
 
 enum ReservedInputDeviceId : int32_t {
+    // Device id assigned to input events generated inside accessibility service
+    ACCESSIBILITY_DEVICE_ID = -2,
     // Device id of a special "virtual" keyboard that is always present.
     VIRTUAL_KEYBOARD_ID = -1,
     // Device id of the "built-in" keyboard if there is one.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3b3fd08..3920129 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -24,7 +24,6 @@
 use std::ffi::{c_void, CString};
 use std::mem::ManuallyDrop;
 use std::ops::Deref;
-use std::ptr;
 
 /// Rust wrapper around Binder remotable objects.
 ///
@@ -273,7 +272,7 @@
     /// Must be called with a valid pointer to a `T` object. After this call,
     /// the pointer will be invalid and should not be dereferenced.
     unsafe extern "C" fn on_destroy(object: *mut c_void) {
-        ptr::drop_in_place(object as *mut T)
+        Box::from_raw(object as *mut T);
     }
 
     /// Called whenever a new, local `AIBinder` object is needed of a specific
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9610437..660c5bd 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -182,12 +182,12 @@
 
 void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
                                                    sp<SurfaceControl> surfaceControl) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
     mJankListeners.insert({surfaceControl->getHandle(), listener});
 }
 
 void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
     for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
         if (it->second == listener) {
             it = mJankListeners.erase(it);
@@ -210,13 +210,13 @@
 
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
     mSurfaceStatsListeners.insert({surfaceControl->getHandle(),
             SurfaceStatsCallbackEntry(context, cookie, listener)});
 }
 
 void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
     for (auto it = mSurfaceStatsListeners.begin(); it != mSurfaceStatsListeners.end();) {
         auto [itContext, itCookie, itListener] = it->second;
         if (itContext == context && itCookie == cookie) {
@@ -242,8 +242,6 @@
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
-    std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
-    std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
@@ -259,8 +257,6 @@
          * sp<SurfaceControl> that could possibly exist for the callbacks.
          */
         callbacksMap = mCallbacks;
-        jankListenersMap = mJankListeners;
-        surfaceListeners = mSurfaceStatsListeners;
         for (const auto& transactionStats : listenerStats.transactionStats) {
             for (auto& callbackId : transactionStats.callbackIds) {
                 mCallbacks.erase(callbackId);
@@ -341,15 +337,26 @@
                              surfaceControlStats);
         }
         for (const auto& surfaceStats : transactionStats.surfaceStats) {
-            auto listenerRange = surfaceListeners.equal_range(surfaceStats.surfaceControl);
-            for (auto it = listenerRange.first; it != listenerRange.second; it++) {
-                auto entry = it->second;
-                entry.callback(entry.context, transactionStats.latchTime,
-                    transactionStats.presentFence, surfaceStats);
+            {
+                // Acquire surface stats listener lock such that we guarantee that after calling
+                // unregister, there won't be any further callback.
+                std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
+                auto listenerRange = mSurfaceStatsListeners.equal_range(
+                        surfaceStats.surfaceControl);
+                for (auto it = listenerRange.first; it != listenerRange.second; it++) {
+                    auto entry = it->second;
+                    entry.callback(entry.context, transactionStats.latchTime,
+                        transactionStats.presentFence, surfaceStats);
+                }
             }
 
             if (surfaceStats.jankData.empty()) continue;
-            auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
+
+            // Acquire jank listener lock such that we guarantee that after calling unregister,
+            // there won't be any further callback.
+            std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+            auto copy = mJankListeners;
+            auto jankRange = copy.equal_range(surfaceStats.surfaceControl);
             for (auto it = jankRange.first; it != jankRange.second; it++) {
                 it->second->onJankDataAvailable(surfaceStats.jankData);
             }
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 62a782f..5aa132c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -652,6 +652,12 @@
 
     std::mutex mMutex;
 
+    // This lock needs to be recursive so we can unregister a callback from within that callback.
+    std::recursive_mutex mJankListenerMutex;
+
+    // This lock needs to be recursive so we can unregister a callback from within that callback.
+    std::recursive_mutex mSurfaceStatsListenerMutex;
+
     bool mListening GUARDED_BY(mMutex) = false;
 
     int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
@@ -674,11 +680,16 @@
 
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
             GUARDED_BY(mMutex);
-    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+
+    // This is protected by mJankListenerMutex, but GUARDED_BY isn't supported for
+    // std::recursive_mutex
+    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners;
     std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
             mReleaseBufferCallbacks GUARDED_BY(mMutex);
-    std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry>
-                mSurfaceStatsListeners GUARDED_BY(mMutex);
+
+    // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
+    // std::recursive_mutex
+    std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
 
 public:
     static sp<TransactionCompletedListener> getInstance();
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 4b90844..3038d9d 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -40,4 +40,18 @@
      * available.
      */
     const int INVALID_INPUT_EVENT_ID = 0;
+
+    /**
+     * The injected event was originally sent from InputDispatcher. Most likely, the journey of the
+     * event looked as follows:
+     * InputDispatcherPolicyInterface::filterInputEvent -> InputFilter.java::onInputEvent ->
+     * InputFilter.java::sendInputEvent -> InputDispatcher::injectInputEvent, without being modified
+     * along the way.
+     */
+    const int POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000;
+
+    /**
+     * The input event was injected from accessibility
+     */
+    const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 3c58238..b5dd8ac 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,9 +515,10 @@
     return mDrawingBuffer.get();
 }
 
-void GLESRenderEngine::primeCache() {
+std::future<void> GLESRenderEngine::primeCache() {
     ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
                                            mUseColorManagement, mPrecacheToneMapperShaderOnly);
+    return {};
 }
 
 base::unique_fd GLESRenderEngine::flush() {
@@ -969,37 +970,17 @@
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
-bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) {
+bool GLESRenderEngine::canSkipPostRenderCleanup() const {
+    return mPriorResourcesCleaned ||
+            (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled);
+}
+
+void GLESRenderEngine::cleanupPostRender() {
     ATRACE_CALL();
 
-    if (mPriorResourcesCleaned ||
-        (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+    if (canSkipPostRenderCleanup()) {
         // If we don't have a prior frame needing cleanup, then don't do anything.
-        return false;
-    }
-
-    // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should
-    // not need to keep memory around if we don't need to do so.
-    if (mode == CleanupMode::CLEAN_ALL) {
-        // TODO: SurfaceFlinger memory utilization may benefit from resetting
-        // texture bindings as well. Assess if it does and there's no performance regression
-        // when rebinding the same image data to the same texture, and if so then its mode
-        // behavior can be tweaked.
-        if (mPlaceholderImage != EGL_NO_IMAGE_KHR) {
-            for (auto [textureName, bufferId] : mTextureView) {
-                if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) {
-                    glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName);
-                    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
-                                                 static_cast<GLeglImageOES>(mPlaceholderImage));
-                    mTextureView[textureName] = std::nullopt;
-                    checkErrors();
-                }
-            }
-        }
-        {
-            std::lock_guard<std::mutex> lock(mRenderingMutex);
-            mImageCache.clear();
-        }
+        return;
     }
 
     // Bind the texture to placeholder so that backing image data can be freed.
@@ -1010,7 +991,6 @@
     // we could no-op repeated calls of this method instead.
     mLastDrawFence = nullptr;
     mPriorResourcesCleaned = true;
-    return true;
 }
 
 void GLESRenderEngine::cleanFramebufferCache() {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e7ed9c0..915dba3 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
                      EGLSurface protectedStub);
     ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
-    void primeCache() override;
+    std::future<void> primeCache() override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     bool isProtected() const override { return mInProtectedContext; }
@@ -68,7 +68,7 @@
                         const std::shared_ptr<ExternalTexture>& buffer,
                         const bool useFramebufferCache, base::unique_fd&& bufferFence,
                         base::unique_fd* drawFence) override;
-    bool cleanupPostRender(CleanupMode mode) override;
+    void cleanupPostRender() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
     void onPrimaryDisplaySizeChanged(ui::Size size) override {}
@@ -106,6 +106,7 @@
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
             EXCLUDES(mRenderingMutex);
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+    bool canSkipPostRenderCleanup() const override;
 
 private:
     friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index d1bbcc5..ac0affb 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -29,6 +29,7 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
+#include <future>
 #include <memory>
 
 /**
@@ -48,6 +49,11 @@
  */
 #define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME "debug.renderengine.capture_filename"
 
+/**
+ * Allows recording of Skia drawing commands with systrace.
+ */
+#define PROPERTY_SKIA_ATRACE_ENABLED "debug.renderengine.skia_atrace_enabled"
+
 struct ANativeWindowBuffer;
 
 namespace android {
@@ -94,17 +100,13 @@
 
     static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
 
-    RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
-
-    RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
-
     virtual ~RenderEngine() = 0;
 
     // ----- BEGIN DEPRECATED INTERFACE -----
     // This interface, while still in use until a suitable replacement is built,
     // should be considered deprecated, minus some methods which still may be
     // used to support legacy behavior.
-    virtual void primeCache() = 0;
+    virtual std::future<void> primeCache() = 0;
 
     // dump the extension strings. always call the base class.
     virtual void dump(std::string& result) = 0;
@@ -112,25 +114,6 @@
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
 
-    enum class CleanupMode {
-        CLEAN_OUTPUT_RESOURCES,
-        CLEAN_ALL,
-    };
-    // Clean-up method that should be called on the main thread after the
-    // drawFence returned by drawLayers fires. This method will free up
-    // resources used by the most recently drawn frame. If the frame is still
-    // being drawn, then this call is silently ignored.
-    //
-    // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the
-    // output framebuffer are cleaned up, including the sibling texture.
-    //
-    // If mode is CLEAN_ALL, then we also cleanup resources related to any input
-    // buffers.
-    //
-    // Returns true if resources were cleaned up, and false if we didn't need to
-    // do any work.
-    virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
-
     // queries that are required to be thread safe
     virtual size_t getMaxTextureSize() const = 0;
     virtual size_t getMaxViewportDims() const = 0;
@@ -184,6 +167,13 @@
                                 const std::shared_ptr<ExternalTexture>& buffer,
                                 const bool useFramebufferCache, base::unique_fd&& bufferFence,
                                 base::unique_fd* drawFence) = 0;
+
+    // Clean-up method that should be called on the main thread after the
+    // drawFence returned by drawLayers fires. This method will free up
+    // resources used by the most recently drawn frame. If the frame is still
+    // being drawn, then the implementation is free to silently ignore this call.
+    virtual void cleanupPostRender() = 0;
+
     virtual void cleanFramebufferCache() = 0;
     // Returns the priority this context was actually created with. Note: this may not be
     // the same as specified at context creation time, due to implementation limits on the
@@ -204,6 +194,10 @@
     static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
 
 protected:
+    RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+    RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+
     // Maps GPU resources for this buffer.
     // Note that work may be deferred to an additional thread, i.e. this call
     // is made asynchronously, but the caller can expect that map/unmap calls
@@ -228,8 +222,15 @@
     // that's conflict serializable, i.e. unmap a buffer should never occur before binding the
     // buffer if the caller called mapExternalTextureBuffer before calling unmap.
     virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+
+    // A thread safe query to determine if any post rendering cleanup is necessary.  Returning true
+    // is a signal that calling the postRenderCleanup method would be a no-op and that callers can
+    // avoid any thread synchronization that may be required by directly calling postRenderCleanup.
+    virtual bool canSkipPostRenderCleanup() const = 0;
+
     friend class ExternalTexture;
     friend class threaded::RenderEngineThreaded;
+    friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
     const RenderEngineType mRenderEngineType;
 };
 
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 27dbd1e..0175af3 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
     RenderEngine();
     ~RenderEngine() override;
 
-    MOCK_METHOD0(primeCache, void());
+    MOCK_METHOD0(primeCache, std::future<void>());
     MOCK_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
@@ -45,7 +45,8 @@
     MOCK_CONST_METHOD0(isProtected, bool());
     MOCK_CONST_METHOD0(supportsProtectedContent, bool());
     MOCK_METHOD1(useProtectedContext, bool(bool));
-    MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
+    MOCK_METHOD0(cleanupPostRender, void());
+    MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
     MOCK_METHOD6(drawLayers,
                  status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
                           const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8356005..5c122d4 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -29,8 +29,8 @@
 namespace skia {
 
 AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
-                                       bool isOutputBuffer)
-      : mIsOutputBuffer(isOutputBuffer) {
+                                       bool isOutputBuffer, CleanupManager& cleanupMgr)
+      : mCleanupMgr(cleanupMgr), mIsOutputBuffer(isOutputBuffer) {
     ATRACE_CALL();
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
@@ -49,6 +49,13 @@
              this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
 }
 
+AutoBackendTexture::~AutoBackendTexture() {
+    if (mBackendTexture.isValid()) {
+        mDeleteProc(mImageCtx);
+        mBackendTexture = {};
+    }
+}
+
 void AutoBackendTexture::unref(bool releaseLocalResources) {
     if (releaseLocalResources) {
         mSurface = nullptr;
@@ -57,11 +64,7 @@
 
     mUsageCount--;
     if (mUsageCount <= 0) {
-        if (mBackendTexture.isValid()) {
-            mDeleteProc(mImageCtx);
-            mBackendTexture = {};
-        }
-        delete this;
+        mCleanupMgr.add(this);
     }
 }
 
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index a9e8430..00b901b 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -25,6 +25,9 @@
 
 #include "android-base/macros.h"
 
+#include <mutex>
+#include <vector>
+
 namespace android {
 namespace renderengine {
 namespace skia {
@@ -36,13 +39,50 @@
  */
 class AutoBackendTexture {
 public:
+    // Manager class that is responsible for the immediate or deferred cleanup
+    // of AutoBackendTextures.  Clients of AutoBackendTexture are responsible for
+    // ensuring that access to this class is thread safe.  Clients also control when
+    // the resources are reclaimed by setting the manager into deferred mode.
+    class CleanupManager {
+    public:
+        CleanupManager() = default;
+        void add(AutoBackendTexture* abt) {
+            if (mDeferCleanup) {
+                mCleanupList.push_back(abt);
+            } else {
+                delete abt;
+            }
+        }
+
+        void setDeferredStatus(bool enabled) { mDeferCleanup = enabled; }
+
+        bool isEmpty() const { return mCleanupList.empty(); }
+
+        // If any AutoBackedTextures were added while in deferred mode this method
+        // will ensure they are deleted before returning.  It must only be called
+        // on the thread where the GPU context that created the AutoBackedTexture
+        // is active.
+        void cleanup() {
+            for (auto abt : mCleanupList) {
+                delete abt;
+            }
+            mCleanupList.clear();
+        }
+
+    private:
+        DISALLOW_COPY_AND_ASSIGN(CleanupManager);
+        bool mDeferCleanup = false;
+        std::vector<AutoBackendTexture*> mCleanupList;
+    };
+
     // Local reference that supports RAII-style management of an AutoBackendTexture
     // AutoBackendTexture by itself can't be managed in a similar fashion because
     // of shared ownership with Skia objects, so we wrap it here instead.
     class LocalRef {
     public:
-        LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
-            mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+        LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+                 CleanupManager& cleanupMgr) {
+            mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr);
             mTexture->ref();
         }
 
@@ -75,10 +115,11 @@
 
 private:
     // Creates a GrBackendTexture whose contents come from the provided buffer.
-    AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+    AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+                       CleanupManager& cleanupMgr);
 
     // The only way to invoke dtor is with unref, when mUsageCount is 0.
-    ~AutoBackendTexture() {}
+    ~AutoBackendTexture();
 
     void ref() { mUsageCount++; }
 
@@ -100,6 +141,8 @@
     GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
     GrAHardwareBufferUtils::TexImageCtx mImageCtx;
 
+    CleanupManager& mCleanupMgr;
+
     static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
     static void releaseImageProc(SkImage::ReleaseContext releaseContext);
 
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 8a41395..b3975b0 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -292,7 +292,11 @@
 
         drawSolidLayers(renderengine, display, dstTexture);
         drawShadowLayers(renderengine, display, srcTexture);
-        drawBlurLayers(renderengine, display, dstTexture);
+
+        if (renderengine->supportsBackgroundBlur()) {
+            drawBlurLayers(renderengine, display, dstTexture);
+        }
+
         // The majority of shaders are related to sampling images.
         drawImageLayers(renderengine, display, dstTexture, srcTexture);
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index d4b139c..d28d623 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -235,8 +235,9 @@
     return engine;
 }
 
-void SkiaGLRenderEngine::primeCache() {
+std::future<void> SkiaGLRenderEngine::primeCache() {
     Cache::primeShaderCache(this);
+    return {};
 }
 
 EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
@@ -316,6 +317,7 @@
     GrContextOptions options;
     options.fDisableDriverCorrectnessWorkarounds = true;
     options.fDisableDistanceFieldPaths = true;
+    options.fReducedShaderVariations = true;
     options.fPersistentCache = &mSkSLCacheMonitor;
     mGrContext = GrDirectContext::MakeGL(glInterface, options);
     if (useProtectedContext(true)) {
@@ -507,17 +509,18 @@
         return;
     }
     // We currently don't attempt to map a buffer if the buffer contains protected content
-    // or we are using a protected context because GPU resources for protected buffers is
-    // much more limited.
+    // because GPU resources for protected buffers is much more limited.
     const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
-    if (isProtectedBuffer || mInProtectedContext) {
+    if (isProtectedBuffer) {
         return;
     }
     ATRACE_CALL();
 
-    // If we were to support caching protected buffers then we will need to switch the currently
-    // bound context if we are not already using the protected context (and subsequently switch
-    // back after the buffer is cached).
+    // If we were to support caching protected buffers then we will need to switch the
+    // currently bound context if we are not already using the protected context (and subsequently
+    // switch back after the buffer is cached).  However, for non-protected content we can bind
+    // the texture in either GL context because they are initialized with the same share_context
+    // which allows the texture state to be shared between them.
     auto grContext = getActiveGrContext();
     auto& cache = mTextureCache;
 
@@ -528,7 +531,7 @@
         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
                                                                buffer->toAHardwareBuffer(),
-                                                               isRenderable);
+                                                               isRenderable, mTextureCleanupMgr);
         cache.insert({buffer->getId(), imageTextureRef});
     }
 }
@@ -551,12 +554,36 @@
 
         if (iter->second == 0) {
             mTextureCache.erase(buffer->getId());
-            mProtectedTextureCache.erase(buffer->getId());
             mGraphicBufferExternalRefs.erase(buffer->getId());
         }
     }
 }
 
+bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    return mTextureCleanupMgr.isEmpty();
+}
+
+void SkiaGLRenderEngine::cleanupPostRender() {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    mTextureCleanupMgr.cleanup();
+}
+
+// Helper class intended to be used on the stack to ensure that texture cleanup
+// is deferred until after this class goes out of scope.
+class DeferTextureCleanup final {
+public:
+    DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
+        mMgr.setDeferredStatus(true);
+    }
+    ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
+    AutoBackendTexture::CleanupManager& mMgr;
+};
+
 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
         sk_sp<SkShader> shader,
         const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
@@ -703,7 +730,10 @@
     validateOutputBufferUsage(buffer->getBuffer());
 
     auto grContext = getActiveGrContext();
-    auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
+    auto& cache = mTextureCache;
+
+    // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
+    DeferTextureCleanup dtc(mTextureCleanupMgr);
 
     std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
     if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
@@ -713,7 +743,7 @@
                 std::make_shared<AutoBackendTexture::LocalRef>(grContext,
                                                                buffer->getBuffer()
                                                                        ->toAHardwareBuffer(),
-                                                               true);
+                                                               true, mTextureCleanupMgr);
     }
 
     const ui::Dataspace dstDataspace =
@@ -969,7 +999,7 @@
                 imageTextureRef = std::make_shared<
                         AutoBackendTexture::LocalRef>(grContext,
                                                       item.buffer->getBuffer()->toAHardwareBuffer(),
-                                                      false);
+                                                      false, mTextureCleanupMgr);
             }
 
             // isOpaque means we need to ignore the alpha in the image,
@@ -1446,12 +1476,6 @@
         StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
         gpuProtectedReporter.logOutput(result, true);
 
-        StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
-                      mProtectedTextureCache.size());
-        StringAppendF(&result, "Dumping buffer ids...\n");
-        for (const auto& [id, unused] : mProtectedTextureCache) {
-            StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
-        }
         StringAppendF(&result, "\n");
         StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
         for (const auto& [linearEffect, unused] : mRuntimeEffects) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 97d3b72..b30355b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -53,13 +53,14 @@
                        EGLSurface protectedPlaceholder);
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
-    void primeCache() override;
+    std::future<void> primeCache() override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
                         const std::shared_ptr<ExternalTexture>& buffer,
                         const bool useFramebufferCache, base::unique_fd&& bufferFence,
                         base::unique_fd* drawFence) override;
-    void cleanFramebufferCache() override {}
+    void cleanupPostRender() override;
+    void cleanFramebufferCache() override{};
     int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
@@ -75,6 +76,7 @@
     size_t getMaxViewportDims() const override;
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+    bool canSkipPostRenderCleanup() const override;
 
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -126,19 +128,18 @@
     // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
     std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
             GUARDED_BY(mRenderingMutex);
-    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+    // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
     std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
             GUARDED_BY(mRenderingMutex);
-    std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
-            mProtectedTextureCache GUARDED_BY(mRenderingMutex);
     std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+    AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
 
     StretchShaderFactory mStretchShaderFactory;
     // Mutex guarding rendering operations, so that:
     // 1. GL operations aren't interleaved, and
     // 2. Internal state related to rendering that is potentially modified by
     // multiple threads is guaranteed thread-safe.
-    std::mutex mRenderingMutex;
+    mutable std::mutex mRenderingMutex;
 
     sp<Fence> mLastDrawFence;
 
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 81f0b6f..29175a2 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -14,13 +14,22 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "SkiaRenderEngine.h"
+
+#include <android-base/properties.h>
+#include <src/core/SkTraceEventCommon.h>
+
 namespace android {
 namespace renderengine {
-namespace skia {} // namespace skia
+namespace skia {
+SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {
+    SkAndroidFrameworkTraceUtil::setEnableTracing(
+            base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false));
+}
+} // namespace skia
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 308c5ff..31ad63e 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -36,10 +36,10 @@
 class SkiaRenderEngine : public RenderEngine {
 public:
     static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
-    SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
+    SkiaRenderEngine(RenderEngineType type);
     ~SkiaRenderEngine() override {}
 
-    virtual void primeCache() override{};
+    virtual std::future<void> primeCache() override { return {}; };
     virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
@@ -53,15 +53,14 @@
                                 base::unique_fd* /*drawFence*/) override {
         return 0;
     };
-    virtual bool cleanupPostRender(CleanupMode) override { return true; };
     virtual int getContextPriority() override { return 0; }
     virtual void assertShadersCompiled(int numShaders) {}
     virtual int reportShadersCompiled() { return 0; }
 
 protected:
     virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
-                                          bool /*isRenderable*/) override;
-    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
+                                          bool /*isRenderable*/) override = 0;
+    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index dfab6e8..e258741 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -43,6 +43,7 @@
 constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
 
 namespace android {
+namespace renderengine {
 
 class RenderEngineFactory {
 public:
@@ -1779,13 +1780,6 @@
 }
 
 TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
     initializeRenderEngine();
 
     renderengine::DisplaySettings settings;
@@ -1810,53 +1804,9 @@
         sync_wait(fd, -1);
     }
     // Only cleanup the first time.
-    EXPECT_TRUE(mRE->cleanupPostRender(
-            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
-    EXPECT_FALSE(mRE->cleanupPostRender(
-            renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
-}
-
-TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
-    initializeRenderEngine();
-
-    renderengine::DisplaySettings settings;
-    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    settings.physicalDisplay = fullscreenRect();
-    settings.clip = fullscreenRect();
-
-    std::vector<const renderengine::LayerSettings*> layers;
-    renderengine::LayerSettings layer;
-    layer.geometry.boundaries = fullscreenRect().toFloatRect();
-    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-    layer.alpha = 1.0;
-    layers.push_back(&layer);
-
-    base::unique_fd fence;
-    mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
-
-    const int fd = fence.get();
-    if (fd >= 0) {
-        sync_wait(fd, -1);
-    }
-
-    uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
-    uint32_t texName = layer.source.buffer.textureName;
-    EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
-    EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
-
-    EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
-
-    // Now check that our view of memory is good.
-    EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
-    EXPECT_EQ(std::nullopt, mGLESRE->getBufferIdForTextureNameForTesting(bufferId));
-    EXPECT_TRUE(mGLESRE->isTextureNameKnownForTesting(texName));
+    EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
+    mRE->cleanupPostRender();
+    EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
 }
 
 TEST_P(RenderEngineTest, testRoundedCornersCrop) {
@@ -2080,6 +2030,7 @@
         expectBufferColor(rect, 0, 255, 0, 255);
     }
 }
+} // namespace renderengine
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index e3917cc..c65e731 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -130,22 +130,22 @@
     ASSERT_EQ(true, result);
 }
 
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
-    EXPECT_CALL(*mRenderEngine,
-                cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
-            .WillOnce(Return(false));
-    status_t result =
-            mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
-    ASSERT_EQ(false, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
+    EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0);
+    mThreadedRE->cleanupPostRender();
+
+    // call ANY synchronous function to ensure that cleanupPostRender has completed.
+    mThreadedRE->getContextPriority();
 }
 
-TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
-    EXPECT_CALL(*mRenderEngine,
-                cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL))
-            .WillOnce(Return(true));
-    status_t result =
-            mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL);
-    ASSERT_EQ(true, result);
+TEST_F(RenderEngineThreadedTest, PostRenderCleanup_notSkipped) {
+    EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return());
+    mThreadedRE->cleanupPostRender();
+
+    // call ANY synchronous function to ensure that cleanupPostRender has completed.
+    mThreadedRE->getContextPriority();
 }
 
 TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsFalse) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 9009ce4..ea3871f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -24,6 +24,7 @@
 
 #include <android-base/stringprintf.h>
 #include <private/gui/SyncFeatures.h>
+#include <processgroup/processgroup.h>
 #include <utils/Trace.h>
 
 #include "gl/GLESRenderEngine.h"
@@ -48,44 +49,74 @@
 }
 
 RenderEngineThreaded::~RenderEngineThreaded() {
-    {
-        std::lock_guard lock(mThreadMutex);
-        mRunning = false;
-        mCondition.notify_one();
-    }
+    mRunning = false;
+    mCondition.notify_one();
 
     if (mThread.joinable()) {
         mThread.join();
     }
 }
 
+status_t RenderEngineThreaded::setSchedFifo(bool enabled) {
+    static constexpr int kFifoPriority = 2;
+    static constexpr int kOtherPriority = 0;
+
+    struct sched_param param = {0};
+    int sched_policy;
+    if (enabled) {
+        sched_policy = SCHED_FIFO;
+        param.sched_priority = kFifoPriority;
+    } else {
+        sched_policy = SCHED_OTHER;
+        param.sched_priority = kOtherPriority;
+    }
+
+    if (sched_setscheduler(0, sched_policy, &param) != 0) {
+        return -errno;
+    }
+    return NO_ERROR;
+}
+
 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
 void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
     ATRACE_CALL();
 
-    struct sched_param param = {0};
-    param.sched_priority = 2;
-    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
-        ALOGE("Couldn't set SCHED_FIFO");
+    if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
+        ALOGW("Failed to set render-engine task profile!");
+    }
+
+    if (setSchedFifo(true) != NO_ERROR) {
+        ALOGW("Couldn't set SCHED_FIFO");
     }
 
     mRenderEngine = factory();
 
-    std::unique_lock<std::mutex> lock(mThreadMutex);
     pthread_setname_np(pthread_self(), mThreadName);
 
     {
-        std::unique_lock<std::mutex> lock(mInitializedMutex);
+        std::scoped_lock lock(mInitializedMutex);
         mIsInitialized = true;
     }
     mInitializedCondition.notify_all();
 
     while (mRunning) {
-        if (!mFunctionCalls.empty()) {
-            auto task = mFunctionCalls.front();
-            mFunctionCalls.pop();
-            task(*mRenderEngine);
+        const auto getNextTask = [this]() -> std::optional<Work> {
+            std::scoped_lock lock(mThreadMutex);
+            if (!mFunctionCalls.empty()) {
+                Work task = mFunctionCalls.front();
+                mFunctionCalls.pop();
+                return std::make_optional<Work>(task);
+            }
+            return std::nullopt;
+        };
+
+        const auto task = getNextTask();
+
+        if (task) {
+            (*task)(*mRenderEngine);
         }
+
+        std::unique_lock<std::mutex> lock(mThreadMutex);
         mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
             return !mRunning || !mFunctionCalls.empty();
         });
@@ -100,18 +131,31 @@
     mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
 }
 
-void RenderEngineThreaded::primeCache() {
+std::future<void> RenderEngineThreaded::primeCache() {
+    const auto resultPromise = std::make_shared<std::promise<void>>();
+    std::future<void> resultFuture = resultPromise->get_future();
     ATRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([resultPromise](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::primeCache");
+            if (setSchedFifo(false) != NO_ERROR) {
+                ALOGW("Couldn't set SCHED_OTHER for primeCache");
+            }
+
             instance.primeCache();
+            resultPromise->set_value();
+
+            if (setSchedFifo(true) != NO_ERROR) {
+                ALOGW("Couldn't set SCHED_FIFO for primeCache");
+            }
         });
     }
     mCondition.notify_one();
+
+    return resultFuture;
 }
 
 void RenderEngineThreaded::dump(std::string& result) {
@@ -231,19 +275,26 @@
     return resultFuture.get();
 }
 
-bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
+void RenderEngineThreaded::cleanupPostRender() {
+    if (canSkipPostRenderCleanup()) {
+        return;
+    }
+
+    // This function is designed so it can run asynchronously, so we do not need to wait
+    // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::cleanupPostRender");
-            bool returnValue = instance.cleanupPostRender(mode);
-            resultPromise.set_value(returnValue);
+        mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+            instance.cleanupPostRender();
         });
     }
     mCondition.notify_one();
-    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
+    waitUntilInitialized();
+    return mRenderEngine->canSkipPostRenderCleanup();
 }
 
 status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index eb6098e..9b523b2 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
 
     RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
     ~RenderEngineThreaded() override;
-    void primeCache() override;
+    std::future<void> primeCache() override;
 
     void dump(std::string& result) override;
 
@@ -54,7 +54,7 @@
     bool isProtected() const override;
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
-    bool cleanupPostRender(CleanupMode mode) override;
+    void cleanupPostRender() override;
 
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
@@ -70,10 +70,12 @@
 protected:
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
     void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+    bool canSkipPostRenderCleanup() const override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
     void waitUntilInitialized() const;
+    static status_t setSchedFifo(bool enabled);
 
     /* ------------------------------------------------------------------------
      * Threading
@@ -82,9 +84,10 @@
     // Protects the creation and destruction of mThread.
     mutable std::mutex mThreadMutex;
     std::thread mThread GUARDED_BY(mThreadMutex);
-    bool mRunning GUARDED_BY(mThreadMutex) = true;
-    mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
-            GUARDED_BY(mThreadMutex);
+    std::atomic<bool> mRunning = true;
+
+    using Work = std::function<void(renderengine::RenderEngine&)>;
+    mutable std::queue<Work> mFunctionCalls GUARDED_BY(mThreadMutex);
     mutable std::condition_variable mCondition;
 
     // Used to allow select thread safe methods to be accessed without requiring the
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 847b351..efbe613 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -82,9 +82,13 @@
 
 void egl_surface_t::disconnect() {
     if (win != nullptr && connected) {
-        native_window_set_buffers_format(win, 0);
-        if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
-            ALOGW("EGLNativeWindowType %p disconnect failed", win);
+        // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+        // native_window_* calls, so don't do them here.
+        if (!cnx->useAngle) {
+            native_window_set_buffers_format(win, 0);
+            if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) {
+                ALOGW("EGLNativeWindowType %p disconnect failed", win);
+            }
         }
         connected = false;
     }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index cf433c0..d2b8739 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3787,7 +3787,7 @@
         if (shouldSendKeyToInputFilterLocked(args)) {
             mLock.unlock();
 
-            policyFlags |= POLICY_FLAG_FILTERED;
+            policyFlags |= POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                 return; // event was consumed by the filter
             }
@@ -4009,6 +4009,19 @@
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
 
+    // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
+    // that have gone through the InputFilter. If the event passed through the InputFilter,
+    // but did not get modified, assign the provided device id. If the InputFilter modifies the
+    // events in any way, it is responsible for removing this flag.
+    // If the injected event originated from accessibility, assign the accessibility device id,
+    // so that it can be distinguished from regular injected events.
+    int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
+    if (policyFlags & POLICY_FLAG_INPUTFILTER_TRUSTED) {
+        resolvedDeviceId = event->getDeviceId();
+    } else if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+        resolvedDeviceId = ACCESSIBILITY_DEVICE_ID;
+    }
+
     std::queue<std::unique_ptr<EventEntry>> injectedEntries;
     switch (event->getType()) {
         case AINPUT_EVENT_TYPE_KEY: {
@@ -4021,10 +4034,10 @@
             int32_t flags = incomingKey.getFlags();
             int32_t keyCode = incomingKey.getKeyCode();
             int32_t metaState = incomingKey.getMetaState();
-            accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action,
+            accelerateMetaShortcuts(resolvedDeviceId, action,
                                     /*byref*/ keyCode, /*byref*/ metaState);
             KeyEvent keyEvent;
-            keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+            keyEvent.initialize(incomingKey.getId(), resolvedDeviceId, incomingKey.getSource(),
                                 incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
                                 incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
                                 incomingKey.getDownTime(), incomingKey.getEventTime());
@@ -4045,7 +4058,7 @@
             mLock.lock();
             std::unique_ptr<KeyEntry> injectedEntry =
                     std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
-                                               VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                               resolvedDeviceId, incomingKey.getSource(),
                                                incomingKey.getDisplayId(), policyFlags, action,
                                                flags, keyCode, incomingKey.getScanCode(), metaState,
                                                incomingKey.getRepeatCount(),
@@ -4055,18 +4068,18 @@
         }
 
         case AINPUT_EVENT_TYPE_MOTION: {
-            const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-            int32_t action = motionEvent->getAction();
-            size_t pointerCount = motionEvent->getPointerCount();
-            const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
-            int32_t actionButton = motionEvent->getActionButton();
-            int32_t displayId = motionEvent->getDisplayId();
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+            int32_t action = motionEvent.getAction();
+            size_t pointerCount = motionEvent.getPointerCount();
+            const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
+            int32_t actionButton = motionEvent.getActionButton();
+            int32_t displayId = motionEvent.getDisplayId();
             if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
                 return InputEventInjectionResult::FAILED;
             }
 
             if (!(policyFlags & POLICY_FLAG_FILTERED)) {
-                nsecs_t eventTime = motionEvent->getEventTime();
+                nsecs_t eventTime = motionEvent.getEventTime();
                 android::base::Timer t;
                 mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
                 if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4076,47 +4089,46 @@
             }
 
             mLock.lock();
-            const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
-            const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+            const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
+            const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
             std::unique_ptr<MotionEntry> injectedEntry =
-                    std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
-                                                  VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
-                                                  motionEvent->getDisplayId(), policyFlags, action,
-                                                  actionButton, motionEvent->getFlags(),
-                                                  motionEvent->getMetaState(),
-                                                  motionEvent->getButtonState(),
-                                                  motionEvent->getClassification(),
-                                                  motionEvent->getEdgeFlags(),
-                                                  motionEvent->getXPrecision(),
-                                                  motionEvent->getYPrecision(),
-                                                  motionEvent->getRawXCursorPosition(),
-                                                  motionEvent->getRawYCursorPosition(),
-                                                  motionEvent->getDownTime(),
-                                                  uint32_t(pointerCount), pointerProperties,
-                                                  samplePointerCoords, motionEvent->getXOffset(),
-                                                  motionEvent->getYOffset());
+                    std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+                                                  resolvedDeviceId, motionEvent.getSource(),
+                                                  motionEvent.getDisplayId(), policyFlags, action,
+                                                  actionButton, motionEvent.getFlags(),
+                                                  motionEvent.getMetaState(),
+                                                  motionEvent.getButtonState(),
+                                                  motionEvent.getClassification(),
+                                                  motionEvent.getEdgeFlags(),
+                                                  motionEvent.getXPrecision(),
+                                                  motionEvent.getYPrecision(),
+                                                  motionEvent.getRawXCursorPosition(),
+                                                  motionEvent.getRawYCursorPosition(),
+                                                  motionEvent.getDownTime(), uint32_t(pointerCount),
+                                                  pointerProperties, samplePointerCoords,
+                                                  motionEvent.getXOffset(),
+                                                  motionEvent.getYOffset());
             injectedEntries.push(std::move(injectedEntry));
-            for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+            for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
                 samplePointerCoords += pointerCount;
                 std::unique_ptr<MotionEntry> nextInjectedEntry =
-                        std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
-                                                      VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
-                                                      motionEvent->getDisplayId(), policyFlags,
-                                                      action, actionButton, motionEvent->getFlags(),
-                                                      motionEvent->getMetaState(),
-                                                      motionEvent->getButtonState(),
-                                                      motionEvent->getClassification(),
-                                                      motionEvent->getEdgeFlags(),
-                                                      motionEvent->getXPrecision(),
-                                                      motionEvent->getYPrecision(),
-                                                      motionEvent->getRawXCursorPosition(),
-                                                      motionEvent->getRawYCursorPosition(),
-                                                      motionEvent->getDownTime(),
+                        std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
+                                                      resolvedDeviceId, motionEvent.getSource(),
+                                                      motionEvent.getDisplayId(), policyFlags,
+                                                      action, actionButton, motionEvent.getFlags(),
+                                                      motionEvent.getMetaState(),
+                                                      motionEvent.getButtonState(),
+                                                      motionEvent.getClassification(),
+                                                      motionEvent.getEdgeFlags(),
+                                                      motionEvent.getXPrecision(),
+                                                      motionEvent.getYPrecision(),
+                                                      motionEvent.getRawXCursorPosition(),
+                                                      motionEvent.getRawYCursorPosition(),
+                                                      motionEvent.getDownTime(),
                                                       uint32_t(pointerCount), pointerProperties,
-                                                      samplePointerCoords,
-                                                      motionEvent->getXOffset(),
-                                                      motionEvent->getYOffset());
+                                                      samplePointerCoords, motionEvent.getXOffset(),
+                                                      motionEvent.getYOffset());
                 injectedEntries.push(std::move(nextInjectedEntry));
             }
             break;
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index c075078..c7c8e28 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -169,8 +169,9 @@
     mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()),
                    std::make_move_iterator(frames.end()));
     if (mFrames.size() > MAX_QUEUE_SIZE) {
-        ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE,
-              mFrames.size() - MAX_QUEUE_SIZE);
+        // A user-space grip suppression process may be processing the video frames, and holding
+        // back the input events. This could result in video frames being produced without the
+        // matching input events. Drop the oldest frame here to prepare for the next input event.
         mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE);
     }
     return numFrames;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 93aa6ac..d51acce 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -473,6 +473,7 @@
                           const sp<InputWindowHandle>& focusedWindow = nullptr) {
         FocusRequest request;
         request.token = window->getToken();
+        request.windowName = window->getName();
         if (focusedWindow) {
             request.focusedToken = focusedWindow->getToken();
         }
@@ -1085,6 +1086,20 @@
         return mInputReceiver->consume();
     }
 
+    MotionEvent* consumeMotion() {
+        InputEvent* event = consume();
+        if (event == nullptr) {
+            ADD_FAILURE() << "Consume failed : no event";
+            return nullptr;
+        }
+        if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+            ADD_FAILURE() << "Instead of motion event, got "
+                          << inputEventTypeToString(event->getType());
+            return nullptr;
+        }
+        return static_cast<MotionEvent*>(event);
+    }
+
     void assertNoEvents() {
         if (mInputReceiver == nullptr &&
             mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
@@ -2446,13 +2461,10 @@
                 generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
         mDispatcher->notifyMotion(&motionArgs);
 
-        InputEvent* event = window->consume();
+        MotionEvent* event = window->consumeMotion();
         ASSERT_NE(event, nullptr);
-        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
-                << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
-                << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
-        const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+        const MotionEvent& motionEvent = *event;
         EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
         EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
 
@@ -3118,6 +3130,70 @@
     testNotifyKey(/*expectToBeFiltered*/ false);
 }
 
+class InputFilterInjectionPolicyTest : public InputDispatcherTest {
+protected:
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        /**
+         * We don't need to enable input filter to test the injected event policy, but we enabled it
+         * here to make the tests more realistic, since this policy only matters when inputfilter is
+         * on.
+         */
+        mDispatcher->setInputFilterEnabled(true);
+
+        std::shared_ptr<InputApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        mWindow =
+                new FakeWindowHandle(application, mDispatcher, "Test Window", ADISPLAY_ID_DEFAULT);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+        mWindow->setFocusable(true);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+        setFocusedWindow(mWindow);
+        mWindow->consumeFocusEvent(true);
+    }
+
+    void testInjectedKey(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId) {
+        KeyEvent event;
+
+        const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        event.initialize(InputEvent::nextId(), injectedDeviceId, AINPUT_SOURCE_KEYBOARD,
+                         ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A,
+                         KEY_A, AMETA_NONE, 0 /*repeatCount*/, eventTime, eventTime);
+        const int32_t additionalPolicyFlags =
+                POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                                InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+                                                policyFlags | additionalPolicyFlags));
+
+        InputEvent* received = mWindow->consume();
+        ASSERT_NE(nullptr, received);
+        ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
+    }
+
+private:
+    sp<FakeWindowHandle> mWindow;
+};
+
+TEST_F(InputFilterInjectionPolicyTest, TrustedFilteredEvents_KeepOriginalDeviceId) {
+    // We don't need POLICY_FLAG_FILTERED here, but it will be set in practice, so keep it to make
+    // the test more closely resemble the real usage
+    testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED, 3 /*injectedDeviceId*/,
+                    3 /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, EventsInjectedFromAccessibility_HaveAccessibilityDeviceId) {
+    testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+                    3 /*injectedDeviceId*/, ACCESSIBILITY_DEVICE_ID /*resolvedDeviceId*/);
+}
+
+TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
+    testInjectedKey(0 /*policyFlags*/, 3 /*injectedDeviceId*/,
+                    VIRTUAL_KEYBOARD_ID /*resolvedDeviceId*/);
+}
+
 class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9df020d..2281721 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1626,19 +1626,17 @@
 }
 
 void SensorService::notifyProximityStateLocked(
-        const std::vector<sp<ProximityActiveListener>>& listnrs) {
-    std::async(
-        std::launch::async,
-        [](uint64_t mySeq, bool isActive, std::vector<sp<ProximityActiveListener>> listeners) {
-            while (completedCallbackSeq.load() != mySeq - 1)
-                std::this_thread::sleep_for(1ms);
-            for (auto& listener : listeners)
-                listener->onProximityActive(isActive);
-            completedCallbackSeq++;
-        },
-        ++curProxCallbackSeq, mProximityActiveCount > 0,
-        listnrs /* (this is meant to be a copy) */
-    );
+        const std::vector<sp<ProximityActiveListener>>& listeners) {
+    const bool isActive = mProximityActiveCount > 0;
+    const uint64_t mySeq = ++curProxCallbackSeq;
+    std::thread t([isActive, mySeq, listenersCopy = listeners]() {
+        while (completedCallbackSeq.load() != mySeq - 1)
+            std::this_thread::sleep_for(1ms);
+        for (auto& listener : listenersCopy)
+            listener->onProximityActive(isActive);
+        completedCallbackSeq++;
+    });
+    t.detach();
 }
 
 status_t SensorService::addProximityActiveListener(const sp<ProximityActiveListener>& callback) {
@@ -2172,6 +2170,13 @@
     return true;
 }
 
+/**
+ * Checks if a sensor should be capped according to HIGH_SAMPLING_RATE_SENSORS
+ * permission.
+ *
+ * This needs to be kept in sync with the list defined on the Java side
+ * in frameworks/base/core/java/android/hardware/SystemSensorManager.java
+ */
 bool SensorService::isSensorInCappedSet(int sensorType) {
     return (sensorType == SENSOR_TYPE_ACCELEROMETER
             || sensorType == SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index d68a0e0..6213a8a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -33,6 +33,7 @@
 #include <gui/BufferQueue.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
+#include "TunnelModeEnabledReporter.h"
 
 #include "EffectLayer.h"
 #include "FrameTracer/FrameTracer.h"
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 289cb11..29937fb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -82,6 +82,9 @@
 
     // The earliest time to send the present command to the HAL
     std::chrono::steady_clock::time_point earliestPresentTime;
+
+    // The predicted next invalidation time
+    std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 257974f..1416b1e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -286,7 +286,7 @@
     virtual std::optional<base::unique_fd> composeSurfaces(
             const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
     virtual void postFramebuffer() = 0;
-    virtual void renderCachedSets() = 0;
+    virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
     virtual void chooseCompositionStrategy() = 0;
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f10ff25..f832084 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -93,7 +93,7 @@
     std::optional<base::unique_fd> composeSurfaces(
             const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
     void postFramebuffer() override;
-    void renderCachedSets() override;
+    void renderCachedSets(const CompositionRefreshArgs&) override;
     void cacheClientCompositionRequests(uint32_t) override;
 
     // Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index a4356c5..7cb0f6b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -98,6 +98,7 @@
         mDrawFence = nullptr;
         mBlurLayer = nullptr;
         mHolePunchLayer = nullptr;
+        mSkipCount = 0;
 
         mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
         Region boundingRegion;
@@ -107,6 +108,8 @@
         mVisibleRegion.orSelf(other.mVisibleRegion);
     }
     void incrementAge() { ++mAge; }
+    void incrementSkipCount() { mSkipCount++; }
+    size_t getSkipCount() { return mSkipCount; }
 
     // Renders the cached set with the supplied output composition state.
     void render(renderengine::RenderEngine& re, TexturePool& texturePool,
@@ -155,6 +158,7 @@
     Rect mBounds = Rect::EMPTY_RECT;
     Region mVisibleRegion;
     size_t mAge = 0;
+    size_t mSkipCount = 0;
 
     // TODO(b/190411067): This is a shared pointer only because CachedSets are copied into different
     // containers in the Flattener. Logically this should have unique ownership otherwise.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 94a169e..7534548 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -20,6 +20,7 @@
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
+#include <chrono>
 #include <numeric>
 #include <vector>
 
@@ -37,7 +38,35 @@
 
 class Flattener {
 public:
-    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false);
+    struct CachedSetRenderSchedulingTunables {
+        // This default assumes that rendering a cached set takes about 3ms. That time is then cut
+        // in half - the next frame using the cached set would have the same workload, meaning that
+        // composition cost is the same. This is best illustrated with the following example:
+        //
+        // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
+        // renderCachedSets costs 3ms, then two consecutive frames have timings:
+        //
+        // First frame: Start at 0ms, end at 6.8ms.
+        // renderCachedSets: Start at 6.8ms, end at 9.8ms.
+        // Second frame: Start at 9.8ms, end at 16.6ms.
+        //
+        // Now the second frame won't render a cached set afterwards, but the first frame didn't
+        // really steal time from the second frame.
+        static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us;
+
+        static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
+
+        // Duration allocated for rendering a cached set. If we don't have enough time for rendering
+        // a cached set, then rendering is deferred to another frame.
+        const std::chrono::nanoseconds cachedSetRenderDuration;
+        // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set
+        // too many times, then render it anyways so that future frames would benefit from the
+        // flattened cached set.
+        const size_t maxDeferRenderAttempts;
+    };
+    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false,
+              std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables =
+                      std::nullopt);
 
     void setDisplaySize(ui::Size size) {
         mDisplaySize = size;
@@ -48,16 +77,14 @@
                                 std::chrono::steady_clock::time_point now);
 
     // Renders the newest cached sets with the supplied output composition state
-    void renderCachedSets(const OutputCompositionState& outputState);
+    void renderCachedSets(const OutputCompositionState& outputState,
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
     void dump(std::string& result) const;
     void dumpLayers(std::string& result) const;
 
     const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
 
-protected:
-    std::optional<CachedSet> mNewCachedSet;
-
 private:
     size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
 
@@ -149,9 +176,15 @@
 
     renderengine::RenderEngine& mRenderEngine;
     const bool mEnableHolePunch;
+    const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables;
 
     TexturePool mTexturePool;
 
+protected:
+    // mNewCachedSet must be destroyed before mTexturePool is.
+    std::optional<CachedSet> mNewCachedSet;
+
+private:
     ui::Size mDisplaySize;
 
     NonBufferHash mCurrentGeometry;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index fd1ddfc..be34153 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -58,8 +58,11 @@
     void reportFinalPlan(
             compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
 
-    // The planner will call to the Flattener to render any pending cached set
-    void renderCachedSets(const OutputCompositionState& outputState);
+    // The planner will call to the Flattener to render any pending cached set.
+    // Rendering a pending cached set is optional: if the renderDeadline is not far enough in the
+    // future then the planner may opt to skip rendering the cached set.
+    void renderCachedSets(const OutputCompositionState& outputState,
+                          std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
     void dump(const Vector<String16>& args, std::string&);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 4b4d375..8e777e3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -109,7 +109,7 @@
     MOCK_CONST_METHOD0(getSkipColorTransform, bool());
 
     MOCK_METHOD0(postFramebuffer, void());
-    MOCK_METHOD0(renderCachedSets, void());
+    MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
 
     MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index cd2f742..67bb149 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -434,7 +434,7 @@
     devOptRepaintFlash(refreshArgs);
     finishFrame(refreshArgs);
     postFramebuffer();
-    renderCachedSets();
+    renderCachedSets(refreshArgs);
 }
 
 void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -1312,9 +1312,9 @@
     mReleasedLayers.clear();
 }
 
-void Output::renderCachedSets() {
+void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getState());
+        mPlanner->renderCachedSets(getState(), refreshArgs.nextInvalidateTime);
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 68b6231..acc7ed2 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -277,6 +277,7 @@
         mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
         mOrientation = orientation;
+        mSkipCount = 0;
     } else {
         mTexture.reset();
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 192c411..2bcaf60 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -23,7 +23,7 @@
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
-#include <utils/Trace.h>
+#include <gui/TraceUtils.h>
 
 using time_point = std::chrono::steady_clock::time_point;
 using namespace std::chrono_literals;
@@ -60,9 +60,12 @@
 
 } // namespace
 
-Flattener::Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch)
+Flattener::Flattener(
+        renderengine::RenderEngine& renderEngine, bool enableHolePunch,
+        std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables)
       : mRenderEngine(renderEngine),
         mEnableHolePunch(enableHolePunch),
+        mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables),
         mTexturePool(mRenderEngine) {
     const int timeoutInMs =
             base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0);
@@ -105,12 +108,45 @@
     return hash;
 }
 
-void Flattener::renderCachedSets(const OutputCompositionState& outputState) {
+void Flattener::renderCachedSets(
+        const OutputCompositionState& outputState,
+        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
     ATRACE_CALL();
-    if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) {
+
+    if (!mNewCachedSet) {
         return;
     }
 
+    // Ensure that a cached set has a valid buffer first
+    if (mNewCachedSet->hasRenderedBuffer()) {
+        ATRACE_NAME("mNewCachedSet->hasRenderedBuffer()");
+        return;
+    }
+
+    const auto now = std::chrono::steady_clock::now();
+
+    // If we have a render deadline, and the flattener is configured to skip rendering if we don't
+    // have enough time, then we skip rendering the cached set if we think that we'll steal too much
+    // time from the next frame.
+    if (renderDeadline && mCachedSetRenderSchedulingTunables) {
+        if (const auto estimatedRenderFinish =
+                    now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration;
+            estimatedRenderFinish > *renderDeadline) {
+            mNewCachedSet->incrementSkipCount();
+
+            if (mNewCachedSet->getSkipCount() <=
+                mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) {
+                ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
+                              std::chrono::duration_cast<std::chrono::microseconds>(
+                                      estimatedRenderFinish - *renderDeadline)
+                                      .count());
+                return;
+            } else {
+                ATRACE_NAME("DeadlinePassed: exceeded max skips");
+            }
+        }
+    }
+
     mNewCachedSet->render(mRenderEngine, mTexturePool, outputState);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 711a634..be2510f 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -26,16 +26,43 @@
 #include <compositionengine/impl/planner/Planner.h>
 
 #include <utils/Trace.h>
+#include <chrono>
 
 namespace android::compositionengine::impl::planner {
 
+namespace {
+
+std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() {
+    if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
+        return std::nullopt;
+    }
+
+    auto renderDuration = std::chrono::nanoseconds(
+            base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
+                                            Flattener::CachedSetRenderSchedulingTunables::
+                                                    kDefaultCachedSetRenderDuration.count()));
+
+    auto maxDeferRenderAttempts = base::GetUintProperty<
+            size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
+                    Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts);
+
+    return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>(
+            Flattener::CachedSetRenderSchedulingTunables{
+                    .cachedSetRenderDuration = renderDuration,
+                    .maxDeferRenderAttempts = maxDeferRenderAttempts,
+            });
+}
+
+} // namespace
+
 Planner::Planner(renderengine::RenderEngine& renderEngine)
       // Implicitly, layer caching must also be enabled for the hole punch or
       // predictor to have any effect.
       // E.g., setprop debug.sf.enable_layer_caching 1, or
       // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
       : mFlattener(renderEngine,
-                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true)) {
+                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
+                   buildFlattenerTuneables()) {
     mPredictorEnabled =
             base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
 }
@@ -161,9 +188,11 @@
                             finalPlan);
 }
 
-void Planner::renderCachedSets(const OutputCompositionState& outputState) {
+void Planner::renderCachedSets(
+        const OutputCompositionState& outputState,
+        std::optional<std::chrono::steady_clock::time_point> renderDeadline) {
     ATRACE_CALL();
-    mFlattener.renderCachedSets(outputState);
+    mFlattener.renderCachedSets(outputState, renderDeadline);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c381081..742b155 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1767,7 +1767,7 @@
         MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD0(postFramebuffer, void());
-        MOCK_METHOD0(renderCachedSets, void());
+        MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
     };
 
     StrictMock<OutputPartialMock> mOutput;
@@ -1787,7 +1787,7 @@
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
     EXPECT_CALL(mOutput, finishFrame(Ref(args)));
     EXPECT_CALL(mOutput, postFramebuffer());
-    EXPECT_CALL(mOutput, renderCachedSets());
+    EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
     mOutput.present(args);
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 4a76a95..0acc317 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -223,6 +223,16 @@
     EXPECT_EQ(2u, cachedSet.getAge());
 }
 
+TEST_F(CachedSetTest, incrementSkipCount) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet cachedSet(layer);
+    EXPECT_EQ(0u, cachedSet.getSkipCount());
+    cachedSet.incrementSkipCount();
+    EXPECT_EQ(1u, cachedSet.getSkipCount());
+    cachedSet.incrementSkipCount();
+    EXPECT_EQ(2u, cachedSet.getSkipCount());
+}
+
 TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
     CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
     CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
@@ -257,6 +267,8 @@
     CachedSet cachedSet1(layer1);
     CachedSet cachedSet2(layer2);
     cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+    cachedSet1.incrementSkipCount();
+    EXPECT_EQ(1u, cachedSet1.getSkipCount());
     cachedSet1.append(cachedSet2);
 
     EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
@@ -268,6 +280,8 @@
     EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
     EXPECT_EQ(3u, cachedSet1.getLayerCount());
     EXPECT_EQ(0u, cachedSet1.getAge());
+    EXPECT_EQ(0u, cachedSet1.getSkipCount());
+
     expectNoBuffer(cachedSet1);
     // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
     // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index e176c98..334b855 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -24,6 +24,7 @@
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/LayerSettings.h>
 #include <renderengine/mock/RenderEngine.h>
+#include <chrono>
 
 namespace android::compositionengine {
 using namespace std::chrono_literals;
@@ -46,22 +47,28 @@
 
 class TestableFlattener : public Flattener {
 public:
-    TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch)
-          : Flattener(renderEngine, enableHolePunch) {}
+    TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch,
+                      std::optional<Flattener::CachedSetRenderSchedulingTunables>
+                              cachedSetRenderSchedulingTunables = std::nullopt)
+          : Flattener(renderEngine, enableHolePunch, cachedSetRenderSchedulingTunables) {}
     const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
 };
 
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true)) {}
+    FlattenerTest() : FlattenerTest(std::nullopt) {}
     void SetUp() override;
 
 protected:
+    FlattenerTest(std::optional<Flattener::CachedSetRenderSchedulingTunables>
+                          cachedSetRenderSchedulingTunables)
+          : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true,
+                                                           cachedSetRenderSchedulingTunables)) {}
     void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
     void initializeFlattener(const std::vector<const LayerState*>& layers);
     void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
 
-    // mRenderEngine is held as a reference in mFlattener, so mFlattener must be destroyed first.
+    // mRenderEngine is held as a reference in mFlattener, so explicitly destroy mFlattener first.
     renderengine::mock::RenderEngine mRenderEngine;
     std::unique_ptr<TestableFlattener> mFlattener;
 
@@ -148,13 +155,13 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 }
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -164,7 +171,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -174,7 +181,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -209,7 +216,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 }
 
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -255,7 +262,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -360,7 +367,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -397,7 +404,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -406,7 +413,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -419,7 +426,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -428,7 +435,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -470,7 +477,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -484,7 +491,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -497,7 +504,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -512,7 +519,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -524,7 +531,7 @@
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
     mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -563,7 +570,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -576,7 +583,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -625,7 +632,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We've rendered a CachedSet, but we haven't merged it in.
     EXPECT_EQ(nullptr, overrideBuffer1);
@@ -638,7 +645,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -682,7 +689,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -692,7 +699,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
     EXPECT_EQ(nullptr, overrideBuffer3);
@@ -726,7 +733,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -737,7 +744,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
     }
@@ -778,7 +785,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -788,7 +795,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
     EXPECT_NE(nullptr, overrideBuffer3);
@@ -825,7 +832,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
     ASSERT_NE(std::nullopt, cachedSet);
@@ -839,7 +846,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
     EXPECT_EQ(nullptr, blurOverrideBuffer);
@@ -866,7 +873,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -874,16 +881,61 @@
     // Simulate attempting to render prior to merging the new cached set with the layer stack.
     // Here we should not try to re-render.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     // We provide the override buffer now that it's rendered
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mOutputState);
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer2, overrideBuffer1);
 }
 
+const constexpr std::chrono::nanoseconds kCachedSetRenderDuration = 0ms;
+const constexpr size_t kMaxDeferRenderAttempts = 2;
+
+class FlattenerRenderSchedulingTest : public FlattenerTest {
+public:
+    FlattenerRenderSchedulingTest()
+          : FlattenerTest(
+                    Flattener::CachedSetRenderSchedulingTunables{.cachedSetRenderDuration =
+                                                                         kCachedSetRenderDuration,
+                                                                 .maxDeferRenderAttempts =
+                                                                         kMaxDeferRenderAttempts}) {
+    }
+};
+
+TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToMaxAttempts) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // Mark the layers inactive
+    mTime += 200ms;
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) {
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+        mFlattener->renderCachedSets(mOutputState,
+                                     std::chrono::steady_clock::now() -
+                                             (kCachedSetRenderDuration + 10ms));
+    }
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mOutputState,
+                                 std::chrono::steady_clock::now() -
+                                         (kCachedSetRenderDuration + 10ms));
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3523b56..c294ff2 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -693,6 +693,7 @@
         actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
         actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
         actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+        actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer);
     });
 
     // Actual timeline end
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e50087f..4beb526 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -847,11 +847,13 @@
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
-    return mTransactionFlags.fetch_and(~flags) & flags;
+    auto ret = mTransactionFlags & flags;
+    mTransactionFlags &= ~flags;
+    return ret;
 }
 
 uint32_t Layer::setTransactionFlags(uint32_t flags) {
-    return mTransactionFlags.fetch_or(flags);
+    return mTransactionFlags |= flags;
 }
 
 bool Layer::setPosition(float x, float y) {
@@ -2018,7 +2020,7 @@
 
 const std::vector<BlurRegion> Layer::getBlurRegions() const {
     auto regionsCopy(getDrawingState().blurRegions);
-    int layerAlpha = getAlpha();
+    float layerAlpha = getAlpha();
     for (auto& region : regionsCopy) {
         region.alpha = region.alpha * layerAlpha;
     }
@@ -2382,6 +2384,16 @@
     info.touchableRegion = inputTransform.transform(info.touchableRegion);
 }
 
+void Layer::fillTouchOcclusionMode(InputWindowInfo& info) {
+    sp<Layer> p = this;
+    while (p != nullptr && !p->hasInputInfo()) {
+        p = p->mDrawingParent.promote();
+    }
+    if (p != nullptr) {
+        info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
+    }
+}
+
 InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
     if (!hasInputInfo()) {
         mDrawingState.inputInfo.name = getName();
@@ -2419,6 +2431,7 @@
     // anything.
     info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
     info.alpha = getAlpha();
+    fillTouchOcclusionMode(info);
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dde0031..5669049 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -960,7 +960,7 @@
 
     // these are protected by an external lock (mStateLock)
     State mCurrentState;
-    std::atomic<uint32_t> mTransactionFlags{0};
+    uint32_t mTransactionFlags{0};
 
     // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
@@ -1057,6 +1057,10 @@
     // null.
     sp<Layer> getRootLayer();
 
+    // Fills in the touch occlusion mode of the first parent (including this layer) that
+    // hasInputInfo() or no-op if no such parent is found.
+    void fillTouchOcclusionMode(InputWindowInfo& info);
+
     // Fills in the frame and transform info for the InputWindowInfo
     void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b062acd..9746076 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -190,6 +190,45 @@
 RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                                    const GlobalSignals& globalSignals,
                                                    GlobalSignals* outSignalsConsidered) const {
+    std::lock_guard lock(mLock);
+
+    if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
+        return *cached;
+    }
+
+    GlobalSignals signalsConsidered;
+    RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
+    lastBestRefreshRateInvocation.emplace(
+            GetBestRefreshRateInvocation{.layerRequirements = layers,
+                                         .globalSignals = globalSignals,
+                                         .outSignalsConsidered = signalsConsidered,
+                                         .resultingBestRefreshRate = result});
+    if (outSignalsConsidered) {
+        *outSignalsConsidered = signalsConsidered;
+    }
+    return result;
+}
+
+std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
+    const bool sameAsLastCall = lastBestRefreshRateInvocation &&
+            lastBestRefreshRateInvocation->layerRequirements == layers &&
+            lastBestRefreshRateInvocation->globalSignals == globalSignals;
+
+    if (sameAsLastCall) {
+        if (outSignalsConsidered) {
+            *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
+        }
+        return lastBestRefreshRateInvocation->resultingBestRefreshRate;
+    }
+
+    return {};
+}
+
+RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
     ATRACE_CALL();
     ALOGV("getBestRefreshRate %zu layers", layers.size());
 
@@ -206,8 +245,6 @@
         }
     };
 
-    std::lock_guard lock(mLock);
-
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -592,6 +629,11 @@
 
 void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
+
+    // Invalidate the cached invocation to getBestRefreshRate. This forces
+    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+    lastBestRefreshRateInvocation.reset();
+
     mCurrentRefreshRate = mRefreshRates.at(modeId).get();
 }
 
@@ -605,11 +647,16 @@
 void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
                                             DisplayModeId currentModeId) {
     std::lock_guard lock(mLock);
+
     // The current mode should be supported
     LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
         return mode->getId() == currentModeId;
     }));
 
+    // Invalidate the cached invocation to getBestRefreshRate. This forces
+    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+    lastBestRefreshRateInvocation.reset();
+
     mRefreshRates.clear();
     for (const auto& mode : modes) {
         const auto modeId = mode->getId();
@@ -666,6 +713,7 @@
         ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
+    lastBestRefreshRateInvocation.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mDisplayManagerPolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -680,6 +728,7 @@
     if (policy && !isPolicyValidLocked(*policy)) {
         return BAD_VALUE;
     }
+    lastBestRefreshRateInvocation.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mOverridePolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ee89149..342fde0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -250,6 +250,10 @@
         bool touch = false;
         // True if the system hasn't seen any buffers posted to layers recently.
         bool idle = false;
+
+        bool operator==(const GlobalSignals& other) const {
+            return touch == other.touch && idle == other.idle;
+        }
     };
 
     // Returns the refresh rate that fits best to the given layers.
@@ -350,6 +354,15 @@
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
             std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
 
+    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                                        const GlobalSignals& globalSignals,
+                                                        GlobalSignals* outSignalsConsidered) const
+            REQUIRES(mLock);
+
+    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
+                                         const GlobalSignals& globalSignals,
+                                         GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
+
     // Returns the refresh rate with the highest score in the collection specified from begin
     // to end. If there are more than one with the same highest refresh rate, the first one is
     // returned.
@@ -414,6 +427,15 @@
 
     const bool mEnableFrameRateOverride;
     bool mSupportsFrameRateOverride;
+
+    struct GetBestRefreshRateInvocation {
+        std::vector<LayerRequirement> layerRequirements;
+        GlobalSignals globalSignals;
+        GlobalSignals outSignalsConsidered;
+        RefreshRate resultingBestRefreshRate;
+    };
+    mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
+            GUARDED_BY(mLock);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 80f4665..208a767 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -61,6 +61,7 @@
         if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
             return;
         }
+        mTimeStats.incrementRefreshRateSwitches();
         flushTime();
         mCurrentRefreshRate = currRefreshRate;
     }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fac2c65..4b8cbfb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -194,8 +194,6 @@
 
 std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
         const scheduler::RefreshRateConfigs& configs) {
-    if (!configs.canSwitch()) return nullptr;
-
     return std::make_unique<scheduler::LayerHistory>(configs);
 }
 
@@ -579,8 +577,6 @@
 }
 
 void Scheduler::registerLayer(Layer* layer) {
-    if (!mLayerHistory) return;
-
     scheduler::LayerHistory::LayerVoteType voteType;
 
     if (!mOptions.useContentDetection ||
@@ -600,26 +596,22 @@
 }
 
 void Scheduler::deregisterLayer(Layer* layer) {
-    if (mLayerHistory) {
-        mLayerHistory->deregisterLayer(layer);
-    }
+    mLayerHistory->deregisterLayer(layer);
 }
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    if (mLayerHistory) {
+    if (mRefreshRateConfigs.canSwitch()) {
         mLayerHistory->record(layer, presentTime, systemTime(), updateType);
     }
 }
 
 void Scheduler::setModeChangePending(bool pending) {
-    if (mLayerHistory) {
-        mLayerHistory->setModeChangePending(pending);
-    }
+    mLayerHistory->setModeChangePending(pending);
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    if (!mLayerHistory) return;
+    if (!mRefreshRateConfigs.canSwitch()) return;
 
     ATRACE_CALL();
 
@@ -630,9 +622,6 @@
     bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mFeatures.contentRequirements == summary) {
-            return;
-        }
         mFeatures.contentRequirements = summary;
 
         newModeId = calculateRefreshRateModeId(&consideredSignals);
@@ -691,9 +680,7 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    if (mLayerHistory) {
-        mLayerHistory->clear();
-    }
+    mLayerHistory->clear();
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -732,9 +719,7 @@
     // NOTE: Instead of checking all the layers, we should be checking the layer
     // that is currently on top. b/142507166 will give us this capability.
     if (handleTimerStateChanged(&mFeatures.touch, touch)) {
-        if (mLayerHistory) {
-            mLayerHistory->clear();
-        }
+        mLayerHistory->clear();
     }
     ATRACE_INT("TouchState", static_cast<int>(touch));
 }
@@ -908,9 +893,7 @@
 }
 
 void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
-    if (mLayerHistory) {
-        mLayerHistory->setDisplayArea(displayArea);
-    }
+    mLayerHistory->setDisplayArea(displayArea);
 }
 
 void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 49d3d93..30a3253 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -268,7 +268,7 @@
     VsyncSchedule mVsyncSchedule;
 
     // Used to choose refresh rate if content detection is enabled.
-    const std::unique_ptr<LayerHistory> mLayerHistory;
+    std::unique_ptr<LayerHistory> mLayerHistory;
 
     // Timer that records time between requests for next vsync.
     std::optional<scheduler::OneShotTimer> mIdleTimer;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 028f7a6..e9bd92a 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -69,7 +69,21 @@
 
     auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
     auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
-    return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
+    if (percent >= kOutlierTolerancePercent &&
+        percent <= (kMaxPercent - kOutlierTolerancePercent)) {
+        return false;
+    }
+
+    const auto iter = std::min_element(mTimestamps.begin(), mTimestamps.end(),
+                                       [timestamp](nsecs_t a, nsecs_t b) {
+                                           return std::abs(timestamp - a) < std::abs(timestamp - b);
+                                       });
+    const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod;
+    if (distancePercent < kOutlierTolerancePercent) {
+        // duplicate timestamp
+        return false;
+    }
+    return true;
 }
 
 nsecs_t VSyncPredictor::currentPeriod() const {
@@ -86,6 +100,9 @@
         // in the learning phase we should just clear all timestamps and start
         // over.
         if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+            // Add the timestamp to mTimestamps before clearing it so we could
+            // update mKnownTimestamp based on the new timestamp.
+            mTimestamps.push_back(timestamp);
             clearTimestamps();
         } else if (!mTimestamps.empty()) {
             mKnownTimestamp =
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a4b6fef..4fcec16 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -63,6 +63,7 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <private/gui/SyncFeatures.h>
+#include <processgroup/processgroup.h>
 #include <renderengine/RenderEngine.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
@@ -680,6 +681,10 @@
     if (mStartPropertySetThread->join() != NO_ERROR) {
         ALOGE("Join StartPropertySetThread failed!");
     }
+
+    if (mRenderEnginePrimeCacheFuture.valid()) {
+        mRenderEnginePrimeCacheFuture.get();
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -781,6 +786,12 @@
                                     ? renderengine::RenderEngine::ContextPriority::REALTIME
                                     : renderengine::RenderEngine::ContextPriority::MEDIUM)
                     .build()));
+
+    // Set SF main policy after initializing RenderEngine which has its own policy.
+    if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
+        ALOGW("Failed to set main task profile");
+    }
+
     mCompositionEngine->setTimeStats(mTimeStats);
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
     mCompositionEngine->getHwComposer().setCallback(this);
@@ -807,7 +818,15 @@
     char primeShaderCache[PROPERTY_VALUE_MAX];
     property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
     if (atoi(primeShaderCache)) {
-        getRenderEngine().primeCache();
+        if (setSchedFifo(false) != NO_ERROR) {
+            ALOGW("Can't set SCHED_OTHER for primeCache");
+        }
+
+        mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache();
+
+        if (setSchedFifo(true) != NO_ERROR) {
+            ALOGW("Can't set SCHED_OTHER for primeCache");
+        }
     }
 
     getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
@@ -1139,7 +1158,18 @@
         // have been already updated with the upcoming active mode.
         return;
     }
-    const Fps oldRefreshRate = display->getActiveMode()->getFps();
+
+    if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+        auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+        // We need to generate new sequenceId in order to recreate the display (and this
+        // way the framebuffer).
+        state.sequenceId = DisplayDeviceState{}.sequenceId;
+        state.physical->activeMode = upcomingMode;
+        processDisplayChangesLocked();
+
+        // processDisplayChangesLocked will update all necessary components so we're done here.
+        return;
+    }
 
     std::lock_guard<std::mutex> lock(mActiveModeLock);
     mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
@@ -1149,9 +1179,6 @@
 
     mRefreshRateStats->setRefreshRate(refreshRate);
 
-    if (!refreshRate.equalsWithMargin(oldRefreshRate)) {
-        mTimeStats->incrementRefreshRateSwitches();
-    }
     updatePhaseConfiguration(refreshRate);
     ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
 
@@ -2040,6 +2067,7 @@
     }
 
     refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+    refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
 
     mGeometryInvalid = false;
 
@@ -2225,14 +2253,14 @@
         compositorTiming = getBE().mCompositorTiming;
     }
 
-    mDrawingState.traverse([&](Layer* layer) {
+    for (const auto& layer: mLayersWithQueuedFrames) {
         const bool frameLatched =
                 layer->onPostComposition(display, glCompositionDoneFenceTime,
                                          mPreviousPresentFences[0].fenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
-    });
+    }
 
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
             hdrInfoListeners;
@@ -2808,7 +2836,10 @@
                 mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
                                                         currentState.physical->activeMode->getId());
                 mVsyncConfiguration->reset();
-                updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
+                const Fps refreshRate = currentState.physical->activeMode->getFps();
+                updatePhaseConfiguration(refreshRate);
+                mRefreshRateStats->setRefreshRate(refreshRate);
+
                 if (mRefreshRateOverlay) {
                     mRefreshRateOverlay->reset();
                 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 519a5ab..a3fa8d6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1183,6 +1183,8 @@
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
+    std::future<void> mRenderEnginePrimeCacheFuture;
+
     // access must be protected by mStateLock
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index f1b153f..7c1f21f 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -846,8 +846,8 @@
         timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
         timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
         timelineStats.stats[layerKey].uid = info.uid;
-        timelineStats.stats[layerKey].layerName = kDefaultGameMode;
-        timelineStats.stats[layerKey].gameMode = info.gameMode;
+        timelineStats.stats[layerKey].layerName = kDefaultLayerName;
+        timelineStats.stats[layerKey].gameMode = kDefaultGameMode;
     }
 
     TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index dd48950..9e70684 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -16,18 +16,10 @@
 
 #pragma once
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
 #include <cstdint>
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <../Fps.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <gui/JankInfo.h>
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 71a567a..0a8c748 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -877,7 +877,7 @@
 ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
         int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
         ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
-        ProtoJankType jankType, ProtoPredictionType predictionType) {
+        ProtoJankType jankType, ProtoPredictionType predictionType, bool isBuffer) {
     ProtoActualSurfaceFrameStart proto;
     proto.set_cookie(cookie);
     proto.set_token(token);
@@ -889,6 +889,7 @@
     proto.set_gpu_composition(gpuComposition);
     proto.set_jank_type(jankType);
     proto.set_prediction_type(predictionType);
+    proto.set_is_buffer(isBuffer);
     return proto;
 }
 
@@ -978,6 +979,8 @@
     EXPECT_EQ(received.jank_type(), source.jank_type());
     ASSERT_TRUE(received.has_prediction_type());
     EXPECT_EQ(received.prediction_type(), source.prediction_type());
+    ASSERT_TRUE(received.has_is_buffer());
+    EXPECT_EQ(received.is_buffer(), source.is_buffer());
 }
 
 void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
@@ -1154,7 +1157,7 @@
                                                displayFrameToken1, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_DROPPED, false, false,
                                                FrameTimelineEvent::JANK_NONE,
-                                               FrameTimelineEvent::PREDICTION_VALID);
+                                               FrameTimelineEvent::PREDICTION_VALID, true);
     auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
 
     auto protoPresentedSurfaceFrameExpectedStart =
@@ -1166,7 +1169,7 @@
                                                displayFrameToken1, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_ON_TIME, true, false,
                                                FrameTimelineEvent::JANK_NONE,
-                                               FrameTimelineEvent::PREDICTION_VALID);
+                                               FrameTimelineEvent::PREDICTION_VALID, true);
     auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
 
     // Set up the display frame
@@ -1309,7 +1312,7 @@
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
                                                false, FrameTimelineEvent::JANK_UNKNOWN,
-                                               FrameTimelineEvent::PREDICTION_EXPIRED);
+                                               FrameTimelineEvent::PREDICTION_EXPIRED, true);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
     // Set up the display frame
@@ -1383,7 +1386,7 @@
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_DROPPED, false, false,
                                                FrameTimelineEvent::JANK_NONE,
-                                               FrameTimelineEvent::PREDICTION_EXPIRED);
+                                               FrameTimelineEvent::PREDICTION_EXPIRED, true);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
     // Set up the display frame
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7ace70a..d04a7d7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -45,9 +45,16 @@
 
 class RefreshRateConfigsTest : public testing::Test {
 protected:
+    using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
+
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
+    RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+        return {displayMode->getId(), displayMode, displayMode->getFps(),
+                RefreshRate::ConstructorTag(0)};
+    }
+
     Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
         return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
     }
@@ -71,6 +78,19 @@
         return *refreshRateConfigs.mMaxSupportedRefreshRate;
     }
 
+    void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
+                                          const GetBestRefreshRateInvocation& invocation) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
+                GetBestRefreshRateInvocation(invocation));
+    }
+
+    std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
+            const RefreshRateConfigs& refreshRateConfigs) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        return refreshRateConfigs.lastBestRefreshRateInvocation;
+    }
+
     // Test config IDs
     static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
     static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
@@ -1752,6 +1772,78 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
+    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    setLastBestRefreshRateInvocation(*refreshRateConfigs,
+                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+                                                                          LayerRequirement>(),
+                                                                  .globalSignals = {.touch = true,
+                                                                                    .idle = true},
+                                                                  .outSignalsConsidered =
+                                                                          {.touch = true,
+                                                                           .idle = false},
+                                                                  .resultingBestRefreshRate =
+                                                                          createRefreshRate(
+                                                                                  mConfig90)});
+
+    EXPECT_EQ(createRefreshRate(mConfig90),
+              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+                                                     {.touch = true, .idle = true}));
+
+    const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+    setLastBestRefreshRateInvocation(*refreshRateConfigs,
+                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+                                                                          LayerRequirement>(),
+                                                                  .globalSignals = {.touch = true,
+                                                                                    .idle = true},
+                                                                  .outSignalsConsidered =
+                                                                          cachedSignalsConsidered,
+                                                                  .resultingBestRefreshRate =
+                                                                          createRefreshRate(
+                                                                                  mConfig30)});
+
+    GlobalSignals signalsConsidered;
+    EXPECT_EQ(createRefreshRate(mConfig30),
+              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+                                                     {.touch = true, .idle = true},
+                                                     &signalsConsidered));
+
+    EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
+    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
+
+    GlobalSignals globalSignals{.touch = true, .idle = true};
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 0.5f}};
+    const auto lastResult =
+            refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
+                                                   /* outSignalsConsidered */ nullptr);
+
+    const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);
+
+    ASSERT_TRUE(lastInvocation.has_value());
+    ASSERT_EQ(layers, lastInvocation->layerRequirements);
+    ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
+    ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);
+
+    // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
+    // to getBestRefreshRate()
+    GlobalSignals detaultSignals;
+    ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
+}
+
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
     EXPECT_TRUE(mExpected60Config < mExpected90Config);
     EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 38e503f..423d0cc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -51,9 +51,18 @@
 
     SchedulerTest();
 
-    const scheduler::RefreshRateConfigs
-            mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
-                     DisplayModeId(0)};
+    const DisplayModePtr mode60 = DisplayMode::Builder(0)
+                                          .setId(DisplayModeId(0))
+                                          .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+                                          .setGroup(0)
+                                          .build();
+    const DisplayModePtr mode120 = DisplayMode::Builder(1)
+                                           .setId(DisplayModeId(1))
+                                           .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+                                           .setGroup(0)
+                                           .build();
+
+    scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()};
 
     mock::SchedulerCallback mSchedulerCallback;
 
@@ -149,15 +158,14 @@
     EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
-TEST_F(SchedulerTest, noLayerHistory) {
-    // Layer history should not be created if there is a single config.
-    ASSERT_FALSE(mScheduler->hasLayerHistory());
-
+TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
+    // The layer is registered at creation time and deregistered at destruction time.
     sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
 
-    // Content detection should be no-op.
-    mScheduler->registerLayer(layer.get());
+    // recordLayerHistory should be a noop
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
 
     constexpr bool kPowerStateNormal = true;
     mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -169,6 +177,18 @@
     mScheduler->chooseRefreshRateForContent();
 }
 
+TEST_F(SchedulerTest, updateDisplayModes) {
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
+    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+    ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+
+    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+}
+
 TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
     // If the optional fields are cleared, the function should return before
     // onModeChange is called.
@@ -200,4 +220,25 @@
     EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
 }
 
+MATCHER(Is120Hz, "") {
+    return arg.getFps().equalsWithMargin(Fps(120.f));
+}
+
+TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
+    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+
+    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+
+    constexpr bool kPowerStateNormal = true;
+    mScheduler->setDisplayPowerState(kPowerStateNormal);
+
+    constexpr uint32_t kDisplayArea = 999'999;
+    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+
+    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
+    mScheduler->chooseRefreshRateForContent();
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f9dd01..41fd6e3 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -64,6 +64,11 @@
         return mutableLayerHistory()->mLayerInfos.size();
     }
 
+    size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
+        if (!mLayerHistory) return 0;
+        return mutableLayerHistory()->mActiveLayersEnd;
+    }
+
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
             mTouchTimer.reset();
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 2a658dd..37ecd7c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -478,7 +478,7 @@
     }
 }
 
-TEST_F(VSyncPredictorTest, InconsistentVsyncValueIsFlushedEventually) {
+TEST_F(VSyncPredictorTest, inconsistentVsyncValueIsFlushedEventually) {
     EXPECT_TRUE(tracker.addVsyncTimestamp(600));
     EXPECT_TRUE(tracker.needsMoreSamples());
 
@@ -492,6 +492,46 @@
     EXPECT_FALSE(tracker.needsMoreSamples());
 }
 
+TEST_F(VSyncPredictorTest, knownVsyncIsUpdated) {
+    EXPECT_TRUE(tracker.addVsyncTimestamp(600));
+    EXPECT_TRUE(tracker.needsMoreSamples());
+    EXPECT_EQ(600, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+
+    EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += mPeriod));
+    EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.needsMoreSamples());
+        EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+        EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+    }
+
+    EXPECT_FALSE(tracker.needsMoreSamples());
+    EXPECT_EQ(mNow + 1000, tracker.nextAnticipatedVSyncTimeFrom(mNow));
+}
+
+TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) {
+    // these are real vsync timestamps from b/190331974 which caused vsync predictor
+    // period to spike to 18ms due to very close timestamps
+    std::vector<nsecs_t> const simulatedVsyncs{
+            198353408177, 198370074844, 198371400000, 198374274000, 198390941000, 198407565000,
+            198540887994, 198607538588, 198624218276, 198657655939, 198674224176, 198690880955,
+            198724204319, 198740988133, 198758166681, 198790869196, 198824205052, 198840871678,
+            198857715631, 198890885797, 198924199640, 198940873834, 198974204401,
+    };
+    auto constexpr idealPeriod = 16'666'666;
+    auto constexpr expectedPeriod = 16'644'742;
+    auto constexpr expectedIntercept = 125'626;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 1010aa5..f15a963 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -95,7 +95,10 @@
 }
 
 HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
-    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+        status.transactionError() == android::UNKNOWN_TRANSACTION) {
+        // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+        // the same as the operation being unsupported by this HAL. Should not retry.
         return HalResult<void>::unsupported();
     }
     if (status.isOk()) {
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 8720d9d..87bc34e 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -42,7 +42,10 @@
     static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
 
     static HalResult<T> fromStatus(binder::Status status, T data) {
-        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+            status.transactionError() == android::UNKNOWN_TRANSACTION) {
+            // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
+            // the same as the operation being unsupported by this HAL. Should not retry.
             return HalResult<T>::unsupported();
         }
         if (status.isOk()) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index af0cdb8..7813303 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -189,8 +189,7 @@
                 .WillRepeatedly(vibrator::TriggerSchedulerCallback());
         EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+                .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
         EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -228,8 +227,7 @@
         EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.1f))).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.2f)))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+                .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
         EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(0.5f)))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -265,8 +263,7 @@
         EXPECT_CALL(*mMockHal.get(),
                     alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+                .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
         EXPECT_CALL(*mMockHal.get(),
                     alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
                 .Times(Exactly(1))
@@ -397,8 +394,7 @@
                     Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
     EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
             .Times(Exactly(1))
-            .WillRepeatedly(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+            .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
     EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
@@ -411,8 +407,7 @@
                     Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
     EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_))
             .Times(Exactly(1))
-            .WillRepeatedly(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+            .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
     EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
             .Times(Exactly(1))
             .WillRepeatedly(
@@ -451,8 +446,7 @@
                         DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+                .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
         EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -549,8 +543,7 @@
                 .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+                .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 548d028..1593cb1 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -308,8 +308,7 @@
                                       Return(Status())));
         EXPECT_CALL(*mMockHal.get(), triggerSynced(_))
                 .Times(Exactly(3))
-                .WillOnce(Return(
-                        Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+                .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
                 .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
                 .WillRepeatedly(DoAll(TriggerCallback(), Return(Status())));
     }
@@ -345,8 +344,7 @@
 TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSynced) {
     EXPECT_CALL(*mMockHal.get(), cancelSynced())
             .Times(Exactly(3))
-            .WillOnce(
-                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
             .WillRepeatedly(Return(Status()));