Merge "Add TransactionCompleteCallback"
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 6459805..72c03bf 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -68,15 +68,15 @@
                                        apex::kApexPackageVendorDir};
     for (const auto& dir : apex_dirs) {
         // Cast call to void to suppress warn_unused_result.
-        static_cast<void>(apex::scanPackagesDirAndActivate(dir));
+        static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
     }
-    return apex::getActivePackages();
+    return apex::GetActivePackages();
 }
 
 static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
     for (const apex::ApexFile& apex_file : active_packages) {
         const std::string& package_path = apex_file.GetPath();
-        base::Result<void> status = apex::deactivatePackage(package_path);
+        base::Result<void> status = apex::DeactivatePackage(package_path);
         if (!status.ok()) {
             LOG(ERROR) << "Failed to deactivate " << package_path << ": "
                        << status.error();
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 7aac7da..b21010d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -494,7 +494,7 @@
     sp<BpBinder> bpBinder = binder->remoteBinder();
     if (bpBinder == nullptr) return -1;
 
-    return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+    return ProcessState::self()->getStrongRefCountForNode(bpBinder);
 }
 
 void ServiceManager::handleClientCallbacks() {
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 4aa2f60..639df7a 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -37,7 +37,6 @@
     PLAYER_STATE_STARTED  = 2,
     PLAYER_STATE_PAUSED   = 3,
     PLAYER_STATE_STOPPED  = 4,
-    PLAYER_UPDATE_DEVICE_ID = 5,
 } player_state_t;
 
 // must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 7d1f38f..2f5ccb8 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -49,8 +49,7 @@
                 audio_content_type_t content, const sp<IBinder>& player) = 0;
     /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
                 audio_content_type_t content)= 0;
-    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
-                audio_port_handle_t deviceId) = 0;
+    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
     /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
     virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
     /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 91e465f..87eab52 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -206,7 +206,7 @@
             if (proxy == nullptr) {
                 ALOGE("null proxy");
             }
-            const int32_t handle = proxy ? proxy->handle() : 0;
+            const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
             obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
             obj.handle = handle;
@@ -2509,19 +2509,15 @@
 void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
     const binder_size_t* objects, size_t objectsCount, release_func relFunc)
 {
-    binder_size_t minOffset = 0;
-    freeDataNoInit();
-    mError = NO_ERROR;
+    freeData();
+
     mData = const_cast<uint8_t*>(data);
     mDataSize = mDataCapacity = dataSize;
-    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
-    mDataPos = 0;
-    ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
     mObjects = const_cast<binder_size_t*>(objects);
     mObjectsSize = mObjectsCapacity = objectsCount;
-    mNextObjectHint = 0;
-    mObjectsSorted = false;
     mOwner = relFunc;
+
+    binder_size_t minOffset = 0;
     for (size_t i = 0; i < mObjectsSize; i++) {
         binder_size_t offset = mObjects[i];
         if (offset < minOffset) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 9aedf28..6f26450 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -204,11 +204,11 @@
 // that the handle points to. Can only be used by the servicemanager.
 //
 // Returns -1 in case of failure, otherwise the strong reference count.
-ssize_t ProcessState::getStrongRefCountForNodeByHandle(int32_t handle) {
+ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
     binder_node_info_for_ref info;
     memset(&info, 0, sizeof(binder_node_info_for_ref));
 
-    info.handle = handle;
+    info.handle = binder->getPrivateAccessorForHandle().handle();
 
     status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 2735315..22300ac 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
 namespace internal {
 class Stability;
 }
+class ProcessState;
 
 using binder_proxy_limit_callback = void(*)(int);
 
@@ -37,8 +38,6 @@
 public:
     static BpBinder*    create(int32_t handle);
 
-    int32_t             handle() const;
-
     virtual const String16&    getInterfaceDescriptor() const;
     virtual bool        isBinderAlive() const;
     virtual status_t    pingBinder();
@@ -109,7 +108,23 @@
         KeyedVector<const void*, entry_t> mObjects;
     };
 
+    class PrivateAccessorForHandle {
+    private:
+        friend BpBinder;
+        friend ::android::Parcel;
+        friend ::android::ProcessState;
+        explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
+        int32_t handle() const { return mBinder->handle(); }
+        const BpBinder* mBinder;
+    };
+    const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
+        return PrivateAccessorForHandle(this);
+    }
+
 private:
+    friend PrivateAccessorForHandle;
+
+    int32_t             handle() const;
                         BpBinder(int32_t handle,int32_t trackedUid);
     virtual             ~BpBinder();
     virtual void        onFirstRef();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9f5260a..b49951b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -504,9 +504,6 @@
                                             const binder_size_t* objects, size_t objectsCount,
                                             release_func relFunc);
 
-                        Parcel(const Parcel& o);
-    Parcel&             operator=(const Parcel& o);
-    
     status_t            finishWrite(size_t len);
     void                releaseObjects();
     void                acquireObjects();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 46457cd..bab6469 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -70,7 +70,7 @@
                                 // 2. Temporary strong references held by the kernel during a
                                 //    transaction on the node.
                                 // It does NOT include local strong references to the node
-            ssize_t             getStrongRefCountForNodeByHandle(int32_t handle);
+            ssize_t             getStrongRefCountForNode(const sp<BpBinder>& binder);
 
             enum class CallRestriction {
                 // all calls okay
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index c44a24b..53871f2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -27,19 +27,12 @@
 #pragma once
 
 #include <android/binder_ibinder.h>
+#include <android/binder_internal_logging.h>
 #include <android/binder_parcel.h>
 #include <android/binder_status.h>
 
 #include <assert.h>
 
-// defined differently by liblog
-#pragma push_macro("LOG_PRI")
-#ifdef LOG_PRI
-#undef LOG_PRI
-#endif
-#include <syslog.h>
-#pragma pop_macro("LOG_PRI")
-
 #include <unistd.h>
 #include <cstddef>
 #include <string>
diff --git a/libs/binder/ndk/include_cpp/android/binder_internal_logging.h b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
new file mode 100644
index 0000000..88c6443
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_internal_logging.h
+ * @brief This provides the ability to use syslog from binder headers, since
+ * other logging functionality might be inaccessable.
+ */
+
+#pragma once
+
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
+/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 054aebe..83190aa 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,6 +27,7 @@
 #pragma once
 
 #include <android/binder_auto_utils.h>
+#include <android/binder_internal_logging.h>
 #include <android/binder_parcel.h>
 
 #include <optional>
@@ -179,6 +180,7 @@
 static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel,
                                                                 const SpAIBinder& binder) {
     if (binder.get() == nullptr) {
+        syslog(LOG_ERR, "Passing null binder object as non-@nullable AIDL IBinder");
         return STATUS_UNEXPECTED_NULL;
     }
     return AParcel_writeStrongBinder(parcel, binder.get());
@@ -228,6 +230,7 @@
 static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor(
         AParcel* parcel, const ScopedFileDescriptor& fd) {
     if (fd.get() < 0) {
+        syslog(LOG_ERR, "Passing -1 file descriptor as non-@nullable AIDL ParcelFileDescriptor");
         return STATUS_UNEXPECTED_NULL;
     }
     return AParcel_writeParcelFileDescriptor(parcel, fd.get());
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
index c685b41..6ca0e2f 100644
--- a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -48,9 +48,7 @@
 static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
                                             const sp<IBinder::DeathRecipient>&)>>
         gBPBinderOperations =
-                {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
-                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
-                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                {[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
                     const sp<IBinder::DeathRecipient>& s_recipient) -> void {
                      // Clean up possible leftover memory.
                      wp<IBinder::DeathRecipient> outRecipient(nullptr);
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e17f319..3415d9d 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -326,7 +326,9 @@
     t->setCrop(mSurfaceControl, computeCrop(bufferItem));
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
-    t->setDesiredPresentTime(bufferItem.mTimestamp);
+    if (!bufferItem.mIsAutoTimestamp) {
+        t->setDesiredPresentTime(bufferItem.mTimestamp);
+    }
     t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
 
     if (!mNextFrameTimelineVsyncIdQueue.empty()) {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 405658b..a8d6832 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -71,7 +71,7 @@
     virtual status_t setTransactionState(
             int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            const InputWindowCommands& commands, int64_t desiredPresentTime,
+            const InputWindowCommands& commands, int64_t desiredPresentTime, bool isAutoTimestamp,
             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
             const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
         Parcel data, reply;
@@ -92,6 +92,7 @@
         SAFE_PARCEL(data.writeStrongBinder, applyToken);
         SAFE_PARCEL(commands.write, data);
         SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+        SAFE_PARCEL(data.writeBool, isAutoTimestamp);
         SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
         SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
         SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
@@ -1297,7 +1298,9 @@
             SAFE_PARCEL(inputWindowCommands.read, data);
 
             int64_t desiredPresentTime = 0;
+            bool isAutoTimestamp = true;
             SAFE_PARCEL(data.readInt64, &desiredPresentTime);
+            SAFE_PARCEL(data.readBool, &isAutoTimestamp);
 
             client_cache_t uncachedBuffer;
             sp<IBinder> tmpBinder;
@@ -1323,8 +1326,8 @@
 
             return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
                                        applyToken, inputWindowCommands, desiredPresentTime,
-                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
-                                       transactionId);
+                                       isAutoTimestamp, uncachedBuffer, hasListenerCallbacks,
+                                       listenerCallbacks, transactionId);
         }
         case BOOT_FINISHED: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2dacae1..1808571 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -99,15 +99,13 @@
 
 status_t JankData::writeToParcel(Parcel* output) const {
     SAFE_PARCEL(output->writeInt64, frameVsyncId);
-    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankType));
+    SAFE_PARCEL(output->writeInt32, jankType);
     return NO_ERROR;
 }
 
 status_t JankData::readFromParcel(const Parcel* input) {
     SAFE_PARCEL(input->readInt64, &frameVsyncId);
-    int32_t jankTypeInt;
-    SAFE_PARCEL(input->readInt32, &jankTypeInt);
-    jankType = static_cast<JankType>(jankTypeInt);
+    SAFE_PARCEL(input->readInt32, &jankType);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0d370d3..5570d99 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -395,6 +395,7 @@
         mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
+        mIsAutoTimestamp(other.mIsAutoTimestamp),
         mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
@@ -424,6 +425,7 @@
     const bool explicitEarlyWakeupEnd = parcel->readBool();
     const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
+    const bool isAutoTimestamp = parcel->readBool();
     const int64_t frameTimelineVsyncId = parcel->readInt64();
 
     size_t count = static_cast<size_t>(parcel->readUint32());
@@ -497,6 +499,7 @@
     mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
+    mIsAutoTimestamp = isAutoTimestamp;
     mFrameTimelineVsyncId = frameTimelineVsyncId;
     mDisplayStates = displayStates;
     mListenerCallbacks = listenerCallbacks;
@@ -527,6 +530,7 @@
     parcel->writeBool(mExplicitEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
+    parcel->writeBool(mIsAutoTimestamp);
     parcel->writeInt64(mFrameTimelineVsyncId);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
     for (auto const& displayState : mDisplayStates) {
@@ -628,7 +632,8 @@
     mEarlyWakeup = false;
     mExplicitEarlyWakeupStart = false;
     mExplicitEarlyWakeupEnd = false;
-    mDesiredPresentTime = -1;
+    mDesiredPresentTime = 0;
+    mIsAutoTimestamp = true;
     mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 }
 
@@ -640,8 +645,9 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
-                            uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {},
+                            systemTime(), true, uncacheBuffer, false, {},
+                            0 /* Undefined transactionId */);
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -759,7 +765,7 @@
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
     sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
-                            mInputWindowCommands, mDesiredPresentTime,
+                            mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
                             hasListenerCallbacks, listenerCallbacks, mId);
     mId = generateId();
@@ -1201,6 +1207,9 @@
     }
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
+    if (mIsAutoTimestamp) {
+        mDesiredPresentTime = systemTime();
+    }
 
     registerSurfaceControlForCallback(sc);
 
@@ -1295,6 +1304,7 @@
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
         nsecs_t desiredPresentTime) {
     mDesiredPresentTime = desiredPresentTime;
+    mIsAutoTimestamp = false;
     return *this;
 }
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 7d25d61..40316db 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -166,7 +166,7 @@
             int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
             const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-            const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+            bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
             const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
 
     /* signal that we're done booting.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 26b3840..cb17cee 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -69,15 +69,14 @@
     status_t readFromParcel(const Parcel* input) override;
 
     JankData();
-    JankData(int64_t frameVsyncId, JankType jankType)
-          : frameVsyncId(frameVsyncId),
-            jankType(jankType) {}
+    JankData(int64_t frameVsyncId, int32_t jankType)
+          : frameVsyncId(frameVsyncId), jankType(jankType) {}
 
     // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
     int64_t frameVsyncId;
 
-    // The type of jank occurred
-    JankType jankType;
+    // Bitmask of janks that occurred
+    int32_t jankType;
 };
 
 class SurfaceStats : public Parcelable {
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 47daf95..fc91714 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -18,24 +18,31 @@
 
 namespace android {
 
-// Jank information tracked by SurfaceFlinger for the purpose of funneling to telemetry.
+// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+// TODO(b/175843808): Change JankType from enum to enum class
 enum JankType {
     // No Jank
     None = 0x0,
-    // Jank not related to SurfaceFlinger or the App
-    Display = 0x1,
+    // Jank that occurs in the layers below SurfaceFlinger
+    DisplayHAL = 0x1,
     // SF took too long on the CPU
-    SurfaceFlingerDeadlineMissed = 0x2,
+    SurfaceFlingerCpuDeadlineMissed = 0x2,
     // SF took too long on the GPU
     SurfaceFlingerGpuDeadlineMissed = 0x4,
     // Either App or GPU took too long on the frame
     AppDeadlineMissed = 0x8,
-    // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
-    // jank
-    // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
-    PredictionExpired = 0x10,
-    // Latching a buffer early might cause an early present of the frame
-    SurfaceFlingerEarlyLatch = 0x20,
+    // Vsync predictions have drifted beyond the threshold from the actual HWVsync
+    PredictionError = 0x10,
+    // Janks caused due to the time SF was scheduled to work on the frame
+    // Example: SF woke up too early and latched a buffer resulting in an early present
+    SurfaceFlingerScheduling = 0x20,
+    // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
+    // presented later because the previous buffer was presented in its expected vsync. This
+    // usually happens if there is an unexpectedly long frame causing the rest of the buffers
+    // to enter a stuffed state.
+    BufferStuffing = 0x40,
+    // Jank due to unknown reasons.
+    Unknown = 0x80,
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 3ee4a39..0abe72c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -373,13 +373,17 @@
         // to be presented. When it is not possible to present at exactly that time, it will be
         // presented after the time has passed.
         //
+        // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+        // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+        //
         // Desired present times that are more than 1 second in the future may be ignored.
         // When a desired present time has already passed, the transaction will be presented as soon
         // as possible.
         //
         // Transactions from the same process are presented in the same order that they are applied.
         // The desired present time does not affect this ordering.
-        int64_t mDesiredPresentTime = -1;
+        int64_t mDesiredPresentTime = 0;
+        bool mIsAutoTimestamp = true;
 
         // The vsync Id provided by Choreographer.getVsyncId
         int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c75c46c..3965ea0 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -638,6 +638,9 @@
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
     nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
+    // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
+    // the default obscured/untrusted touch filter introduced in S.
+    nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
     nonTouchableSurface->showAt(100, 100);
 
     injectTap(190, 199);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 7761db8..fa98cd4c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -700,7 +700,7 @@
                                  const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                                  const sp<IBinder>& /*applyToken*/,
                                  const InputWindowCommands& /*inputWindowCommands*/,
-                                 int64_t /*desiredPresentTime*/,
+                                 int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
                                  const client_cache_t& /*cachedBuffer*/,
                                  bool /*hasListenerCallbacks*/,
                                  const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index e2cf245..f8f2f4e 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -43,7 +43,7 @@
                                                                   KEY_LAYOUT);
         ASSERT_FALSE(path.empty());
         base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
-        ASSERT_TRUE(ret) << "Cannot load KeyLayout at " << path;
+        ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path;
         mKeyMap.keyLayoutMap = std::move(*ret);
         mKeyMap.keyLayoutFile = path;
     }
@@ -58,7 +58,7 @@
         ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found";
         base::Result<std::shared_ptr<KeyCharacterMap>> ret =
                 KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
-        ASSERT_TRUE(ret) << "Cannot load KeyCharacterMap at " << path;
+        ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path;
         mKeyMap.keyCharacterMap = *ret;
         mKeyMap.keyCharacterMapFile = path;
     }
@@ -82,4 +82,4 @@
     ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 279e648..c88e298 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -222,6 +222,29 @@
     return err;
 }
 
+std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
+        const RenderEngineCreationArgs& args) {
+    if (!GLExtensions::getInstance().hasContextPriority()) {
+        return std::nullopt;
+    }
+
+    switch (args.contextPriority) {
+        case RenderEngine::ContextPriority::REALTIME:
+            if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+                return RenderEngine::ContextPriority::REALTIME;
+            } else {
+                ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+                return RenderEngine::ContextPriority::HIGH;
+            }
+        case RenderEngine::ContextPriority::HIGH:
+        case RenderEngine::ContextPriority::MEDIUM:
+        case RenderEngine::ContextPriority::LOW:
+            return args.contextPriority;
+        default:
+            return std::nullopt;
+    }
+}
+
 std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
     // initialize EGL for the default display
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -235,6 +258,7 @@
         LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
     }
 
+    // Use the Android impl to grab EGL_NV_context_priority_realtime
     const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
     if (!eglExtensions) {
         checkGlError(__FUNCTION__, __LINE__);
@@ -251,17 +275,16 @@
         config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
     }
 
-    bool useContextPriority =
-            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+    const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
     EGLContext protectedContext = EGL_NO_CONTEXT;
     if (args.enableProtectedContext && extensions.hasProtectedContent()) {
-        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
-                                            Protection::PROTECTED);
+        protectedContext =
+                createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
     }
 
-    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
-                                       Protection::UNPROTECTED);
+    EGLContext ctxt =
+            createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -311,7 +334,6 @@
     ALOGI("extensions: %s", extensions.getExtensions());
     ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
     ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
     return engine;
 }
 
@@ -802,6 +824,12 @@
     ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
 }
 
+int GLESRenderEngine::getContextPriority() {
+    int value;
+    eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+    return value;
+}
+
 FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
     // Translate win by the rounded corners rect coordinates, to have all values in
     // layer coordinate space.
@@ -1617,7 +1645,8 @@
 }
 
 EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
-                                              EGLContext shareContext, bool useContextPriority,
+                                              EGLContext shareContext,
+                                              std::optional<ContextPriority> contextPriority,
                                               Protection protection) {
     EGLint renderableType = 0;
     if (config == EGL_NO_CONFIG) {
@@ -1640,9 +1669,23 @@
     contextAttributes.reserve(7);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
-    if (useContextPriority) {
+    if (contextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        switch (*contextPriority) {
+            case ContextPriority::REALTIME:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+                break;
+            case ContextPriority::MEDIUM:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+                break;
+            case ContextPriority::LOW:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+                break;
+            case ContextPriority::HIGH:
+            default:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                break;
+        }
     }
     if (protection == Protection::PROTECTED) {
         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 92e1529..64d6c6b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,6 +71,7 @@
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
     bool cleanupPostRender(CleanupMode mode) override;
+    int getContextPriority() override;
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -116,8 +117,11 @@
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     static GlesVersion parseGlesVersion(const char* str);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
-                                       EGLContext shareContext, bool useContextPriority,
+                                       EGLContext shareContext,
+                                       std::optional<ContextPriority> contextPriority,
                                        Protection protection);
+    static std::optional<RenderEngine::ContextPriority> createContextPriority(
+            const RenderEngineCreationArgs& args);
     static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
                                                   int hwcFormat, Protection protection);
     std::unique_ptr<Framebuffer> createFramebuffer();
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 2924b0e..3dd534e 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -120,6 +120,10 @@
     if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
         mHasSurfacelessContext = true;
     }
+
+    if (extensionSet.hasExtension("EGL_NV_context_priority_realtime")) {
+        mHasRealtimePriority = true;
+    }
 }
 
 char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index ef00009..e415ff3 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -41,6 +41,7 @@
     bool hasContextPriority() const { return mHasContextPriority; }
     bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
     bool hasProtectedTexture() const { return mHasProtectedTexture; }
+    bool hasRealtimePriority() const { return mHasRealtimePriority; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
@@ -67,6 +68,7 @@
     bool mHasContextPriority = false;
     bool mHasSurfacelessContext = false;
     bool mHasProtectedTexture = false;
+    bool mHasRealtimePriority = false;
 
     String8 mVendor;
     String8 mRenderer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ef12fd2..9157066 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -75,6 +75,7 @@
         LOW = 1,
         MEDIUM = 2,
         HIGH = 3,
+        REALTIME = 4,
     };
 
     enum class RenderEngineType {
@@ -181,6 +182,10 @@
                                 const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                                 base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 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
+    // number of contexts that can be created at a specific priority level in the system.
+    virtual int getContextPriority() = 0;
 
 protected:
     friend class threaded::RenderEngineThreaded;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 95ee925..2c34da4 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -53,6 +53,7 @@
                           const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                           base::unique_fd*));
     MOCK_METHOD0(cleanFramebufferCache, void());
+    MOCK_METHOD0(getContextPriority, int());
 };
 
 } // namespace mock
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1f98a46..3d6b7af 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -169,17 +169,16 @@
         config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
     }
 
-    bool useContextPriority =
-            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
     EGLContext protectedContext = EGL_NO_CONTEXT;
+    const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
     if (args.enableProtectedContext && extensions.hasProtectedContent()) {
-        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
-                                            Protection::PROTECTED);
+        protectedContext =
+                createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
     }
 
-    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
-                                       Protection::UNPROTECTED);
+    EGLContext ctxt =
+            createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -832,7 +831,8 @@
 }
 
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
-                                                EGLContext shareContext, bool useContextPriority,
+                                                EGLContext shareContext,
+                                                std::optional<ContextPriority> contextPriority,
                                                 Protection protection) {
     EGLint renderableType = 0;
     if (config == EGL_NO_CONFIG_KHR) {
@@ -855,9 +855,23 @@
     contextAttributes.reserve(7);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
-    if (useContextPriority) {
+    if (contextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        switch (*contextPriority) {
+            case ContextPriority::REALTIME:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+                break;
+            case ContextPriority::MEDIUM:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+                break;
+            case ContextPriority::LOW:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+                break;
+            case ContextPriority::HIGH:
+            default:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                break;
+        }
     }
     if (protection == Protection::PROTECTED) {
         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
@@ -882,6 +896,29 @@
     return context;
 }
 
+std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
+        const RenderEngineCreationArgs& args) {
+    if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+        return std::nullopt;
+    }
+
+    switch (args.contextPriority) {
+        case RenderEngine::ContextPriority::REALTIME:
+            if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+                return RenderEngine::ContextPriority::REALTIME;
+            } else {
+                ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+                return RenderEngine::ContextPriority::HIGH;
+            }
+        case RenderEngine::ContextPriority::HIGH:
+        case RenderEngine::ContextPriority::MEDIUM:
+        case RenderEngine::ContextPriority::LOW:
+            return args.contextPriority;
+        default:
+            return std::nullopt;
+    }
+}
+
 EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
                                                                   EGLConfig config, int hwcFormat,
                                                                   Protection protection) {
@@ -906,6 +943,12 @@
 
 void SkiaGLRenderEngine::cleanFramebufferCache() {}
 
+int SkiaGLRenderEngine::getContextPriority() {
+    int value;
+    eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+    return value;
+}
+
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index b53250e..3d8c693 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -56,6 +56,7 @@
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
     void cleanFramebufferCache() override;
+    int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
@@ -68,8 +69,11 @@
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
-                                       EGLContext shareContext, bool useContextPriority,
+                                       EGLContext shareContext,
+                                       std::optional<ContextPriority> contextPriority,
                                        Protection protection);
+    static std::optional<RenderEngine::ContextPriority> createContextPriority(
+            const RenderEngineCreationArgs& args);
     static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
                                                          int hwcFormat, Protection protection);
     inline SkRect getSkRect(const FloatRect& layer);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 2352c7e..12b8586 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -56,6 +56,7 @@
         return 0;
     };
     virtual bool cleanupPostRender(CleanupMode) override { return true; };
+    virtual int getContextPriority() override { return 0; }
 };
 
 } // namespace skia
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 5453302..08f2949 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -304,6 +304,21 @@
     resultFuture.wait();
 }
 
+int RenderEngineThreaded::getContextPriority() {
+    std::promise<int> resultPromise;
+    std::future<int> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getContextPriority");
+            int priority = instance.getContextPriority();
+            resultPromise.set_value(priority);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
 } // namespace threaded
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index cdfbd04..8b1e2de 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -63,6 +63,7 @@
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
     void cleanFramebufferCache() override;
+    int getContextPriority() override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 393ced7..48abdce 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,36 +52,17 @@
     license: "include/KHR/NOTICE",
 }
 
-llndk_library {
-    name: "libEGL.llndk",
-    symbol_file: "libs/libEGL.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv1_CM.llndk",
-    symbol_file: "libs/libGLESv1_CM.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv2.llndk",
-    symbol_file: "libs/libGLESv2.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv3.llndk",
-    symbol_file: "libs/libGLESv3.map.txt",
-    export_include_dirs: ["include"],
-}
-
 cc_library_headers {
     name: "gl_headers",
     vendor_available: true,
     export_include_dirs: ["include"],
 }
 
+llndk_headers {
+    name: "gl_llndk_headers",
+    export_include_dirs: ["include"],
+}
+
 subdirs = [
     "*",
 ]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 77d887c..5d17561 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -225,3 +225,27 @@
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
+
+llndk_library {
+    name: "libEGL.llndk",
+    symbol_file: "libEGL.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv1_CM.llndk",
+    symbol_file: "libGLESv1_CM.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv2.llndk",
+    symbol_file: "libGLESv2.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv3.llndk",
+    symbol_file: "libGLESv3.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 0d17265..6235f06 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -84,13 +84,11 @@
         return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
-    virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
-            audio_port_handle_t deviceId) {
+    virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
         data.writeInt32((int32_t) piid);
         data.writeInt32((int32_t) event);
-        data.writeInt32((int32_t) deviceId);
         return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index b35a611..9606daa 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -41,6 +41,7 @@
     ],
     static_libs: [
         "libgmock",
+        "libperfetto_client_experimental",
         "perfetto_trace_protos",
     ],
     require_root: true,
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
index 7fa75e1..cd8e19c 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -75,6 +75,22 @@
 
     int getTracerThreadCount() { return mGpuMemTracer->tracerThreadCount; }
 
+    std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsBlocking(
+            perfetto::TracingSession* tracingSession) {
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        perfetto::protos::Trace trace;
+        trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
+
+        std::vector<perfetto::protos::TracePacket> packets;
+        for (const auto& packet : trace.packet()) {
+            if (!packet.has_gpu_mem_total_event()) {
+                continue;
+            }
+            packets.emplace_back(packet);
+        }
+        return packets;
+    }
+
     std::shared_ptr<GpuMem> mGpuMem;
     TestableGpuMem mTestableGpuMem;
     std::unique_ptr<GpuMemTracer> mGpuMemTracer;
@@ -125,7 +141,7 @@
     // The test tracer thread should have finished its execution by now.
     EXPECT_EQ(getTracerThreadCount(), 0);
 
-    auto packets = mGpuMemTracer->readGpuMemTotalPacketsForTestBlocking(tracingSession.get());
+    auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
     EXPECT_EQ(packets.size(), 3);
 
     const auto& packet0 = packets[0];
@@ -176,7 +192,7 @@
     // The test tracer thread should have finished its execution by now.
     EXPECT_EQ(getTracerThreadCount(), 0);
 
-    auto packets = mGpuMemTracer->readGpuMemTotalPacketsForTestBlocking(tracingSession.get());
+    auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
     EXPECT_EQ(packets.size(), 0);
 }
 } // namespace android
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
index 9f951d3..919fed3 100644
--- a/services/gpuservice/tracing/Android.bp
+++ b/services/gpuservice/tracing/Android.bp
@@ -21,13 +21,10 @@
         "libgpumem",
         "libbase",
         "liblog",
-        "libprotobuf-cpp-lite",
-        "libprotoutil",
         "libutils",
     ],
     static_libs: [
         "libperfetto_client_experimental",
-        "perfetto_trace_protos",
     ],
     export_include_dirs: ["include"],
     export_static_lib_headers: [
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 584498e..6975151 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -22,7 +22,6 @@
 
 #include <gpumem/GpuMem.h>
 #include <perfetto/trace/android/gpu_mem_event.pbzero.h>
-#include <perfetto/trace/trace.pb.h>
 #include <unistd.h>
 
 #include <thread>
@@ -63,22 +62,6 @@
     tracerThreadCount++;
 }
 
-std::vector<perfetto::protos::TracePacket> GpuMemTracer::readGpuMemTotalPacketsForTestBlocking(
-        perfetto::TracingSession* tracingSession) {
-    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
-    perfetto::protos::Trace trace;
-    trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
-
-    std::vector<perfetto::protos::TracePacket> packets;
-    for (const auto& packet : trace.packet()) {
-        if (!packet.has_gpu_mem_total_event()) {
-            continue;
-        }
-        packets.emplace_back(packet);
-    }
-    return packets;
-}
-
 // Each tracing session can be used for a single block of Start -> Stop.
 std::unique_ptr<perfetto::TracingSession> GpuMemTracer::getTracingSessionForTest() {
     perfetto::TraceConfig cfg;
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3d99589..a50e5c7 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -148,7 +148,7 @@
     }
 
     base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
-    if (!channel) {
+    if (!channel.ok()) {
         return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
                                                  channel.error().message().c_str());
     }
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fa75ffa..6561707 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -389,6 +389,20 @@
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
+bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
+    // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
+    // vsync period. We can do this change as soon as ag/13100772 is merged.
+    constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
+
+    const auto presentTime = nextPredictedPresentTime();
+    if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+        return false;
+    }
+
+    return presentTime >= expectedPresentTime &&
+            presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
+}
+
 bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
@@ -421,6 +435,12 @@
         return false;
     }
 
+    if (frameIsEarly(expectedPresentTime)) {
+        ATRACE_NAME("frameIsEarly()");
+        mFlinger->signalLayerUpdate();
+        return false;
+    }
+
     // If the head buffer's acquire fence hasn't signaled yet, return and
     // try again later
     if (!fenceHasSignaled()) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 63dfe5f..5cd9a7c 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -224,6 +224,18 @@
     std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+
+    // Returns true if the next frame is considered too early to present
+    // at the given expectedPresentTime
+    bool frameIsEarly(nsecs_t expectedPresentTime) const;
+
+    // Returns the predicted present time of the next frame if available or
+    // 0 otherwise.
+    virtual nsecs_t nextPredictedPresentTime() const = 0;
+
+    // The amount of time SF can delay a frame if it is considered early based
+    // on the VsyncModulator::VsyncConfig::appWorkDuration
+    static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 325ecfe..e8e31db 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -222,6 +222,21 @@
     return mQueuedFrames > 0;
 }
 
+nsecs_t BufferQueueLayer::nextPredictedPresentTime() const {
+    Mutex::Autolock lock(mQueueItemLock);
+    if (mQueueItems.empty()) {
+        return 0;
+    }
+
+    const auto& bufferData = mQueueItems[0];
+
+    if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
+        return 0;
+    }
+
+    return bufferData.surfaceFrame->getPredictions().presentTime;
+}
+
 status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                           nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -274,8 +289,8 @@
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
-                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                          PresentState::Dropped);
+                mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+                mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
             }
             mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
@@ -290,8 +305,8 @@
             Mutex::Autolock lock(mQueueItemLock);
             for (auto& [item, surfaceFrame] : mQueueItems) {
                 if (surfaceFrame) {
-                    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
-                                                              PresentState::Dropped);
+                    surfaceFrame->setPresentState(PresentState::Dropped);
+                    mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
                 }
             }
             mQueueItems.clear();
@@ -321,8 +336,8 @@
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
-                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                          PresentState::Dropped);
+                mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+                mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
             }
             mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
@@ -336,8 +351,9 @@
         if (mQueueItems[0].surfaceFrame) {
             mQueueItems[0].surfaceFrame->setAcquireFenceTime(
                     mQueueItems[0].item.mFenceTime->getSignalTime());
-            mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                      PresentState::Presented);
+            mQueueItems[0].surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
+            mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
+            mLastLatchTime = latchTime;
         }
         mQueueItems.erase(mQueueItems.begin());
     }
@@ -436,8 +452,9 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
-                                                                     mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+                                                                     mOwnerPid, mOwnerUid, mName,
+                                                                     mName);
         surfaceFrame->setActualQueueTime(systemTime());
 
         mQueueItems.push_back({item, surfaceFrame});
@@ -475,8 +492,9 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
-                                                                     mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+                                                                     mOwnerPid, mOwnerUid, mName,
+                                                                     mName);
         surfaceFrame->setActualQueueTime(systemTime());
         mQueueItems[mQueueItems.size() - 1].item = item;
         mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5a6b9bc..2347d8c 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -116,6 +116,8 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
+    nsecs_t nextPredictedPresentTime() const override;
+
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
@@ -146,6 +148,11 @@
     // a buffer to correlate the buffer with the vsync id. Can only be accessed
     // with the SF state lock held.
     std::optional<int64_t> mFrameTimelineVsyncId;
+
+    // Keeps track of the time SF latched the last buffer from this layer.
+    // Used in buffer stuffing analysis in FrameTimeline.
+    // TODO(b/176106798): Find a way to do this for BLASTBufferQueue as well.
+    nsecs_t mLastLatchTime = 0;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 0b9caba..7ec7f36 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -341,7 +341,7 @@
 }
 
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
-                                 nsecs_t postTime, nsecs_t desiredPresentTime,
+                                 nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber) {
     ATRACE_CALL();
 
@@ -365,13 +365,13 @@
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
                                       mOwnerUid, postTime);
-    desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
+    mCurrentState.isAutoTimestamp = isAutoTimestamp;
 
-    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+    mFlinger->mScheduler->recordLayerHistory(this, isAutoTimestamp ? 0 : desiredPresentTime,
                                              LayerHistory::LayerUpdateType::Buffer);
 
-    addFrameEvent(acquireFence, postTime, desiredPresentTime);
+    addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
     return true;
 }
 
@@ -533,7 +533,7 @@
         return true;
     }
 
-    return mCurrentState.desiredPresentTime <= expectedPresentTime;
+    return mCurrentState.isAutoTimestamp || mCurrentState.desiredPresentTime <= expectedPresentTime;
 }
 
 bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -601,6 +601,14 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
+nsecs_t BufferStateLayer::nextPredictedPresentTime() const {
+    if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) {
+        return 0;
+    }
+
+    return mSurfaceFrame->getPredictions().presentTime;
+}
+
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
                                           nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ad00c65..6414e6b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,8 +70,8 @@
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
-                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
-                   uint64_t frameNumber) override;
+                   nsecs_t desiredPresentTime, bool isAutoTimestamp,
+                   const client_cache_t& clientCacheId, uint64_t frameNumber) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -152,6 +152,8 @@
 
     bool bufferNeedsFiltering() const override;
 
+    nsecs_t nextPredictedPresentTime() const override;
+
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b09f07a..a8fef04 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -26,7 +26,7 @@
 #include <cinttypes>
 #include <numeric>
 
-namespace android::frametimeline::impl {
+namespace android::frametimeline {
 
 using base::StringAppendF;
 using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
@@ -60,7 +60,8 @@
         StringAppendF(&result, "\t%10.2f\t|",
                       std::chrono::duration<double, std::milli>(startTime).count());
     }
-    if (actuals.endTime == 0) {
+    if (actuals.endTime <= 0) {
+        // Animation leashes can send the endTime as -1
         StringAppendF(&result, "\t\tN/A\t|");
     } else {
         std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
@@ -89,124 +90,160 @@
         case PredictionState::Expired:
             return "Expired";
         case PredictionState::None:
-        default:
             return "None";
     }
 }
 
-std::string toString(JankType jankType) {
-    switch (jankType) {
-        case JankType::None:
-            return "None";
-        case JankType::Display:
-            return "Composer/Display - outside SF and App";
-        case JankType::SurfaceFlingerDeadlineMissed:
-            return "SurfaceFlinger Deadline Missed";
-        case JankType::AppDeadlineMissed:
-            return "App Deadline Missed";
-        case JankType::PredictionExpired:
-            return "Prediction Expired";
-        case JankType::SurfaceFlingerEarlyLatch:
-            return "SurfaceFlinger Early Latch";
-        default:
-            return "Unclassified";
-    }
-}
-
-std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
-    std::vector<std::string> jankInfo;
-
-    if (jankMetadata & EarlyStart) {
-        jankInfo.emplace_back("Early Start");
-    } else if (jankMetadata & LateStart) {
-        jankInfo.emplace_back("Late Start");
-    }
-
-    if (jankMetadata & EarlyFinish) {
-        jankInfo.emplace_back("Early Finish");
-    } else if (jankMetadata & LateFinish) {
-        jankInfo.emplace_back("Late Finish");
-    }
-
-    if (jankMetadata & EarlyPresent) {
-        jankInfo.emplace_back("Early Present");
-    } else if (jankMetadata & LatePresent) {
-        jankInfo.emplace_back("Late Present");
-    }
-    // TODO(b/169876734): add GPU composition metadata here
-
-    if (jankInfo.empty()) {
+std::string jankTypeBitmaskToString(int32_t jankType) {
+    // TODO(b/175843808): Make this a switch case if jankType becomes an enum class
+    std::vector<std::string> janks;
+    if (jankType == JankType::None) {
         return "None";
     }
-    return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+    if (jankType & JankType::DisplayHAL) {
+        janks.emplace_back("Display HAL");
+    }
+    if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+        janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+    }
+    if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+        janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+    }
+    if (jankType & JankType::AppDeadlineMissed) {
+        janks.emplace_back("App Deadline Missed");
+    }
+    if (jankType & JankType::PredictionError) {
+        janks.emplace_back("Prediction Error");
+    }
+    if (jankType & JankType::SurfaceFlingerScheduling) {
+        janks.emplace_back("SurfaceFlinger Scheduling");
+    }
+    if (jankType & JankType::BufferStuffing) {
+        janks.emplace_back("Buffer Stuffing");
+    }
+    if (jankType & JankType::Unknown) {
+        janks.emplace_back("Unknown jank");
+    }
+    return std::accumulate(janks.begin(), janks.end(), std::string(),
                            [](const std::string& l, const std::string& r) {
                                return l.empty() ? r : l + ", " + r;
                            });
 }
 
-FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
-    if (jankMetadata & EarlyPresent) {
-        return FrameTimelineEvent::PRESENT_EARLY;
+std::string toString(FramePresentMetadata presentMetadata) {
+    switch (presentMetadata) {
+        case FramePresentMetadata::OnTimePresent:
+            return "On Time Present";
+        case FramePresentMetadata::LatePresent:
+            return "Late Present";
+        case FramePresentMetadata::EarlyPresent:
+            return "Early Present";
+        case FramePresentMetadata::UnknownPresent:
+            return "Unknown Present";
     }
-    if (jankMetadata & LatePresent) {
-        return FrameTimelineEvent::PRESENT_LATE;
-    }
-    return FrameTimelineEvent::PRESENT_ON_TIME;
 }
 
-FrameTimelineEvent::JankType JankTypeToProto(JankType jankType) {
+std::string toString(FrameReadyMetadata finishMetadata) {
+    switch (finishMetadata) {
+        case FrameReadyMetadata::OnTimeFinish:
+            return "On Time Finish";
+        case FrameReadyMetadata::LateFinish:
+            return "Late Finish";
+        case FrameReadyMetadata::UnknownFinish:
+            return "Unknown Finish";
+    }
+}
+
+std::string toString(FrameStartMetadata startMetadata) {
+    switch (startMetadata) {
+        case FrameStartMetadata::OnTimeStart:
+            return "On Time Start";
+        case FrameStartMetadata::LateStart:
+            return "Late Start";
+        case FrameStartMetadata::EarlyStart:
+            return "Early Start";
+        case FrameStartMetadata::UnknownStart:
+            return "Unknown Start";
+    }
+}
+
+std::string toString(SurfaceFrame::PresentState presentState) {
+    using PresentState = SurfaceFrame::PresentState;
+    switch (presentState) {
+        case PresentState::Presented:
+            return "Presented";
+        case PresentState::Dropped:
+            return "Dropped";
+        case PresentState::Unknown:
+            return "Unknown";
+    }
+}
+
+FrameTimelineEvent::PresentType toProto(FramePresentMetadata presentMetadata) {
+    switch (presentMetadata) {
+        case FramePresentMetadata::EarlyPresent:
+            return FrameTimelineEvent::PRESENT_EARLY;
+        case FramePresentMetadata::LatePresent:
+            return FrameTimelineEvent::PRESENT_LATE;
+        case FramePresentMetadata::OnTimePresent:
+            return FrameTimelineEvent::PRESENT_ON_TIME;
+        case FramePresentMetadata::UnknownPresent:
+            return FrameTimelineEvent::PRESENT_UNSPECIFIED;
+    }
+}
+
+FrameTimelineEvent::JankType jankTypeBitmaskToProto(int32_t jankType) {
+    // TODO(b/175843808): Either make the proto a bitmask or jankType an enum class
     switch (jankType) {
         case JankType::None:
             return FrameTimelineEvent::JANK_NONE;
-        case JankType::Display:
+        case JankType::DisplayHAL:
             return FrameTimelineEvent::JANK_DISPLAY_HAL;
-        case JankType::SurfaceFlingerDeadlineMissed:
+        case JankType::SurfaceFlingerCpuDeadlineMissed:
+        case JankType::SurfaceFlingerGpuDeadlineMissed:
             return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
         case JankType::AppDeadlineMissed:
-        case JankType::PredictionExpired:
+        case JankType::PredictionError:
             return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+        case JankType::SurfaceFlingerScheduling:
+            return FrameTimelineEvent::JANK_SF_SCHEDULING;
+        case JankType::BufferStuffing:
+            return FrameTimelineEvent::JANK_BUFFER_STUFFING;
         default:
+            // TODO(b/175843808): Remove default if JankType becomes an enum class
             return FrameTimelineEvent::JANK_UNKNOWN;
     }
 }
 
-int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    const int64_t assignedToken = mCurrentToken++;
-    mPredictions[assignedToken] = predictions;
-    mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
-    flushTokens(systemTime());
-    return assignedToken;
-}
-
-std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    flushTokens(systemTime());
-    auto predictionsIterator = mPredictions.find(token);
-    if (predictionsIterator != mPredictions.end()) {
-        return predictionsIterator->second;
+// Returns the smallest timestamp from the set of predictions and actuals.
+nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
+                   TimelineItem actuals) {
+    nsecs_t minTime = std::numeric_limits<nsecs_t>::max();
+    if (predictionState == PredictionState::Valid) {
+        // Checking start time for predictions is enough because start time is always lesser than
+        // endTime and presentTime.
+        minTime = std::min(minTime, predictions.startTime);
     }
-    return {};
-}
 
-void TokenManager::flushTokens(nsecs_t flushTime) {
-    for (size_t i = 0; i < mTokens.size(); i++) {
-        if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
-            mPredictions.erase(mTokens[i].first);
-            mTokens.erase(mTokens.begin() + static_cast<int>(i));
-            --i;
-        } else {
-            // Tokens are ordered by time. If i'th token is within the retention time, then the
-            // i+1'th token will also be within retention time.
-            break;
-        }
+    // Need to check startTime, endTime and presentTime for actuals because some frames might not
+    // have them set.
+    if (actuals.startTime != 0) {
+        minTime = std::min(minTime, actuals.startTime);
     }
+    if (actuals.endTime != 0) {
+        minTime = std::min(minTime, actuals.endTime);
+    }
+    if (actuals.presentTime != 0) {
+        minTime = std::min(minTime, actuals.endTime);
+    }
+    return minTime;
 }
 
 SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                            std::string debugName, PredictionState predictionState,
-                           frametimeline::TimelineItem&& predictions)
+                           frametimeline::TimelineItem&& predictions,
+                           std::shared_ptr<TimeStats> timeStats,
+                           JankClassificationThresholds thresholds)
       : mToken(token),
         mOwnerPid(ownerPid),
         mOwnerUid(ownerUid),
@@ -216,29 +253,8 @@
         mPredictionState(predictionState),
         mPredictions(predictions),
         mActuals({0, 0, 0}),
-        mActualQueueTime(0),
-        mJankType(JankType::None),
-        mJankMetadata(0) {}
-
-void SurfaceFrame::setPresentState(PresentState state) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mPresentState = state;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mPresentState;
-}
-
-TimelineItem SurfaceFrame::getActuals() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mActuals;
-}
-
-nsecs_t SurfaceFrame::getActualQueueTime() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mActualQueueTime;
-}
+        mTimeStats(timeStats),
+        mJankClassificationThresholds(thresholds) {}
 
 void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -254,18 +270,13 @@
     mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
 }
 
-void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mActuals.presentTime = presentTime;
+    mPresentState = presentState;
+    mLastLatchTime = lastLatchTime;
 }
 
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mJankType = jankType;
-    mJankMetadata = jankMetadata;
-}
-
-std::optional<JankType> SurfaceFrame::getJankType() const {
+std::optional<int32_t> SurfaceFrame::getJankType() const {
     std::lock_guard<std::mutex> lock(mMutex);
     if (mActuals.presentTime == 0) {
         return std::nullopt;
@@ -275,31 +286,30 @@
 
 nsecs_t SurfaceFrame::getBaseTime() const {
     std::lock_guard<std::mutex> lock(mMutex);
-    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
-    if (mPredictionState == PredictionState::Valid) {
-        baseTime = std::min(baseTime, mPredictions.startTime);
-    }
-    if (mActuals.startTime != 0) {
-        baseTime = std::min(baseTime, mActuals.startTime);
-    }
-    baseTime = std::min(baseTime, mActuals.endTime);
-    return baseTime;
+    return getMinTime(mPredictionState, mPredictions, mActuals);
 }
 
-std::string presentStateToString(SurfaceFrame::PresentState presentState) {
-    using PresentState = SurfaceFrame::PresentState;
-    switch (presentState) {
-        case PresentState::Presented:
-            return "Presented";
-        case PresentState::Dropped:
-            return "Dropped";
-        case PresentState::Unknown:
-        default:
-            return "Unknown";
-    }
+TimelineItem SurfaceFrame::getActuals() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mActuals;
 }
 
-void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mPresentState;
+}
+
+FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mFramePresentMetadata;
+}
+
+FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mFrameReadyMetadata;
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
     std::lock_guard<std::mutex> lock(mMutex);
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Layer - %s", mDebugName.c_str());
@@ -309,21 +319,130 @@
     }
     StringAppendF(&result, "\n");
     StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+    StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+    StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Jank Metadata: %s\n",
-                  jankMetadataBitmaskToString(mJankMetadata).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    std::chrono::nanoseconds latchTime(
+            std::max(static_cast<int64_t>(0), mLastLatchTime - baseTime));
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Last latch time: %10f\n",
+                  std::chrono::duration<double, std::milli>(latchTime).count());
+    if (mPredictionState == PredictionState::Valid) {
+        nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+        std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+        StringAppendF(&result, "%s", indent.c_str());
+        StringAppendF(&result, "Present delta: %10f\n",
+                      std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+    }
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
-void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
-    using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType,
+                             nsecs_t vsyncPeriod) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mPresentState != PresentState::Presented) {
+        // No need to update dropped buffers
+        return;
+    }
+
+    mActuals.presentTime = presentTime;
+    // Jank Analysis for SurfaceFrame
+    if (mPredictionState == PredictionState::None) {
+        // Cannot do jank classification on frames that don't have a token.
+        return;
+    }
+    if (mPredictionState == PredictionState::Expired) {
+        // We do not know what happened here to classify this correctly. This could
+        // potentially be AppDeadlineMissed but that's assuming no app will request frames
+        // 120ms apart.
+        mJankType = JankType::Unknown;
+        mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+        mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+        mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+        return;
+    }
+
+    const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+    const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
+    const nsecs_t deltaToVsync = std::abs(presentDelta) % vsyncPeriod;
+
+    if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
+        mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+    } else {
+        mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+    }
+
+    if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+        mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+                                                 : FramePresentMetadata::EarlyPresent;
+    } else {
+        mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+    }
+
+    if (mFramePresentMetadata == FramePresentMetadata::OnTimePresent) {
+        // Frames presented on time are not janky.
+        mJankType = JankType::None;
+    } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+        if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            // Finish on time, Present early
+            if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+                // Delta factor of vsync
+                mJankType = JankType::SurfaceFlingerScheduling;
+            } else {
+                // Delta not a factor of vsync
+                mJankType = JankType::PredictionError;
+            }
+        } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+            // Finish late, Present early
+            mJankType = JankType::Unknown;
+        }
+    } else {
+        if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
+            // Buffer Stuffing.
+            mJankType |= JankType::BufferStuffing;
+        }
+        if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            // Finish on time, Present late
+            if (displayFrameJankType != JankType::None) {
+                // Propagate displayFrame's jank if it exists
+                mJankType |= displayFrameJankType;
+            } else {
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+                    // Delta factor of vsync
+                    mJankType |= JankType::SurfaceFlingerScheduling;
+                } else {
+                    // Delta not a factor of vsync
+                    mJankType |= JankType::PredictionError;
+                }
+            }
+        } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+            // Finish late, Present late
+            if (displayFrameJankType == JankType::None) {
+                // Display frame is not janky, so purely app's fault
+                mJankType |= JankType::AppDeadlineMissed;
+            } else {
+                // Propagate DisplayFrame's jankType if it is janky
+                mJankType |= displayFrameJankType;
+            }
+        }
+    }
+    mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+}
+
+void SurfaceFrame::trace(int64_t displayFrameToken) {
+    using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
     FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
         std::lock_guard<std::mutex> lock(mMutex);
         if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
@@ -349,11 +468,12 @@
         } else if (mPresentState == PresentState::Unknown) {
             surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
         } else {
-            surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
+            surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
         }
-        surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
-        surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
-        surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));
+        surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                              FrameReadyMetadata::OnTimeFinish);
+        surfaceFrameEvent->set_gpu_composition(mGpuComposition);
+        surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
 
         surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
         surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
@@ -366,10 +486,45 @@
     });
 }
 
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
-      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+namespace impl {
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    const int64_t assignedToken = mCurrentToken++;
+    mPredictions[assignedToken] = {systemTime(), predictions};
+    flushTokens(systemTime());
+    return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto predictionsIterator = mPredictions.find(token);
+    if (predictionsIterator != mPredictions.end()) {
+        return predictionsIterator->second.predictions;
+    }
+    return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+    for (auto it = mPredictions.begin(); it != mPredictions.end();) {
+        if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
+            it = mPredictions.erase(it);
+        } else {
+            // Tokens are ordered by time. If i'th token is within the retention time, then the
+            // i+1'th token will also be within retention time.
+            break;
+        }
+    }
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+                             JankClassificationThresholds thresholds)
+      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
         mMaxDisplayFrames(kDefaultMaxDisplayFrames),
-        mTimeStats(std::move(timeStats)) {}
+        mTimeStats(std::move(timeStats)),
+        mSurfaceFlingerPid(surfaceFlingerPid),
+        mJankClassificationThresholds(thresholds) {}
 
 void FrameTimeline::onBootFinished() {
     perfetto::TracingInitArgs args;
@@ -384,67 +539,225 @@
     FrameTimelineDataSource::Register(dsd);
 }
 
-FrameTimeline::DisplayFrame::DisplayFrame()
-      : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
-    this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
-}
-
-std::shared_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
-        pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-        std::optional<int64_t> token) {
+std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+        std::optional<int64_t> token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+        std::string debugName) {
     ATRACE_CALL();
     if (!token) {
-        return std::make_shared<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID,ownerPid,
-                                                    ownerUid, std::move(layerName),
-                                                    std::move(debugName), PredictionState::None,
-                                                    TimelineItem());
+        return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+                                              ownerUid, std::move(layerName), std::move(debugName),
+                                              PredictionState::None, TimelineItem(), mTimeStats,
+                                              mJankClassificationThresholds);
     }
     std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
     if (predictions) {
-        return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
-                                                    std::move(layerName), std::move(debugName),
-                                                    PredictionState::Valid,
-                                                    std::move(*predictions));
+        return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+                                              std::move(debugName), PredictionState::Valid,
+                                              std::move(*predictions), mTimeStats,
+                                              mJankClassificationThresholds);
     }
-    return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
-                                                std::move(debugName), PredictionState::Expired,
-                                                TimelineItem());
+    return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+                                          std::move(debugName), PredictionState::Expired,
+                                          TimelineItem(), mTimeStats,
+                                          mJankClassificationThresholds);
 }
 
-void FrameTimeline::addSurfaceFrame(
-        std::shared_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
-        SurfaceFrame::PresentState state) {
-    ATRACE_CALL();
-    surfaceFrame->setPresentState(state);
-    std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->surfaceFrames.push_back(
-            std::static_pointer_cast<impl::SurfaceFrame>(surfaceFrame));
+FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
+                                          JankClassificationThresholds thresholds)
+      : mSurfaceFlingerPredictions(TimelineItem()),
+        mSurfaceFlingerActuals(TimelineItem()),
+        mTimeStats(timeStats),
+        mJankClassificationThresholds(thresholds) {
+    mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
 
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
     ATRACE_CALL();
-    const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
     std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->token = token;
-    if (!prediction) {
-        mCurrentDisplayFrame->predictionState = PredictionState::Expired;
-    } else {
-        mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
-        mCurrentDisplayFrame->predictionState = PredictionState::Valid;
-    }
-    mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+    mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, nsecs_t vsyncPeriod) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    mCurrentDisplayFrame->onSfWakeUp(token, vsyncPeriod,
+                                     mTokenManager.getPredictionsForToken(token), wakeUpTime);
 }
 
 void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
                                  const std::shared_ptr<FenceTime>& presentFence) {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+    mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
     mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
     flushPendingPresentFences();
     finalizeCurrentDisplayFrame();
 }
 
+void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
+    mSurfaceFrames.push_back(surfaceFrame);
+}
+
+void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, nsecs_t vsyncPeriod,
+                                             std::optional<TimelineItem> predictions,
+                                             nsecs_t wakeUpTime) {
+    mToken = token;
+    mVsyncPeriod = vsyncPeriod;
+    if (!predictions) {
+        mPredictionState = PredictionState::Expired;
+    } else {
+        mPredictionState = PredictionState::Valid;
+        mSurfaceFlingerPredictions = *predictions;
+    }
+    mSurfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::DisplayFrame::setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod) {
+    mToken = token;
+    mVsyncPeriod = vsyncPeriod;
+}
+
+void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState,
+                                                 TimelineItem predictions) {
+    mPredictionState = predictionState;
+    mSurfaceFlingerPredictions = predictions;
+}
+
+void FrameTimeline::DisplayFrame::setActualStartTime(nsecs_t actualStartTime) {
+    mSurfaceFlingerActuals.startTime = actualStartTime;
+}
+
+void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
+    mSurfaceFlingerActuals.endTime = actualEndTime;
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+    mSurfaceFlingerActuals.presentTime = signalTime;
+    int32_t totalJankReasons = JankType::None;
+
+    // Delta between the expected present and the actual present
+    const nsecs_t presentDelta =
+            mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+    // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
+    // was a prediction error or not.
+    nsecs_t deltaToVsync = std::abs(presentDelta) % mVsyncPeriod;
+    if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+        mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+                                                 : FramePresentMetadata::EarlyPresent;
+    } else {
+        mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+    }
+
+    if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
+        mJankClassificationThresholds.deadlineThreshold) {
+        mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+    } else {
+        mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+    }
+
+    if (std::abs(mSurfaceFlingerActuals.startTime - mSurfaceFlingerPredictions.startTime) >
+        mJankClassificationThresholds.startThreshold) {
+        mFrameStartMetadata =
+                mSurfaceFlingerActuals.startTime > mSurfaceFlingerPredictions.startTime
+                ? FrameStartMetadata::LateStart
+                : FrameStartMetadata::EarlyStart;
+    }
+
+    if (mFramePresentMetadata != FramePresentMetadata::OnTimePresent) {
+        // Do jank classification only if present is not on time
+        if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+                // Finish on time, Present early
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >=
+                            (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+                    // Delta is a factor of vsync if its within the presentTheshold on either side
+                    // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+                    // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+                    mJankType = JankType::SurfaceFlingerScheduling;
+                } else {
+                    // Delta is not a factor of vsync,
+                    mJankType = JankType::PredictionError;
+                }
+            } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+                // Finish late, Present early
+                mJankType = JankType::SurfaceFlingerScheduling;
+            } else {
+                // Finish time unknown
+                mJankType = JankType::Unknown;
+            }
+        } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+                // Finish on time, Present late
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >=
+                            (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+                    // Delta is a factor of vsync if its within the presentTheshold on either side
+                    // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+                    // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+                    mJankType = JankType::DisplayHAL;
+                } else {
+                    // Delta is not a factor of vsync
+                    mJankType = JankType::PredictionError;
+                }
+            } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+                // Finish late, Present late
+                mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+            } else {
+                // Finish time unknown
+                mJankType = JankType::Unknown;
+            }
+        } else {
+            // Present unknown
+            mJankType = JankType::Unknown;
+        }
+    }
+    totalJankReasons |= mJankType;
+
+    for (auto& surfaceFrame : mSurfaceFrames) {
+        surfaceFrame->onPresent(signalTime, mJankType, mVsyncPeriod);
+        auto surfaceFrameJankType = surfaceFrame->getJankType();
+        if (surfaceFrameJankType != std::nullopt) {
+            totalJankReasons |= *surfaceFrameJankType;
+        }
+    }
+    mTimeStats->incrementJankyFrames(totalJankReasons);
+}
+
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+            ALOGD("Cannot trace DisplayFrame with invalid token");
+            return;
+        }
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* displayFrameEvent = event->set_display_frame();
+
+        displayFrameEvent->set_token(mToken);
+        displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+        displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                              FrameReadyMetadata::OnTimeFinish);
+        displayFrameEvent->set_gpu_composition(mGpuComposition);
+        displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+
+        displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
+        displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+
+        displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
+        displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+
+        displayFrameEvent->set_pid(surfaceFlingerPid);
+    });
+
+    for (auto& surfaceFrame : mSurfaceFrames) {
+        surfaceFrame->trace(mToken);
+    }
+}
+
 void FrameTimeline::flushPendingPresentFences() {
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
@@ -456,93 +769,9 @@
             }
         }
         if (signalTime != Fence::SIGNAL_TIME_INVALID) {
-            int32_t totalJankReasons = JankType::None;
             auto& displayFrame = pendingPresentFence.second;
-            displayFrame->surfaceFlingerActuals.presentTime = signalTime;
-
-            // Jank Analysis for DisplayFrame
-            const auto& sfActuals = displayFrame->surfaceFlingerActuals;
-            const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
-            if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
-                displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
-                        ? LatePresent
-                        : EarlyPresent;
-            }
-            if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
-                if (sfActuals.endTime > sfPredictions.endTime) {
-                    displayFrame->jankMetadata |= LateFinish;
-                } else {
-                    displayFrame->jankMetadata |= EarlyFinish;
-                }
-
-                if ((displayFrame->jankMetadata & EarlyFinish) &&
-                    (displayFrame->jankMetadata & EarlyPresent)) {
-                    displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
-                } else if ((displayFrame->jankMetadata & LateFinish) &&
-                           (displayFrame->jankMetadata & LatePresent)) {
-                    displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
-                } else if (displayFrame->jankMetadata & EarlyPresent ||
-                           displayFrame->jankMetadata & LatePresent) {
-                    // Cases where SF finished early but frame was presented late and vice versa
-                    displayFrame->jankType = JankType::Display;
-                }
-            }
-
-            if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
-                displayFrame->jankMetadata |=
-                        sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
-            }
-
-            totalJankReasons |= displayFrame->jankType;
-            traceDisplayFrame(*displayFrame);
-
-            for (auto& surfaceFrame : displayFrame->surfaceFrames) {
-                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
-                    // Only presented SurfaceFrames need to be updated
-                    surfaceFrame->setActualPresentTime(signalTime);
-
-                    // Jank Analysis for SurfaceFrame
-                    const auto& predictionState = surfaceFrame->getPredictionState();
-                    if (predictionState == PredictionState::Expired) {
-                        // Jank analysis cannot be done on apps that don't use predictions
-                        surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
-                    } else if (predictionState == PredictionState::Valid) {
-                        const auto& actuals = surfaceFrame->getActuals();
-                        const auto& predictions = surfaceFrame->getPredictions();
-                        int32_t jankMetadata = 0;
-                        JankType jankType = JankType::None;
-                        if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
-                            jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
-                                                                                  : EarlyFinish;
-                        }
-                        if (std::abs(actuals.presentTime - predictions.presentTime) >
-                            kPresentThreshold) {
-                            jankMetadata |= actuals.presentTime > predictions.presentTime
-                                    ? LatePresent
-                                    : EarlyPresent;
-                        }
-                        if (jankMetadata & EarlyPresent) {
-                            jankType = JankType::SurfaceFlingerEarlyLatch;
-                        } else if (jankMetadata & LatePresent) {
-                            if (jankMetadata & EarlyFinish) {
-                                // TODO(b/169890654): Classify this properly
-                                jankType = JankType::Display;
-                            } else {
-                                jankType = JankType::AppDeadlineMissed;
-                            }
-                        }
-
-                        totalJankReasons |= jankType;
-                        mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
-                                                         surfaceFrame->getName(),
-                                                         jankType | displayFrame->jankType);
-                        surfaceFrame->setJankInfo(jankType, jankMetadata);
-                    }
-                }
-                surfaceFrame->traceSurfaceFrame(displayFrame->token);
-            }
-
-            mTimeStats->incrementJankyFrames(totalJankReasons);
+            displayFrame->onPresent(signalTime);
+            displayFrame->trace(mSurfaceFlingerPid);
         }
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -557,16 +786,14 @@
     }
     mDisplayFrames.push_back(mCurrentDisplayFrame);
     mCurrentDisplayFrame.reset();
-    mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+    mCurrentDisplayFrame =
+            std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
 }
 
-nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
-    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
-    if (displayFrame->predictionState == PredictionState::Valid) {
-        baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
-    }
-    baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
-    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
+    nsecs_t baseTime =
+            getMinTime(mPredictionState, mSurfaceFlingerPredictions, mSurfaceFlingerActuals);
+    for (const auto& surfaceFrame : mSurfaceFrames) {
         nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
         if (surfaceFrameBaseTime != 0) {
             baseTime = std::min(baseTime, surfaceFrameBaseTime);
@@ -575,60 +802,79 @@
     return baseTime;
 }
 
-void FrameTimeline::dumpDisplayFrame(std::string& result,
-                                     const std::shared_ptr<DisplayFrame>& displayFrame,
-                                     nsecs_t baseTime) {
-    if (displayFrame->jankType != JankType::None) {
+void FrameTimeline::DisplayFrame::dumpJank(std::string& result, nsecs_t baseTime,
+                                           int displayFrameCount) const {
+    if (mJankType == JankType::None) {
+        // Check if any Surface Frame has been janky
+        bool isJanky = false;
+        for (const auto& surfaceFrame : mSurfaceFrames) {
+            if (surfaceFrame->getJankType() != JankType::None) {
+                isJanky = true;
+                break;
+            }
+        }
+        if (!isJanky) {
+            return;
+        }
+    }
+    StringAppendF(&result, "Display Frame %d", displayFrameCount);
+    dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dumpAll(std::string& result, nsecs_t baseTime) const {
+    dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) const {
+    if (mJankType != JankType::None) {
         // Easily identify a janky Display Frame in the dump
         StringAppendF(&result, " [*] ");
     }
     StringAppendF(&result, "\n");
-    StringAppendF(&result, "Prediction State : %s\n",
-                  toString(displayFrame->predictionState).c_str());
-    StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
-    StringAppendF(&result, "Jank Metadata: %s\n",
-                  jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
-    dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
-              "", displayFrame->predictionState, baseTime);
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str());
+    std::chrono::nanoseconds vsyncPeriod(mVsyncPeriod);
+    StringAppendF(&result, "Vsync Period: %10f\n",
+                  std::chrono::duration<double, std::milli>(vsyncPeriod).count());
+    nsecs_t presentDelta =
+            mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+    std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+    StringAppendF(&result, "Present delta: %10f\n",
+                  std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+    std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mVsyncPeriod);
+    StringAppendF(&result, "Present delta %% refreshrate: %10f\n",
+                  std::chrono::duration<double, std::milli>(deltaToVsync).count());
+    dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState,
+              baseTime);
     StringAppendF(&result, "\n");
     std::string indent = "    "; // 4 spaces
-    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+    for (const auto& surfaceFrame : mSurfaceFrames) {
         surfaceFrame->dump(result, indent, baseTime);
     }
     StringAppendF(&result, "\n");
 }
+
 void FrameTimeline::dumpAll(std::string& result) {
     std::lock_guard<std::mutex> lock(mMutex);
     StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
-    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
     for (size_t i = 0; i < mDisplayFrames.size(); i++) {
         StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
-        dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+        mDisplayFrames[i]->dumpAll(result, baseTime);
     }
 }
 
 void FrameTimeline::dumpJank(std::string& result) {
     std::lock_guard<std::mutex> lock(mMutex);
-    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
     for (size_t i = 0; i < mDisplayFrames.size(); i++) {
-        const auto& displayFrame = mDisplayFrames[i];
-        if (displayFrame->jankType == JankType::None) {
-            // Check if any Surface Frame has been janky
-            bool isJanky = false;
-            for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
-                if (surfaceFrame->getJankType() != JankType::None) {
-                    isJanky = true;
-                    break;
-                }
-            }
-            if (!isJanky) {
-                continue;
-            }
-        }
-        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
-        dumpDisplayFrame(result, displayFrame, baseTime);
+        mDisplayFrames[i]->dumpJank(result, baseTime, static_cast<int>(i));
     }
 }
+
 void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
     std::unordered_map<std::string, bool> argsMap;
@@ -656,31 +902,5 @@
     setMaxDisplayFrames(kDefaultMaxDisplayFrames);
 }
 
-void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
-    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
-        if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
-            ALOGD("Cannot trace DisplayFrame with invalid token");
-            return;
-        }
-        auto packet = ctx.NewTracePacket();
-        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
-
-        auto* event = packet->set_frame_timeline_event();
-        auto* displayFrameEvent = event->set_display_frame();
-
-        displayFrameEvent->set_token(displayFrame.token);
-        displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
-        displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
-        displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
-        displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));
-
-        displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
-        displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);
-
-        displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
-        displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
-    });
-}
-
-} // namespace android::frametimeline::impl
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 084935b..f5239aa 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -32,24 +32,44 @@
 
 namespace android::frametimeline {
 
-enum JankMetadata {
-    // Frame was presented earlier than expected
-    EarlyPresent = 0x1,
-    // Frame was presented later than expected
-    LatePresent = 0x2,
-    // App/SF started earlier than expected
-    EarlyStart = 0x4,
-    // App/SF started later than expected
-    LateStart = 0x8,
-    // App/SF finished work earlier than the deadline
-    EarlyFinish = 0x10,
-    // App/SF finished work later than the deadline
-    LateFinish = 0x20,
-    // SF was in GPU composition
-    GpuComposition = 0x40,
+class FrameTimelineTest;
+
+using namespace std::chrono_literals;
+
+// Metadata indicating how the frame was presented w.r.t expected present time.
+enum class FramePresentMetadata : int8_t {
+    // Frame was presented on time
+    OnTimePresent,
+    // Frame was presented late
+    LatePresent,
+    // Frame was presented early
+    EarlyPresent,
+    // Unknown/initial state
+    UnknownPresent,
 };
 
-class FrameTimelineTest;
+// Metadata comparing the frame's actual finish time to the expected deadline.
+enum class FrameReadyMetadata : int8_t {
+    // App/SF finished on time. Early finish is treated as on time since the goal of any component
+    // is to finish before the deadline.
+    OnTimeFinish,
+    // App/SF finished work later than expected
+    LateFinish,
+    // Unknown/initial state
+    UnknownFinish,
+};
+
+// Metadata comparing the frame's actual start time to the expected start time.
+enum class FrameStartMetadata : int8_t {
+    // App/SF started on time
+    OnTimeStart,
+    // App/SF started later than expected
+    LateStart,
+    // App/SF started earlier than expected
+    EarlyStart,
+    // Unknown/initial state
+    UnknownStart,
+};
 
 /*
  * Collection of timestamps that can be used for both predictions and actual times.
@@ -71,6 +91,19 @@
     bool operator!=(const TimelineItem& other) const { return !(*this == other); }
 };
 
+struct TokenManagerPrediction {
+    nsecs_t timestamp = 0;
+    TimelineItem predictions;
+};
+
+struct JankClassificationThresholds {
+    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+    // compared to prediction, we treat it as on time.
+    nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+};
+
 /*
  * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
  * saves these predictions for a short period of time and returns the predictions for a given token,
@@ -83,6 +116,9 @@
     // Generates a token for the given set of predictions. Stores the predictions for 120ms and
     // destroys it later.
     virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+
+    // Returns the stored predictions for a given token, if the predictions haven't expired.
+    virtual std::optional<TimelineItem> getPredictionsForToken(int64_t token) const = 0;
 };
 
 enum class PredictionState {
@@ -91,10 +127,6 @@
     None,    // Predictions are either not present or didn't come from TokenManager
 };
 
-/*
- * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
- * from the app
- */
 class SurfaceFrame {
 public:
     enum class PresentState {
@@ -103,29 +135,76 @@
         Unknown,   // Initial state, SurfaceFlinger hasn't seen this buffer yet
     };
 
-    virtual ~SurfaceFrame() = default;
+    // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
+    // TokenManager), Thresholds and TimeStats pointer.
+    SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+                 std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
+                 std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+    ~SurfaceFrame() = default;
 
-    virtual TimelineItem getPredictions() const = 0;
-    virtual TimelineItem getActuals() const = 0;
-    virtual nsecs_t getActualQueueTime() const = 0;
-    virtual PresentState getPresentState() const = 0;
-    virtual PredictionState getPredictionState() const = 0;
-    virtual pid_t getOwnerPid() const = 0;
+    // Returns std::nullopt if the frame hasn't been classified yet.
+    // Used by both SF and FrameTimeline.
+    std::optional<int32_t> getJankType() const;
 
-    virtual void setPresentState(PresentState state) = 0;
-
+    // Functions called by SF
+    int64_t getToken() const { return mToken; };
+    TimelineItem getPredictions() const { return mPredictions; };
     // Actual timestamps of the app are set individually at different functions.
     // Start time (if the app provides) and Queue time are accessible after queueing the frame,
     // whereas Acquire Fence time is available only during latch.
-    virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
-    virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
-    virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+    void setActualStartTime(nsecs_t actualStartTime);
+    void setActualQueueTime(nsecs_t actualQueueTime);
+    void setAcquireFenceTime(nsecs_t acquireFenceTime);
+    void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
 
-    // Retrieves jank classification, if it's already been classified.
-    virtual std::optional<JankType> getJankType() const = 0;
+    // Functions called by FrameTimeline
+    // BaseTime is the smallest timestamp in this SurfaceFrame.
+    // Used for dumping all timestamps relative to the oldest, making it easy to read.
+    nsecs_t getBaseTime() const;
+    // Sets the actual present time, appropriate metadata and classifies the jank.
+    void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, nsecs_t vsyncPeriod);
+    // All the timestamps are dumped relative to the baseTime
+    void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
+    // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
+    // DisplayFrame at the trace processor side.
+    void trace(int64_t displayFrameToken);
 
-    // Token identifying the frame.
-    virtual int64_t getToken() const = 0;
+    // Getter functions used only by FrameTimelineTests
+    TimelineItem getActuals() const;
+    pid_t getOwnerPid() const { return mOwnerPid; };
+    PredictionState getPredictionState() const { return mPredictionState; };
+    PresentState getPresentState() const;
+    FrameReadyMetadata getFrameReadyMetadata() const;
+    FramePresentMetadata getFramePresentMetadata() const;
+
+private:
+    const int64_t mToken;
+    const pid_t mOwnerPid;
+    const uid_t mOwnerUid;
+    const std::string mLayerName;
+    const std::string mDebugName;
+    PresentState mPresentState GUARDED_BY(mMutex);
+    const PredictionState mPredictionState;
+    const TimelineItem mPredictions;
+    TimelineItem mActuals GUARDED_BY(mMutex);
+    std::shared_ptr<TimeStats> mTimeStats;
+    const JankClassificationThresholds mJankClassificationThresholds;
+    nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0;
+    mutable std::mutex mMutex;
+    // Bitmask for the type of jank
+    int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
+    // Indicates if this frame was composited by the GPU or not
+    bool mGpuComposition GUARDED_BY(mMutex) = false;
+    // Enum for the type of present
+    FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) =
+            FramePresentMetadata::UnknownPresent;
+    // Enum for the type of finish
+    FrameReadyMetadata mFrameReadyMetadata GUARDED_BY(mMutex) = FrameReadyMetadata::UnknownFinish;
+    // Time when the previous buffer from the same layer was latched by SF. This is used in checking
+    // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
+    // was latched instead.
+    nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
 };
 
 /*
@@ -142,20 +221,19 @@
     virtual void onBootFinished() = 0;
 
     // Create a new surface frame, set the predictions based on a token and return it to the caller.
-    // Sets the PredictionState of SurfaceFrame.
     // Debug name is the human-readable debugging string for dumpsys.
-    virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
-            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-            std::optional<int64_t> token) = 0;
+    virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+                                                                     pid_t ownerPid, uid_t ownerUid,
+                                                                     std::string layerName,
+                                                                     std::string debugName) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
-    virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame,
-                                 SurfaceFrame::PresentState state) = 0;
+    virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) = 0;
 
     // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
     // the token and sets the actualSfWakeTime for the current DisplayFrame.
-    virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0;
+    virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) = 0;
 
     // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
     // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
@@ -178,15 +256,13 @@
 
 namespace impl {
 
-using namespace std::chrono_literals;
-
 class TokenManager : public android::frametimeline::TokenManager {
 public:
     TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
     ~TokenManager() = default;
 
     int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
-    std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+    std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
 
 private:
     // Friend class for testing
@@ -194,64 +270,13 @@
 
     void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
 
-    std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
-    std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+    std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
     int64_t mCurrentToken GUARDED_BY(mMutex);
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     static constexpr nsecs_t kMaxRetentionTime =
             std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
 };
 
-class SurfaceFrame : public android::frametimeline::SurfaceFrame {
-public:
-    SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
-                 std::string debugName, PredictionState predictionState,
-                 TimelineItem&& predictions);
-    ~SurfaceFrame() = default;
-
-    TimelineItem getPredictions() const override { return mPredictions; };
-    TimelineItem getActuals() const override;
-    nsecs_t getActualQueueTime() const override;
-    PresentState getPresentState() const override;
-    PredictionState getPredictionState() const override { return mPredictionState; };
-    pid_t getOwnerPid() const override { return mOwnerPid; };
-    std::optional<JankType> getJankType() const override;
-    int64_t getToken() const override { return mToken; };
-    nsecs_t getBaseTime() const;
-    uid_t getOwnerUid() const { return mOwnerUid; };
-    const std::string& getName() const { return mLayerName; };
-
-    void setActualStartTime(nsecs_t actualStartTime) override;
-    void setActualQueueTime(nsecs_t actualQueueTime) override;
-    void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
-    void setPresentState(PresentState state) override;
-    void setActualPresentTime(nsecs_t presentTime);
-    void setJankInfo(JankType jankType, int32_t jankMetadata);
-
-    // All the timestamps are dumped relative to the baseTime
-    void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
-
-    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
-    // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
-    // DisplayFrame at the trace processor side.
-    void traceSurfaceFrame(int64_t displayFrameToken);
-
-private:
-    const int64_t mToken;
-    const pid_t mOwnerPid;
-    const uid_t mOwnerUid;
-    const std::string mLayerName;
-    const std::string mDebugName;
-    PresentState mPresentState GUARDED_BY(mMutex);
-    const PredictionState mPredictionState;
-    const TimelineItem mPredictions;
-    TimelineItem mActuals GUARDED_BY(mMutex);
-    nsecs_t mActualQueueTime GUARDED_BY(mMutex);
-    mutable std::mutex mMutex;
-    JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
-    int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
-};
-
 class FrameTimeline : public android::frametimeline::FrameTimeline {
 public:
     class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
@@ -260,16 +285,94 @@
         void OnStop(const StopArgs&) override{};
     };
 
-    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+    /*
+     * DisplayFrame should be used only internally within FrameTimeline. All members and methods are
+     * guarded by FrameTimeline's mMutex.
+     */
+    class DisplayFrame {
+    public:
+        DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+        virtual ~DisplayFrame() = default;
+        // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
+        // SurfaceFrame is janky.
+        void dumpJank(std::string& result, nsecs_t baseTime, int displayFrameCount) const;
+        // Dumpsys interface - dumps all data irrespective of jank
+        void dumpAll(std::string& result, nsecs_t baseTime) const;
+        // Emits a packet for perfetto tracing. The function body will be executed only if tracing
+        // is enabled.
+        void trace(pid_t surfaceFlingerPid) const;
+        // Sets the token, vsyncPeriod, predictions and SF start time.
+        void onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, std::optional<TimelineItem> predictions,
+                        nsecs_t wakeUpTime);
+        // Sets the appropriate metadata, classifies the jank and returns the classified jankType.
+        void onPresent(nsecs_t signalTime);
+        // Adds the provided SurfaceFrame to the current display frame.
+        void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
+
+        void setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod);
+        void setPredictions(PredictionState predictionState, TimelineItem predictions);
+        void setActualStartTime(nsecs_t actualStartTime);
+        void setActualEndTime(nsecs_t actualEndTime);
+
+        // BaseTime is the smallest timestamp in a DisplayFrame.
+        // Used for dumping all timestamps relative to the oldest, making it easy to read.
+        nsecs_t getBaseTime() const;
+
+        // Functions to be used only in testing.
+        TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
+        TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
+        FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
+        FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
+        int32_t getJankType() const { return mJankType; }
+        const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
+            return mSurfaceFrames;
+        }
+
+    private:
+        void dump(std::string& result, nsecs_t baseTime) const;
+
+        int64_t mToken = ISurfaceComposer::INVALID_VSYNC_ID;
+
+        /* Usage of TimelineItem w.r.t SurfaceFlinger
+         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+         * endTime      Time when SurfaceFlinger sends a composited frame to Display
+         * presentTime  Time when the composited frame was presented on screen
+         */
+        TimelineItem mSurfaceFlingerPredictions;
+        TimelineItem mSurfaceFlingerActuals;
+        std::shared_ptr<TimeStats> mTimeStats;
+        const JankClassificationThresholds mJankClassificationThresholds;
+
+        // Collection of predictions and actual values sent over by Layers
+        std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
+
+        PredictionState mPredictionState = PredictionState::None;
+        // Bitmask for the type of jank
+        int32_t mJankType = JankType::None;
+        // Indicates if this frame was composited by the GPU or not
+        bool mGpuComposition = false;
+        // Enum for the type of present
+        FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+        // Enum for the type of finish
+        FrameReadyMetadata mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+        // Enum for the type of start
+        FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart;
+        // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
+        // timeline
+        nsecs_t mVsyncPeriod = 0;
+    };
+
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+                  JankClassificationThresholds thresholds = {});
     ~FrameTimeline() = default;
 
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
-    std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
-            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-            std::optional<int64_t> token) override;
-    void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame,
-                         SurfaceFrame::PresentState state) override;
-    void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+    std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+                                                             pid_t ownerPid, uid_t ownerUid,
+                                                             std::string layerName,
+                                                             std::string debugName) override;
+    void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
+    void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) override;
     void setSfPresent(nsecs_t sfPresentTime,
                       const std::shared_ptr<FenceTime>& presentFence) override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
@@ -288,67 +391,28 @@
     // Friend class for testing
     friend class android::frametimeline::FrameTimelineTest;
 
-    /*
-     * DisplayFrame should be used only internally within FrameTimeline.
-     */
-    struct DisplayFrame {
-        DisplayFrame();
-
-        int64_t token = ISurfaceComposer::INVALID_VSYNC_ID;
-
-        /* Usage of TimelineItem w.r.t SurfaceFlinger
-         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
-         * endTime      Time when SurfaceFlinger sends a composited frame to Display
-         * presentTime  Time when the composited frame was presented on screen
-         */
-        TimelineItem surfaceFlingerPredictions;
-        TimelineItem surfaceFlingerActuals;
-
-        // Collection of predictions and actual values sent over by Layers
-        std::vector<std::shared_ptr<SurfaceFrame>> surfaceFrames;
-
-        PredictionState predictionState = PredictionState::None;
-        JankType jankType = JankType::None; // Enum for the type of jank
-        int32_t jankMetadata = 0x0; // Additional details about the jank
-    };
-
     void flushPendingPresentFences() REQUIRES(mMutex);
     void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
-    // BaseTime is the smallest timestamp in a DisplayFrame.
-    // Used for dumping all timestamps relative to the oldest, making it easy to read.
-    nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
-    void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
-                          nsecs_t baseTime) REQUIRES(mMutex);
     void dumpAll(std::string& result);
     void dumpJank(std::string& result);
 
-    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
-    // enabled.
-    void traceDisplayFrame(const DisplayFrame& displayFrame) REQUIRES(mMutex);
-
     // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
     std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
     std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
             mPendingPresentFences GUARDED_BY(mMutex);
     std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
     TokenManager mTokenManager;
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     uint32_t mMaxDisplayFrames;
     std::shared_ptr<TimeStats> mTimeStats;
+    const pid_t mSurfaceFlingerPid;
+    const JankClassificationThresholds mJankClassificationThresholds;
     static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although
     // this number doesn't represent any bounds on the number of surface frames that can go in a
     // display frame, this is a good starting size for the vector so that we can avoid the
     // internal vector resizing that happens with push_back.
     static constexpr uint32_t kNumSurfaceFramesInitial = 10;
-    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
-    // compared to prediction, we don't treat it as a jank.
-    static constexpr nsecs_t kPresentThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-    static constexpr nsecs_t kDeadlineThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-    static constexpr nsecs_t kSFStartThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f53c4cc..4731f50 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -911,9 +911,8 @@
                 : std::make_optional(stateToCommit->frameTimelineVsyncId);
 
         mSurfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
-                                                                     mName, mTransactionName,
-                                                                     vsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(vsyncId, mOwnerPid, mOwnerUid,
+                                                                     mName, mTransactionName);
         mSurfaceFrame->setActualQueueTime(stateToCommit->postTime);
         // For transactions we set the acquire fence time to the post time as we
         // don't have a buffer. For BufferStateLayer it is overridden in
@@ -1066,7 +1065,8 @@
 
 void Layer::commitTransaction(const State& stateToCommit) {
     mDrawingState = stateToCommit;
-    mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame, PresentState::Presented);
+    mSurfaceFrame->setPresentState(PresentState::Presented);
+    mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame);
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bb897d5..d6023b6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -275,7 +275,8 @@
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
-        nsecs_t desiredPresentTime = -1;
+        nsecs_t desiredPresentTime = 0;
+        bool isAutoTimestamp = true;
 
         // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
         // be rendered around the layer.
@@ -444,7 +445,8 @@
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
                            nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
+                           bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
+                           uint64_t /* frameNumber */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -881,7 +883,7 @@
      */
     bool hasInputInfo() const;
 
-    uid_t getOwnerUid() { return mOwnerUid; }
+    virtual uid_t getOwnerUid() const { return mOwnerUid; }
 
     pid_t getOwnerPid() { return mOwnerPid; }
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index a959b9a..c291b7f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -235,7 +235,7 @@
 void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
     mCurrentFps = refreshRate.getFps().getIntValue();
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
@@ -247,7 +247,7 @@
     const auto& buffers = getOrCreateBuffers(*mCurrentFps);
     mFrame = (mFrame + 1) % buffers.size();
     auto buffer = buffers[mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 499daad..170933d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -86,9 +86,8 @@
 
 LayerHistory::~LayerHistory() = default;
 
-void LayerHistory::registerLayer(Layer* layer, Fps highRefreshRate, LayerVoteType type) {
-    const nsecs_t highRefreshRatePeriod = highRefreshRate.getPeriodNsecs();
-    auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type);
+void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+    auto info = std::make_unique<LayerInfo>(layer->getName(), type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
@@ -143,8 +142,8 @@
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        summary.push_back(
-                {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
+        summary.push_back({strong->getName(), strong->getOwnerUid(), vote.type, vote.fps,
+                           vote.seamlessness, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
             trace(layer, *info, vote.type, vote.fps.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 4214bab..bae9b5a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,7 +46,7 @@
     ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, Fps highRefreshRate, LayerVoteType type);
+    void registerLayer(Layer*, LayerVoteType type);
 
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 1c0065c..4b2862e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -33,10 +33,8 @@
 const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
 bool LayerInfo::sTraceEnabled = false;
 
-LayerInfo::LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
-                     LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
       : mName(name),
-        mHighRefreshRatePeriod(highRefreshRatePeriod),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps(0.0f)}),
         mRefreshRateHistory(name) {}
@@ -133,7 +131,7 @@
         }
 
         totalQueueTimeDeltas +=
-                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+                std::max(((it + 1)->queueTime - it->queueTime), kMinPeriodBetweenFrames);
         numFrames++;
 
         if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
@@ -147,7 +145,7 @@
         }
 
         totalPresentTimeDeltas +=
-                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+                std::max(((it + 1)->presetTime - it->presetTime), kMinPeriodBetweenFrames);
     }
 
     // Calculate the average frame time based on presentation timestamps. If those
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 9304e62..427cc9e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -70,8 +70,7 @@
         sRefreshRateConfigs = &refreshRateConfigs;
     }
 
-    LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
-              LayerHistory::LayerVoteType defaultVote);
+    LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
@@ -194,8 +193,9 @@
 
     const std::string mName;
 
-    // Used for sanitizing the heuristic data
-    const nsecs_t mHighRefreshRatePeriod;
+    // Used for sanitizing the heuristic data. If two frames are less than
+    // this period apart from each other they'll be considered as duplicates.
+    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
     LayerHistory::LayerVoteType mDefaultVote;
 
     LayerVote mLayerVote;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 47a4f42..7ff0ddf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -69,17 +69,33 @@
 
 // TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
 // and remove the EventThread from MessageQueue
-void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
-    if (mEventTube.getFd() >= 0) {
-        mLooper->removeFd(mEventTube.getFd());
+void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
+    auto& tube = mInjector.tube;
+
+    if (const int fd = tube.getFd(); fd >= 0) {
+        mLooper->removeFd(fd);
     }
 
-    mEvents = connection;
-    if (mEvents) {
-        mEvents->stealReceiveChannel(&mEventTube);
-        mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                       this);
+    if (connection) {
+        // The EventThreadConnection is retained when disabling injection, so avoid subsequently
+        // stealing invalid FDs. Note that the stolen FDs are kept open.
+        if (tube.getFd() < 0) {
+            connection->stealReceiveChannel(&tube);
+        } else {
+            ALOGW("Recycling channel for VSYNC injection.");
+        }
+
+        mLooper->addFd(
+                tube.getFd(), 0, Looper::EVENT_INPUT,
+                [](int, int, void* data) {
+                    reinterpret_cast<MessageQueue*>(data)->injectorCallback();
+                    return 1; // Keep registration.
+                },
+                this);
     }
+
+    std::lock_guard lock(mInjector.mutex);
+    mInjector.connection = std::move(connection);
 }
 
 void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
@@ -149,29 +165,31 @@
 
 void MessageQueue::invalidate() {
     ATRACE_CALL();
-    if (mEvents) {
-        mEvents->requestNextVsync();
-    } else {
-        std::lock_guard lock(mVsync.mutex);
-        mVsync.mScheduled = true;
-        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
-                                       mVsync.lastCallbackTime.count()});
+
+    {
+        std::lock_guard lock(mInjector.mutex);
+        if (CC_UNLIKELY(mInjector.connection)) {
+            ALOGD("%s while injecting VSYNC", __FUNCTION__);
+            mInjector.connection->requestNextVsync();
+            return;
+        }
     }
+
+    std::lock_guard lock(mVsync.mutex);
+    mVsync.mScheduled = true;
+    mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+                                   .readyDuration = 0,
+                                   .earliestVsync = mVsync.lastCallbackTime.count()});
 }
 
 void MessageQueue::refresh() {
     mHandler->dispatchRefresh();
 }
 
-int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
-    MessageQueue* queue = reinterpret_cast<MessageQueue*>(data);
-    return queue->eventReceiver(fd, events);
-}
-
-int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
+void MessageQueue::injectorCallback() {
     ssize_t n;
     DisplayEventReceiver::Event buffer[8];
-    while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
+    while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
@@ -180,8 +198,6 @@
             }
         }
     }
-    return 1;
 }
 
 } // namespace android::impl
-
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 99ce3a6..2934af0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -21,12 +21,11 @@
 #include <type_traits>
 #include <utility>
 
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-
+#include <android-base/thread_annotations.h>
 #include <gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
 
 #include "EventThread.h"
 #include "TracedOrdinal.h"
@@ -68,7 +67,7 @@
     virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                            std::chrono::nanoseconds workDuration) = 0;
     virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
-    virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
+    virtual void setInjector(sp<EventThreadConnection>) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
     virtual void invalidate() = 0;
@@ -99,7 +98,6 @@
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    sp<EventThreadConnection> mEvents;
 
     struct Vsync {
         frametimeline::TokenManager* tokenManager = nullptr;
@@ -113,14 +111,19 @@
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
-    Vsync mVsync;
+    struct Injector {
+        gui::BitTube tube;
+        std::mutex mutex;
+        sp<EventThreadConnection> connection GUARDED_BY(mutex);
+    };
 
-    gui::BitTube mEventTube;
+    Vsync mVsync;
+    Injector mInjector;
+
     sp<Handler> mHandler;
 
-    static int cb_eventReceiver(int fd, int events, void* data);
-    int eventReceiver(int fd, int events);
     void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+    void injectorCallback();
 
 public:
     ~MessageQueue() override = default;
@@ -128,7 +131,7 @@
     void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                    std::chrono::nanoseconds workDuration) override;
     void setDuration(std::chrono::nanoseconds workDuration) override;
-    void setEventConnection(const sp<EventThreadConnection>& connection) override;
+    void setInjector(sp<EventThreadConnection>) override;
 
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 4b7251b..5b78483 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -71,16 +71,84 @@
 
 std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
                                                                  nsecs_t displayPeriod) const {
-    auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
-    if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
-        std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
-        displayFramesQuot++;
-        displayFramesRem = 0;
+    auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+    if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+        std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+        quotient++;
+        remainder = 0;
     }
 
-    return {displayFramesQuot, displayFramesRem};
+    return {quotient, remainder};
 }
 
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+                                                    const RefreshRate& refreshRate,
+                                                    bool isSeamlessSwitch) const {
+    // Slightly prefer seamless switches.
+    constexpr float kSeamedSwitchPenalty = 0.95f;
+    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+    // If the layer wants Max, give higher score to the higher refresh rate
+    if (layer.vote == LayerVoteType::Max) {
+        const auto ratio =
+                refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
+        // use ratio^2 to get a lower score the more we get further from peak
+        return ratio * ratio;
+    }
+
+    const auto displayPeriod = refreshRate.getVsyncPeriod();
+    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+    if (layer.vote == LayerVoteType::ExplicitDefault) {
+        // Find the actual rate the layer will render, assuming
+        // that layerPeriod is the minimal time to render a frame
+        auto actualLayerPeriod = displayPeriod;
+        int multiplier = 1;
+        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+            multiplier++;
+            actualLayerPeriod = displayPeriod * multiplier;
+        }
+        return std::min(1.0f,
+                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+    }
+
+    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+        layer.vote == LayerVoteType::Heuristic) {
+        // Calculate how many display vsyncs we need to present a single frame for this
+        // layer
+        const auto [displayFramesQuotient, displayFramesRemainder] =
+                getDisplayFrames(layerPeriod, displayPeriod);
+        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+        if (displayFramesRemainder == 0) {
+            // Layer desired refresh rate matches the display rate.
+            return 1.0f * seamlessness;
+        }
+
+        if (displayFramesQuotient == 0) {
+            // Layer desired refresh rate is higher than the display rate.
+            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
+        }
+
+        // Layer desired refresh rate is lower than the display rate. Check how well it fits
+        // the cadence.
+        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+        int iter = 2;
+        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+            diff = diff - (displayPeriod - diff);
+            iter++;
+        }
+
+        return (1.0f / iter) * seamlessness;
+    }
+
+    return 0;
+}
+
+struct RefreshRateScore {
+    const RefreshRate* refreshRate;
+    float score;
+};
+
 const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
         const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
         GlobalSignals* outSignalsConsidered) const {
@@ -165,11 +233,11 @@
     }
 
     // Find the best refresh rate based on score
-    std::vector<std::pair<const RefreshRate*, float>> scores;
+    std::vector<RefreshRateScore> scores;
     scores.reserve(mAppRequestRefreshRates.size());
 
     for (const auto refreshRate : mAppRequestRefreshRates) {
-        scores.emplace_back(refreshRate, 0.0f);
+        scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
     }
 
     const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
@@ -184,12 +252,13 @@
         auto weight = layer.weight;
 
         for (auto i = 0u; i < scores.size(); i++) {
-            const bool isSeamlessSwitch =
-                    scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup();
+            const bool isSeamlessSwitch = scores[i].refreshRate->getConfigGroup() ==
+                    mCurrentRefreshRate->getConfigGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
-                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      formatLayerInfo(layer, weight).c_str(),
+                      scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
             }
@@ -198,7 +267,8 @@
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current config = %s",
-                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      formatLayerInfo(layer, weight).c_str(),
+                      scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
             }
@@ -209,18 +279,20 @@
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
             const bool isInPolicyForDefault = seamedLayers > 0
-                    ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
-                    : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+                    ? scores[i].refreshRate->getConfigGroup() ==
+                            mCurrentRefreshRate->getConfigGroup()
+                    : scores[i].refreshRate->getConfigGroup() == defaultConfig->getConfigGroup();
 
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
                 !layer.focused) {
                 ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->toString().c_str(), mCurrentRefreshRate->toString().c_str());
+                      scores[i].refreshRate->toString().c_str(),
+                      mCurrentRefreshRate->toString().c_str());
                 continue;
             }
 
-            bool inPrimaryRange =
-                    scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+            bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min,
+                                                                  policy->primaryRange.max);
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
                 // Only focused layers with ExplicitDefault frame rate settings are allowed to score
@@ -228,81 +300,11 @@
                 continue;
             }
 
-            // If the layer wants Max, give higher score to the higher refresh rate
-            if (layer.vote == LayerVoteType::Max) {
-                const auto ratio =
-                        scores[i].first->fps.getValue() / scores.back().first->fps.getValue();
-                // use ratio^2 to get a lower score the more we get further from peak
-                const auto layerScore = ratio * ratio;
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore;
-                continue;
-            }
-
-            const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
-            const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
-            if (layer.vote == LayerVoteType::ExplicitDefault) {
-                const auto layerScore = [&]() {
-                    // Find the actual rate the layer will render, assuming
-                    // that layerPeriod is the minimal time to render a frame
-                    auto actualLayerPeriod = displayPeriod;
-                    int multiplier = 1;
-                    while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
-                        multiplier++;
-                        actualLayerPeriod = displayPeriod * multiplier;
-                    }
-                    return std::min(1.0f,
-                                    static_cast<float>(layerPeriod) /
-                                            static_cast<float>(actualLayerPeriod));
-                }();
-
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore;
-                continue;
-            }
-
-            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
-                layer.vote == LayerVoteType::Heuristic) {
-                const auto layerScore = [&] {
-                    // Calculate how many display vsyncs we need to present a single frame for this
-                    // layer
-                    const auto [displayFramesQuot, displayFramesRem] =
-                            getDisplayFrames(layerPeriod, displayPeriod);
-                    static constexpr size_t MAX_FRAMES_TO_FIT =
-                            10; // Stop calculating when score < 0.1
-                    if (displayFramesRem == 0) {
-                        // Layer desired refresh rate matches the display rate.
-                        return 1.0f;
-                    }
-
-                    if (displayFramesQuot == 0) {
-                        // Layer desired refresh rate is higher the display rate.
-                        return (static_cast<float>(layerPeriod) /
-                                static_cast<float>(displayPeriod)) *
-                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
-                    }
-
-                    // Layer desired refresh rate is lower the display rate. Check how well it fits
-                    // the cadence
-                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
-                    int iter = 2;
-                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
-                        diff = diff - (displayPeriod - diff);
-                        iter++;
-                    }
-
-                    return 1.0f / iter;
-                }();
-                // Slightly prefer seamless switches.
-                constexpr float kSeamedSwitchPenalty = 0.95f;
-                const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore * seamlessness;
-                continue;
-            }
+            const auto layerScore =
+                    calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+            ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+                  scores[i].refreshRate->getName().c_str(), layerScore);
+            scores[i].score += weight * layerScore;
         }
     }
 
@@ -317,7 +319,7 @@
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
-                        [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+                        [](RefreshRateScore score) { return score.score == 0; })) {
             ALOGV("layers not scored - choose %s",
                   getMaxRefreshRateByPolicyLocked().getName().c_str());
             return getMaxRefreshRateByPolicyLocked();
@@ -342,11 +344,110 @@
     return *bestRefreshRate;
 }
 
+std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
+groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
+    std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
+    for (const auto& layer : layers) {
+        auto iter = layersByUid.emplace(layer.ownerUid,
+                                        std::vector<const RefreshRateConfigs::LayerRequirement*>());
+        auto& layersWithSameUid = iter.first->second;
+        layersWithSameUid.push_back(&layer);
+    }
+
+    // Remove uids that can't have a frame rate override
+    for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
+        const auto& layersWithSameUid = iter->second;
+        bool skipUid = false;
+        for (const auto& layer : layersWithSameUid) {
+            if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
+                layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
+                skipUid = true;
+                break;
+            }
+        }
+        if (skipUid) {
+            iter = layersByUid.erase(iter);
+        } else {
+            ++iter;
+        }
+    }
+
+    return layersByUid;
+}
+
+std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
+        const AllRefreshRatesMapType& refreshRates) {
+    std::vector<RefreshRateScore> scores;
+    scores.reserve(refreshRates.size());
+    for (const auto& [ignored, refreshRate] : refreshRates) {
+        scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
+    }
+    std::sort(scores.begin(), scores.end(),
+              [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
+    return scores;
+}
+
+RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
+        const std::vector<LayerRequirement>& layers, Fps displayFrameRate) const {
+    ATRACE_CALL();
+    ALOGV("getFrameRateOverrides %zu layers", layers.size());
+
+    std::lock_guard lock(mLock);
+    std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+    std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
+            groupLayersByUid(layers);
+    UidToFrameRateOverride frameRateOverrides;
+    for (const auto& [uid, layersWithSameUid] : layersByUid) {
+        for (auto& score : scores) {
+            score.score = 0;
+        }
+
+        for (const auto& layer : layersWithSameUid) {
+            if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+                continue;
+            }
+
+            LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+                                layer->vote != LayerVoteType::ExplicitExactOrMultiple);
+            for (RefreshRateScore& score : scores) {
+                const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
+                                                                  /*isSeamlessSwitch*/ true);
+                score.score += layer->weight * layerScore;
+            }
+        }
+
+        // We just care about the refresh rates which are a divider of the
+        // display refresh rate
+        auto iter =
+                std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
+                    return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0;
+                });
+        scores.erase(iter, scores.end());
+
+        // If we never scored any layers, we don't have a preferred frame rate
+        if (std::all_of(scores.begin(), scores.end(),
+                        [](const RefreshRateScore& score) { return score.score == 0; })) {
+            continue;
+        }
+
+        // Now that we scored all the refresh rates we need to pick the one that got the highest
+        // score.
+        const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+
+        // If the nest refresh rate is the current one, we don't have an override
+        if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
+            frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
+        }
+    }
+
+    return frameRateOverrides;
+}
+
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
     constexpr auto EPSILON = 0.001f;
-    const RefreshRate* bestRefreshRate = begin->first;
-    float max = begin->second;
+    const RefreshRate* bestRefreshRate = begin->refreshRate;
+    float max = begin->score;
     for (auto i = begin; i != end; ++i) {
         const auto [refreshRate, score] = *i;
         ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
@@ -362,10 +463,6 @@
     return bestRefreshRate;
 }
 
-const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
-    return mRefreshRates;
-}
-
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getMinRefreshRateByPolicyLocked();
@@ -649,50 +746,22 @@
     return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
 }
 
-void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
-    if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
-        return;
-    }
-
-    std::lock_guard lock(mLock);
-    if (frameRateOverride.frameRateHz != 0) {
-        mPreferredRefreshRateForUid[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
-    } else {
-        mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
-    }
-}
-
-int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
-    std::lock_guard lock(mLock);
-
-    const auto iter = mPreferredRefreshRateForUid.find(uid);
-    if (iter == mPreferredRefreshRateForUid.end()) {
-        return 1;
-    }
-
+int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
     constexpr float kThreshold = 0.1f;
-    const auto refreshRateHz = iter->second;
-    const auto numPeriods = mCurrentRefreshRate->getFps().getValue() / refreshRateHz.getValue();
+    const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
-        return 1;
+        return 0;
     }
 
     return static_cast<int>(numPeriodsRounded);
 }
 
-std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
     std::lock_guard lock(mLock);
-    std::vector<FrameRateOverride> overrides;
-    overrides.reserve(mPreferredRefreshRateForUid.size());
-
-    for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
-        overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
-    }
-
-    return overrides;
+    return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
 }
 
 void RefreshRateConfigs::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ec7ffe5..6c83f8b 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -221,6 +221,8 @@
     struct LayerRequirement {
         // Layer's name. Used for debugging purposes.
         std::string name;
+        // Layer's owner uid
+        uid_t ownerUid = static_cast<uid_t>(-1);
         // Layer vote type.
         LayerVoteType vote = LayerVoteType::NoVote;
         // Layer's desired refresh rate, if applicable.
@@ -261,9 +263,6 @@
                                           GlobalSignals* outSignalsConsidered = nullptr) const
             EXCLUDES(mLock);
 
-    // Returns all the refresh rates supported by the device. This won't change at runtime.
-    const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
-
     // Returns the lowest refresh rate supported by the device. This won't change at runtime.
     const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
 
@@ -319,18 +318,16 @@
     // refresh rates.
     KernelIdleTimerAction getIdleTimerAction() const;
 
-    // Stores the preferred refresh rate that an app should run at.
-    // FrameRateOverride.refreshRateHz == 0 means no preference.
-    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mLock);
-
     // Returns a divider for the current refresh rate
-    int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+    int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock);
+
+    // Returns the frame rate override for each uid
+    using UidToFrameRateOverride = std::map<uid_t, Fps>;
+    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+                                                 Fps displayFrameRate) const EXCLUDES(mLock);
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
-    // Returns the current frame rate overrides
-    std::vector<FrameRateOverride> getFrameRateOverrides() EXCLUDES(mLock);
-
 private:
     friend class RefreshRateConfigsTest;
 
@@ -367,6 +364,16 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValid(const Policy& policy);
 
+    // Return the display refresh rate divider to match the layer
+    // frame rate, or 0 if the display refresh rate is not a multiple of the
+    // layer refresh rate.
+    static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+
+    // calculates a score for a layer. Used to determine the display refresh rate
+    // and the frame rate override for certains applications.
+    float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
+                                    bool isSeamlessSwitch) const REQUIRES(mLock);
+
     // The list of refresh rates, indexed by display config ID. This must not change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates;
@@ -388,10 +395,6 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
-    // A mapping between a UID and a preferred refresh rate that this app would
-    // run at.
-    std::unordered_map<uid_t, Fps> mPreferredRefreshRateForUid GUARDED_BY(mLock);
-
     // The min and max refresh rates supported by the device.
     // This will not change at runtime.
     const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 07411b0..ab8bff1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,8 +206,32 @@
                                                        readyDuration, traceVsync, name);
 }
 
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+    std::lock_guard lock(mFeatureStateLock);
+    {
+        const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+        if (iter != mFrameRateOverridesFromBackdoor.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    {
+        const auto iter = mFeatures.frameRateOverrides.find(uid);
+        if (iter != mFeatures.frameRateOverrides.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    return std::nullopt;
+}
+
 bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
-    const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+    const auto frameRate = getFrameRateOverride(uid);
+    if (!frameRate.has_value()) {
+        return true;
+    }
+
+    const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
     if (divider <= 1) {
         return true;
     }
@@ -290,8 +314,19 @@
     thread->onScreenReleased();
 }
 
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                            std::vector<FrameRateOverride> overrides) {
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+    std::vector<FrameRateOverride> overrides;
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+        }
+        for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+            if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+                overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+            }
+        }
+    }
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -303,9 +338,11 @@
 
 void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
                                               HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
-    std::lock_guard<std::mutex> lock(mFeatureStateLock);
-    // Cache the last reported config for primary display.
-    mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        // Cache the last reported config for primary display.
+        mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    }
     onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
 }
 
@@ -399,6 +436,10 @@
                                                     impl::EventThread::InterceptVSyncsCallback(),
                                                     impl::EventThread::ThrottleVsyncCallback());
 
+        // EventThread does not dispatch VSYNC unless the display is connected and powered on.
+        eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
+        eventThread->onScreenAcquired();
+
         mInjectorConnectionHandle = createConnection(std::move(eventThread));
     }
 
@@ -510,23 +551,19 @@
 void Scheduler::registerLayer(Layer* layer) {
     if (!mLayerHistory) return;
 
-    const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
-
     if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
-        mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::NoVote);
+        mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::NoVote);
     } else if (!mOptions.useContentDetection) {
         // If the content detection feature is off, all layers are registered at Max. We still keep
         // the layer history, since we use it for other features (like Frame Rate API), so layers
         // still need to be registered.
-        mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::Max);
+        mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Max);
     } else {
         if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
             // Running Wallpaper at Min is considered as part of content detection.
-            mLayerHistory->registerLayer(layer, maxFps,
-                                         scheduler::LayerHistory::LayerVoteType::Min);
+            mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Min);
         } else {
-            mLayerHistory->registerLayer(layer, maxFps,
-                                         scheduler::LayerHistory::LayerVoteType::Heuristic);
+            mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Heuristic);
         }
     }
 }
@@ -550,7 +587,10 @@
     ATRACE_CALL();
 
     scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     HwcConfigIndexType newConfigId;
+    bool frameRateChanged;
+    bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (mFeatures.contentRequirements == summary) {
@@ -558,22 +598,32 @@
         }
         mFeatures.contentRequirements = summary;
 
-        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return;
+            frameRateChanged = false;
+        } else {
+            mFeatures.configId = newConfigId;
+            frameRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
+    }
+    if (frameRateChanged) {
         auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ConfigEvent::None
                                                                     : ConfigEvent::Changed);
     }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
 }
 
 void Scheduler::resetIdleTimer() {
@@ -666,6 +716,21 @@
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
                   toContentDetectionString(mOptions.useContentDetection),
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+    {
+        std::lock_guard lock(mFeatureStateLock);
+        StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+
+        StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+        for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+    }
 }
 
 void Scheduler::dumpVsync(std::string& s) const {
@@ -677,9 +742,35 @@
     mVsyncSchedule.dispatch->dump(s);
 }
 
+bool Scheduler::updateFrameRateOverrides(
+        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+    if (consideredSignals.touch) {
+        const bool changed = !mFeatures.frameRateOverrides.empty();
+        mFeatures.frameRateOverrides.clear();
+        return changed;
+    }
+
+    if (!consideredSignals.idle) {
+        const auto frameRateOverrides =
+                mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+                                                          displayRefreshRate);
+        if (!std::equal(mFeatures.frameRateOverrides.begin(), mFeatures.frameRateOverrides.end(),
+                        frameRateOverrides.begin(), frameRateOverrides.end(),
+                        [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+                            return a.first == b.first && a.second.equalsWithMargin(b.second);
+                        })) {
+            mFeatures.frameRateOverrides = frameRateOverrides;
+            return true;
+        }
+    }
+    return false;
+}
+
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     HwcConfigIndexType newConfigId;
+    bool refreshRateChanged = false;
+    bool frameRateOverridesChanged;
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -688,20 +779,32 @@
         }
         *currentState = newState;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return consideredSignals.touch;
+        } else {
+            mFeatures.configId = newConfigId;
+            refreshRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
     }
-    const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                         consideredSignals.idle ? ConfigEvent::None
-                                                                : ConfigEvent::Changed);
+    if (refreshRateChanged) {
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+
+        mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                             consideredSignals.idle ? ConfigEvent::None
+                                                                    : ConfigEvent::Changed);
+    }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
     return consideredSignals.touch;
 }
 
@@ -775,4 +878,17 @@
     }
 }
 
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+        return;
+    }
+
+    std::lock_guard lock(mFeatureStateLock);
+    if (frameRateOverride.frameRateHz != 0.f) {
+        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+    } else {
+        mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f16e1f9..052c425 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -60,6 +60,7 @@
                                    scheduler::RefreshRateConfigEvent) = 0;
     virtual void repaintEverythingForHWC() = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
+    virtual void triggerOnFrameRateOverridesChanged() = 0;
 
 protected:
     ~ISchedulerCallback() = default;
@@ -93,8 +94,7 @@
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
-    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId,
-                                     std::vector<FrameRateOverride>);
+    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId);
 
     // Modifies work duration in the event thread.
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
@@ -169,6 +169,10 @@
                                                            std::chrono::nanoseconds readyDuration,
                                                            bool traceVsync = true);
 
+    // Stores the preferred refresh rate that an app should run at.
+    // FrameRateOverride.refreshRateHz == 0 means no preference.
+    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFeatureStateLock);
+
 private:
     friend class TestableScheduler;
 
@@ -226,6 +230,10 @@
             REQUIRES(mFeatureStateLock);
 
     void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+    bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
+                                  Fps displayRefreshRate) REQUIRES(mFeatureStateLock);
+
+    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFeatureStateLock);
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
@@ -264,7 +272,7 @@
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
-    std::mutex mFeatureStateLock;
+    mutable std::mutex mFeatureStateLock;
 
     struct {
         TimerState idleTimer = TimerState::Reset;
@@ -285,6 +293,7 @@
         };
 
         std::optional<ConfigChangedParams> cachedConfigChangedParams;
+        scheduler::RefreshRateConfigs::UidToFrameRateOverride frameRateOverrides;
     } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
@@ -295,6 +304,11 @@
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
     const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+
+    // mappings between a UID and a preferred refresh rate that this app would
+    // run at.
+    scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+            GUARDED_BY(mFeatureStateLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 8431323..cb57aea 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -16,14 +16,19 @@
 
 #include "VsyncConfiguration.h"
 
-#include <cutils/properties.h>
-
+#include <chrono>
+#include <cinttypes>
 #include <optional>
 
+#include <cutils/properties.h>
+#include <log/log.h>
+
 #include "SurfaceFlingerProperties.h"
 
 namespace {
 
+using namespace std::chrono_literals;
+
 std::optional<nsecs_t> getProperty(const char* name) {
     char value[PROPERTY_VALUE_MAX];
     property_get(name, value, "-1");
@@ -31,19 +36,6 @@
     return std::nullopt;
 }
 
-std::vector<android::Fps> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<android::Fps> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate->getFps());
-    }
-
-    return refreshRates;
-}
-
 } // namespace
 
 namespace android::scheduler::impl {
@@ -51,25 +43,19 @@
 VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
 
 PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
-    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
-                                   [&fps](const std::pair<Fps, VsyncConfigSet>& candidateFps) {
-                                       return fps.equalsWithMargin(candidateFps.first);
-                                   });
+    std::lock_guard lock(mLock);
+    return getConfigsForRefreshRateLocked(fps);
+}
 
-    if (iter != mOffsets.end()) {
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+    const auto iter = mOffsetsCache.find(fps);
+    if (iter != mOffsetsCache.end()) {
         return iter->second;
     }
 
-    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
-    // In this case just construct the offset.
-    ALOGW("Can't find offset for %s", to_string(fps).c_str());
-    return constructOffsets(fps.getPeriodNsecs());
-}
-
-void VsyncConfiguration::initializeOffsets(const std::vector<Fps>& refreshRates) {
-    for (const auto fps : refreshRates) {
-        mOffsets.emplace(fps, constructOffsets(fps.getPeriodNsecs()));
-    }
+    const auto offset = constructOffsets(fps.getPeriodNsecs());
+    mOffsetsCache[fps] = offset;
+    return offset;
 }
 
 void VsyncConfiguration::dump(std::string& result) const {
@@ -98,10 +84,8 @@
                   earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
 }
 
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
-                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                     sysprop::vsync_event_phase_offset_ns(1000000),
+PhaseOffsets::PhaseOffsets(Fps currentRefreshRate)
+      : PhaseOffsets(currentRefreshRate, sysprop::vsync_event_phase_offset_ns(1000000),
                      sysprop::vsync_sf_event_phase_offset_ns(1000000),
                      getProperty("debug.sf.early_phase_offset_ns"),
                      getProperty("debug.sf.early_gl_phase_offset_ns"),
@@ -121,15 +105,17 @@
                      getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
                              .value_or(std::numeric_limits<nsecs_t>::max())) {}
 
-PhaseOffsets::PhaseOffsets(
-        const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
-        nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
-        std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
-        std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
-        nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+PhaseOffsets::PhaseOffsets(Fps currentFps, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> earlySfOffsetNs,
+                           std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                           std::optional<nsecs_t> earlyAppOffsetNs,
+                           std::optional<nsecs_t> earlyGpuAppOffsetNs,
+                           nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+                           nsecs_t thresholdForNextVsync)
       : VsyncConfiguration(currentFps),
         mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
         mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
@@ -143,9 +129,7 @@
         mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
         mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
         mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
-        mThresholdForNextVsync(thresholdForNextVsync) {
-    initializeOffsets(refreshRates);
-}
+        mThresholdForNextVsync(thresholdForNextVsync) {}
 
 PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
     if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
@@ -361,10 +345,8 @@
     };
 }
 
-WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
-                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                     getProperty("debug.sf.late.sf.duration").value_or(-1),
+WorkDuration::WorkDuration(Fps currentRefreshRate)
+      : WorkDuration(currentRefreshRate, getProperty("debug.sf.late.sf.duration").value_or(-1),
                      getProperty("debug.sf.late.app.duration").value_or(-1),
                      getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
                      getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
@@ -373,17 +355,15 @@
     validateSysprops();
 }
 
-WorkDuration::WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
-                           nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
+                           nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
                            nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration)
-      : VsyncConfiguration(currentFps),
+      : VsyncConfiguration(currentRefreshRate),
         mSfDuration(sfDuration),
         mAppDuration(appDuration),
         mSfEarlyDuration(sfEarlyDuration),
         mAppEarlyDuration(appEarlyDuration),
         mSfEarlyGpuDuration(sfEarlyGpuDuration),
-        mAppEarlyGpuDuration(appEarlyGpuDuration) {
-    initializeOffsets(refreshRates);
-}
+        mAppEarlyGpuDuration(appEarlyGpuDuration) {}
 
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index a120e97..d9d206d 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -16,12 +16,14 @@
 
 #pragma once
 
+#include <mutex>
+#include <type_traits>
 #include <unordered_map>
+#include <vector>
 
 #include <utils/Timers.h>
 
 #include "Fps.h"
-#include "RefreshRateConfigs.h"
 #include "VsyncModulator.h"
 
 namespace android::scheduler {
@@ -39,9 +41,9 @@
     virtual ~VsyncConfiguration() = default;
     virtual VsyncConfigSet getCurrentConfigs() const = 0;
     virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
+    virtual void reset() = 0;
 
     virtual void setRefreshRateFps(Fps fps) = 0;
-
     virtual void dump(std::string& result) const = 0;
 };
 
@@ -57,26 +59,39 @@
     explicit VsyncConfiguration(Fps currentFps);
 
     // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override;
+    VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override EXCLUDES(mLock);
 
     // Returns early, early GL, and late offsets for Apps and SF.
-    VsyncConfigSet getCurrentConfigs() const override {
-        return getConfigsForRefreshRate(mRefreshRateFps);
+    VsyncConfigSet getCurrentConfigs() const override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return getConfigsForRefreshRateLocked(mRefreshRateFps);
+    }
+
+    // Cleans the internal cache.
+    void reset() override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        mOffsetsCache.clear();
     }
 
     // This function should be called when the device is switching between different
     // refresh rates, to properly update the offsets.
-    void setRefreshRateFps(Fps fps) override { mRefreshRateFps = fps; }
+    void setRefreshRateFps(Fps fps) override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        mRefreshRateFps = fps;
+    }
 
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
 protected:
-    void initializeOffsets(const std::vector<Fps>& refreshRates);
     virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
 
-    std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets> mOffsets;
-    std::atomic<Fps> mRefreshRateFps;
+    VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
+
+    mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
+            mOffsetsCache GUARDED_BY(mLock);
+    std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+    mutable std::mutex mLock;
 };
 
 /*
@@ -85,13 +100,13 @@
  */
 class PhaseOffsets : public VsyncConfiguration {
 public:
-    explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+    explicit PhaseOffsets(Fps currentRefreshRate);
 
 protected:
     // Used for unit tests
-    PhaseOffsets(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
-                 nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
-                 std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+    PhaseOffsets(Fps currentRefreshRate, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                 std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                 std::optional<nsecs_t> earlyAppOffsetNs,
                  std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
                  nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
                  std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
@@ -128,13 +143,12 @@
  */
 class WorkDuration : public VsyncConfiguration {
 public:
-    explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+    explicit WorkDuration(Fps currentRefrshRate);
 
 protected:
     // Used for unit tests
-    WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
-                 nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
-                 nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+    WorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+                 nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
 
 private:
     VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d9c8457..5d08328 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -339,7 +339,7 @@
         mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(mFactory.createFrameTracer()),
-        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats)),
+        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -710,16 +710,17 @@
     // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
     mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
             renderengine::RenderEngineCreationArgs::Builder()
-                .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
-                .setImageCacheSize(maxFrameBufferAcquiredBuffers)
-                .setUseColorManagerment(useColorManagement)
-                .setEnableProtectedContext(enable_protected_contents(false))
-                .setPrecacheToneMapperShaderOnly(false)
-                .setSupportsBackgroundBlur(mSupportsBlur)
-                .setContextPriority(useContextPriority
-                        ? renderengine::RenderEngine::ContextPriority::HIGH
-                        : renderengine::RenderEngine::ContextPriority::MEDIUM)
-                .build()));
+                    .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+                    .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+                    .setUseColorManagerment(useColorManagement)
+                    .setEnableProtectedContext(enable_protected_contents(false))
+                    .setPrecacheToneMapperShaderOnly(false)
+                    .setSupportsBackgroundBlur(mSupportsBlur)
+                    .setContextPriority(
+                            useContextPriority
+                                    ? renderengine::RenderEngine::ContextPriority::REALTIME
+                                    : renderengine::RenderEngine::ContextPriority::MEDIUM)
+                    .build()));
     mCompositionEngine->setTimeStats(mTimeStats);
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
     mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
@@ -1430,8 +1431,7 @@
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
-                                                   : nullptr);
+            mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
         }
     }).wait();
 
@@ -1871,7 +1871,7 @@
         const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
         ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
 
-        mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+        mFrameTimeline->setSfWakeUp(vsyncId, frameStart, stats.vsyncPeriod);
 
         refreshNeeded = handleMessageTransaction();
         refreshNeeded |= handleMessageInvalidate();
@@ -2620,8 +2620,14 @@
             setPowerModeInternal(display, hal::PowerMode::ON);
 
             // TODO(b/175678251) Call a listener instead.
-            if (mRefreshRateOverlay) {
-                mRefreshRateOverlay->reset();
+            if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+                const auto displayId = currentState.physical->id;
+                const auto configs = getHwComposer().getConfigs(displayId);
+                mVsyncConfiguration->reset();
+                updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate());
+                if (mRefreshRateOverlay) {
+                    mRefreshRateOverlay->reset();
+                }
             }
         }
         return;
@@ -2884,12 +2890,21 @@
                                        Scheduler::ConfigEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
-    // Sheduler::chooseRefreshRateForContent
+    // Scheduler::chooseRefreshRateForContent
 
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
     changeRefreshRateLocked(refreshRate, event);
 }
 
+void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
+    PhysicalDisplayId displayId = [&]() {
+        ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+        return getDefaultDisplayDeviceLocked()->getPhysicalId();
+    }();
+
+    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+}
+
 void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) {
     if (mScheduler) {
         // In practice it's not allowed to hotplug in/out the primary display once it's been
@@ -2908,7 +2923,7 @@
             std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate.getFps(),
                                                           hal::PowerMode::OFF);
 
-    mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate.getFps());
     mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
 
     // start the EventThread
@@ -3247,8 +3262,9 @@
                 applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                       transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.buffer, transaction.postTime,
-                                      transaction.privileged, transaction.hasListenerCallbacks,
+                                      transaction.isAutoTimestamp, transaction.buffer,
+                                      transaction.postTime, transaction.privileged,
+                                      transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id, /*isMainThread*/ true);
                 transactionQueue.pop();
@@ -3279,7 +3295,7 @@
     bool ready = true;
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
-    if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+    if (desiredPresentTime > 0 && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
         ready = false;
     }
@@ -3292,14 +3308,18 @@
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
           ready = false;
         }
+        sp<Layer> layer = nullptr;
+        if (s.surface) {
+            layer = fromHandleLocked(s.surface).promote();
+        } else {
+            ALOGW("Transaction with buffer, but no Layer?");
+            continue;
+        }
+        if (layer && !mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
+            ATRACE_NAME("!isVsyncValidForUid");
+            ready = false;
+        }
         if (updateTransactionCounters) {
-              sp<Layer> layer = nullptr;
-              if (s.surface) {
-                layer = fromHandleLocked(s.surface).promote();
-              } else {
-                ALOGW("Transaction with buffer, but no Layer?");
-                continue;
-              }
               // See BufferStateLayer::mPendingBufferTransactions
               if (layer) layer->incrementPendingBufferCount();
 
@@ -3312,7 +3332,7 @@
         int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
@@ -3342,24 +3362,30 @@
     const bool pendingTransactions = itr != mTransactionQueues.end();
     // Expected present time is computed and cached on invalidate, so it may be stale.
     if (!pendingTransactions) {
-        mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
+        // The transaction might arrive just before the next vsync but after
+        // invalidate was called. In that case we need to get the next vsync
+        // afterwards.
+        const auto referenceTime = std::max(mExpectedPresentTime.load(), systemTime());
+        mExpectedPresentTime = calculateExpectedPresentTime(referenceTime);
     }
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
 
-    if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states, true)) {
+    if (pendingTransactions ||
+        !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) {
         mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
-                                               desiredPresentTime, uncacheBuffer, postTime,
-                                               privileged, hasListenerCallbacks, listenerCallbacks,
-                                               originPid, originUid, transactionId);
+                                               desiredPresentTime, isAutoTimestamp, uncacheBuffer,
+                                               postTime, privileged, hasListenerCallbacks,
+                                               listenerCallbacks, originPid, originUid,
+                                               transactionId);
         setTransactionFlags(eTransactionFlushNeeded);
         return NO_ERROR;
     }
 
     applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
-                          desiredPresentTime, uncacheBuffer, postTime, privileged,
+                          desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged,
                           hasListenerCallbacks, listenerCallbacks, originPid, originUid,
                           transactionId, /*isMainThread*/ false);
     return NO_ERROR;
@@ -3369,9 +3395,10 @@
         int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags,
         const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
-        const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
-        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-        int originPid, int originUid, uint64_t transactionId, bool isMainThread) {
+        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime,
+        bool privileged, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+        uint64_t transactionId, bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3405,12 +3432,13 @@
     std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |=
-                setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
-                                     privileged, listenerCallbacksWithSurfaces);
+        clientStateFlags |= setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime,
+                                                 isAutoTimestamp, postTime, privileged,
+                                                 listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
-                mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+                mScheduler->recordLayerHistory(layer.get(),
+                                               isAutoTimestamp ? 0 : desiredPresentTime,
                                                LayerHistory::LayerUpdateType::AnimationTX);
             }
         }
@@ -3585,7 +3613,7 @@
 
 uint32_t SurfaceFlinger::setClientStateLocked(
         int64_t frameTimelineVsyncId, const ComposerState& composerState,
-        int64_t desiredPresentTime, int64_t postTime, bool privileged,
+        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
         std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
 
@@ -3896,8 +3924,8 @@
                 ? s.frameNumber
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
-        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
-                             frameNumber)) {
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
+                             s.cachedBuffer, frameNumber)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4175,7 +4203,7 @@
     d.height = 0;
     displays.add(d);
     setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
-                        mPendingInputWindowCommands, -1, {}, false, {},
+                        mPendingInputWindowCommands, systemTime(), true, {}, false, {},
                         0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
@@ -5281,12 +5309,17 @@
                 return NO_ERROR;
             }
             case 1035: {
-                n = data.readInt32();
+                const int newConfigId = data.readInt32();
                 mDebugDisplayConfigSetByBackdoor = false;
-                const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
-                if (n >= 0 && n < numConfigs) {
+                const auto displayId = getInternalDisplayId();
+                if (!displayId) {
+                    ALOGE("No internal display found.");
+                    return NO_ERROR;
+                }
+                const auto numConfigs = getHwComposer().getConfigs(*displayId).size();
+                if (newConfigId >= 0 && newConfigId < numConfigs) {
                     const auto displayToken = getInternalDisplayToken();
-                    status_t result = setActiveConfig(displayToken, n);
+                    status_t result = setActiveConfig(displayToken, newConfigId);
                     if (result != NO_ERROR) {
                         return result;
                     }
@@ -5340,13 +5373,10 @@
 
                 auto inUid = static_cast<uid_t>(data.readInt32());
                 const auto refreshRate = data.readFloat();
-                mRefreshRateConfigs->setPreferredRefreshRateForUid(
-                        FrameRateOverride{inUid, refreshRate});
-                const auto mappings = mRefreshRateConfigs->getFrameRateOverrides();
-                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId,
-                                                        std::move(mappings));
-            }
+                mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
+                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
                 return NO_ERROR;
+            }
         }
     }
     return err;
@@ -6355,10 +6385,7 @@
 }
 
 int SurfaceFlinger::getGPUContextPriority() {
-    // TODO(b/168740533): This is a proof of concept. Once REAL time priority is available
-    // in EGL, we can return it in RenderEngine and propagate it to SurfaceFlinger. Until
-    // then return IntentFilter.SYSTEM_HIGH_PRIORITY.
-    return 1000;
+    return getRenderEngine().getContextPriority();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 542ba98..db75312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -341,7 +341,7 @@
 
     virtual uint32_t setClientStateLocked(
             int64_t frameTimelineVsyncId, const ComposerState& composerState,
-            int64_t desiredPresentTime, int64_t postTime, bool privileged,
+            int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
             std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
             REQUIRES(mStateLock);
     virtual void commitTransactionLocked();
@@ -434,8 +434,9 @@
     struct TransactionState {
         TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         int64_t desiredPresentTime, bool isAutoTimestamp,
+                         const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged,
+                         bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
               : frameTimelineVsyncId(frameTimelineVsyncId),
@@ -443,6 +444,7 @@
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
+                isAutoTimestamp(isAutoTimestamp),
                 buffer(uncacheBuffer),
                 postTime(postTime),
                 privileged(privileged),
@@ -457,6 +459,7 @@
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
+        const bool isAutoTimestamp;
         client_cache_t buffer;
         const int64_t postTime;
         bool privileged;
@@ -520,8 +523,8 @@
                                  const Vector<DisplayState>& displays, uint32_t flags,
                                  const sp<IBinder>& applyToken,
                                  const InputWindowCommands& inputWindowCommands,
-                                 int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                                 bool hasListenerCallbacks,
+                                 int64_t desiredPresentTime, bool isAutoTimestamp,
+                                 const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override;
     void bootFinished() override;
@@ -641,6 +644,8 @@
     void repaintEverythingForHWC() override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
+    // Called when the frame rate override list changed to trigger an event.
+    void triggerOnFrameRateOverridesChanged() override;
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer();
     // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
@@ -721,7 +726,7 @@
     void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
-                               const int64_t desiredPresentTime,
+                               const int64_t desiredPresentTime, bool isAutoTimestamp,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bc487ac..4a75180 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -56,11 +56,11 @@
 }
 
 std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
-        const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+        Fps currentRefreshRate) {
     if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
-        return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
+        return std::make_unique<scheduler::impl::WorkDuration>(currentRefreshRate);
     } else {
-        return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+        return std::make_unique<scheduler::impl::PhaseOffsets>(currentRefreshRate);
     }
 }
 
@@ -136,8 +136,8 @@
 }
 
 std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
-        std::shared_ptr<TimeStats> timeStats) {
-    return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats);
+        std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) {
+    return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats, surfaceFlingerPid);
 }
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index fc45fa9..24148dd 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,7 @@
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<MessageQueue> createMessageQueue() override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs&) override;
+            Fps currentRefreshRate) override;
     std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                ISchedulerCallback&) override;
     sp<SurfaceInterceptor> createSurfaceInterceptor() override;
@@ -56,7 +56,7 @@
     sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) override;
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index deb1b52..885297f 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "Fps.h"
+
 #include <cutils/compiler.h>
 #include <utils/StrongPointer.h>
 
@@ -76,7 +78,7 @@
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs&) = 0;
+            Fps currentRefreshRate) = 0;
     virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                        ISchedulerCallback&) = 0;
     virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
@@ -108,7 +110,7 @@
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) = 0;
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
 
 protected:
     ~Factory() = default;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 2405884..f4a0319 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -680,19 +680,18 @@
 static void updateJankPayload(T& t, int32_t reasons) {
     t.jankPayload.totalFrames++;
 
-    static const constexpr int32_t kValidJankyReason =
-            JankType::SurfaceFlingerDeadlineMissed |
-            JankType::SurfaceFlingerGpuDeadlineMissed |
-            JankType::AppDeadlineMissed | JankType::Display;
+    static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+            JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+            JankType::DisplayHAL;
     if (reasons & kValidJankyReason) {
         t.jankPayload.totalJankyFrames++;
-        if ((reasons & JankType::SurfaceFlingerDeadlineMissed) != 0) {
+        if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
             t.jankPayload.totalSFLongCpu++;
         }
         if ((reasons & JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
             t.jankPayload.totalSFLongGpu++;
         }
-        if ((reasons & JankType::Display) != 0) {
+        if ((reasons & JankType::DisplayHAL) != 0) {
             t.jankPayload.totalSFUnattributed++;
         }
         if ((reasons & JankType::AppDeadlineMissed) != 0) {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 5cbf2ef..b38032d 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -907,6 +907,7 @@
 }
 
 void FakeComposerClient::onSurfaceFlingerStop() {
+    mSurfaceComposer->enableVSyncInjections(false);
     mSurfaceComposer->dispose();
     mSurfaceComposer.clear();
 }
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 538b10d..a24aeba 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -27,6 +27,7 @@
 #include "FakeComposerUtils.h"
 #include "MockComposerHal.h"
 
+#include <binder/Parcel.h>
 #include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
@@ -870,6 +871,25 @@
 
 using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
 
+// Tests that VSYNC injection can be safely toggled while invalidating.
+TEST_F(DisplayTest_2_1, VsyncInjection) {
+    const auto flinger = ComposerService::getComposerService();
+    bool enable = true;
+
+    for (int i = 0; i < 100; i++) {
+        flinger->enableVSyncInjections(enable);
+        enable = !enable;
+
+        constexpr uint32_t kForceInvalidate = 1004;
+        android::Parcel data, reply;
+        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+        EXPECT_EQ(NO_ERROR,
+                  android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
+
+        std::this_thread::sleep_for(5ms);
+    }
+}
+
 TEST_F(DisplayTest_2_1, HotplugOneConfig) {
     Test_HotplugOneConfig();
 }
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
index 36e24d2..e890a62 100644
--- a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -37,6 +37,7 @@
                  FAKE_DURATION_OFFSET_NS}};
     }
 
+    void reset() override {}
     void setRefreshRateFps(Fps) override {}
     void dump(std::string&) const override {}
 };
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 43b5afe..4b897fa 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -26,6 +26,7 @@
 #include <cinttypes>
 
 using namespace std::chrono_literals;
+using testing::AtLeast;
 using testing::Contains;
 using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
 using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
@@ -62,7 +63,8 @@
 
     void SetUp() override {
         mTimeStats = std::make_shared<mock::TimeStats>();
-        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+                                                               kTestThresholds);
         mFrameTimeline->registerDataSource();
         mTokenManager = &mFrameTimeline->mTokenManager;
         maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
@@ -110,7 +112,8 @@
 
     SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
         std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
-        return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+        return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]
+                         ->getSurfaceFrames()[surfaceFrameIdx]);
     }
 
     std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
@@ -123,7 +126,7 @@
                 a.presentTime == b.presentTime;
     }
 
-    const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+    const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
         return mTokenManager->mPredictions;
     }
 
@@ -138,6 +141,16 @@
     FenceToFenceTimeMap fenceFactory;
     uint32_t* maxDisplayFrames;
     nsecs_t maxTokenRetentionTime;
+    pid_t mSurfaceFlingerPid = 666;
+    static constexpr nsecs_t kPresentThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr nsecs_t kDeadlineThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr nsecs_t kStartThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+                                                                  kDeadlineThreshold,
+                                                                  kStartThreshold};
 };
 
 static const std::string sLayerNameOne = "layer1";
@@ -162,55 +175,58 @@
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
-    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidTwo, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
     EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     flushTokens(systemTime() + maxTokenRetentionTime);
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1, SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flush by calling setSfPresent for the next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
 
     auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
@@ -219,48 +235,50 @@
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
-                                                       sLayerNameTwo, surfaceFrameToken2);
-    mFrameTimeline->setSfWakeUp(sfToken1, 22);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame2,
-                                    SurfaceFrame::PresentState::Presented);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameTwo, sLayerNameTwo);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
     mFrameTimeline->setSfPresent(26, presentFence1);
     auto displayFrame = getDisplayFrame(0);
-    SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
-    SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
     presentFence1->signalForTest(42);
 
     // Fences haven't been flushed yet, so it should be 0
-    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
     EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
     EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
 
-    EXPECT_EQ(surfaceFrame1->getToken(), surfaceFrameToken1);
-    EXPECT_EQ(surfaceFrame2->getToken(), surfaceFrameToken2);
-
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame3 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken2);
-    mFrameTimeline->setSfWakeUp(sfToken2, 52);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame3, SurfaceFrame::PresentState::Dropped);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame3);
     mFrameTimeline->setSfPresent(56, presentFence2);
     displayFrame = getDisplayFrame(0);
 
     // Fences have flushed, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
     EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
     EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
     EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
@@ -270,6 +288,12 @@
 TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
     // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
     int frameTimeFactor = 0;
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames));
     for (size_t i = 0; i < *maxDisplayFrames; i++) {
         auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
@@ -277,11 +301,11 @@
         int64_t sfToken = mTokenManager->generateTokenForPredictions(
                 {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, surfaceFrameToken);
-        mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+                mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
+        mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
         presentFence->signalForTest(32 + frameTimeFactor);
         frameTimeFactor += 30;
@@ -289,8 +313,7 @@
     auto displayFrame0 = getDisplayFrame(0);
 
     // The 0th Display Frame should have actuals 22, 27, 32
-    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
-              true);
+    EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(22, 27, 32)), true);
 
     // Add one more display frame
     auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -299,51 +322,54 @@
     int64_t sfToken = mTokenManager->generateTokenForPredictions(
             {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken);
-    mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame, SurfaceFrame::PresentState::Presented);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+    surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame);
     mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
     presentFence->signalForTest(32 + frameTimeFactor);
     displayFrame0 = getDisplayFrame(0);
 
     // The window should have slided by 1 now and the previous 0th display frame
     // should have been removed from the deque
-    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
-              true);
+    EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue", std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue", std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
     auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     presentFence->signalForTest(2);
 
     // Size shouldn't exceed maxDisplayFrames - 64
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -351,15 +377,18 @@
     // Increase the size to 256
     mFrameTimeline->setMaxDisplayFrames(256);
     EXPECT_EQ(*maxDisplayFrames, 256);
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -367,26 +396,30 @@
     // Shrink the size to 128
     mFrameTimeline->setMaxDisplayFrames(128);
     EXPECT_EQ(*maxDisplayFrames, 128);
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
 }
 
+// Tests related to TimeStats
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(sUidOne, sLayerNameOne,
-                                     HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+                                     HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+                incrementJankyFrames(HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -397,12 +430,13 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count());
 
@@ -412,8 +446,9 @@
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::Display)));
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::Display)));
+                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::DisplayHAL)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::DisplayHAL)));
+
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -424,18 +459,18 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                30);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
-    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
                                  presentFence1);
-    EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
-    EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::Display) != 0);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -449,26 +484,26 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
-            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
-             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
-             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(
             std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                11);
 
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
-    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
                                  presentFence1);
 
-    EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
-    EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::AppDeadlineMissed) != 0);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
 }
 
 /*
@@ -481,23 +516,26 @@
  */
 TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
@@ -506,25 +544,29 @@
 
 TEST_F(FrameTimelineTest, tracing_sanityTest) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token2, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
-                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfWakeUp(token2, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -543,6 +585,8 @@
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -550,13 +594,13 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(-1, 20);
+    mFrameTimeline->setSfWakeUp(-1, 20, 11);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token1, 50);
+    mFrameTimeline->setSfWakeUp(token1, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
@@ -572,24 +616,27 @@
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
@@ -662,6 +709,8 @@
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -670,7 +719,7 @@
     int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
+    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
     mFrameTimeline->setSfPresent(26, presentFence1);
     presentFence1->signalForTest(31);
 
@@ -687,7 +736,7 @@
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -713,6 +762,10 @@
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -722,8 +775,8 @@
     int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     surfaceFrame1->setActualStartTime(0);
     surfaceFrame1->setActualQueueTime(15);
     surfaceFrame1->setAcquireFenceTime(20);
@@ -743,15 +796,15 @@
     protoSurfaceFrame.set_pid(sPidOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
-                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(26, presentFence1);
-    presentFence1->signalForTest(31);
+    presentFence1->signalForTest(40);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -775,4 +828,508 @@
     validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
 }
 
+// Tests for Jank classification
+TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    auto& presentedSurfaceFrame = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(29);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+    EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 29);
+    EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 29);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(30);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    displayFrame = getDisplayFrame(0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 30);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    auto displayFrame2 = getDisplayFrame(1);
+    presentFence2->signalForTest(65);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame2 = getDisplayFrame(1);
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    displayFrame = getDisplayFrame(0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    auto displayFrame2 = getDisplayFrame(1);
+    presentFence2->signalForTest(75);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame2 = getDisplayFrame(1);
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 75);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
+    mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+
+    mFrameTimeline->setSfPresent(22, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(28);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 28);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+    mFrameTimeline->setSfPresent(36, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(52);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(16);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(30);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(36);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 30);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 30);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(65);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 65);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(16);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(36);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 50);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(86);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 86);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 86);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(40);
+    mFrameTimeline->setSfWakeUp(sfToken1, 42, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(46, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 50);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
+    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+    // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
+    // jank to the SurfaceFrame.
+
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(26);
+    mFrameTimeline->setSfWakeUp(sfToken1, 32, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(36, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(40);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(40);
+    mFrameTimeline->setSfWakeUp(sfToken2, 43, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 40);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 40);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(60);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 60);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 60);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(50);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, 30);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(56, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(60);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(84);
+    mFrameTimeline->setSfWakeUp(sfToken2, 112, 30);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(116, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+    presentFence2->signalForTest(120);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.endTime, 50);
+    EXPECT_EQ(actuals1.presentTime, 60);
+
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 120);
+
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+              JankType::AppDeadlineMissed | JankType::BufferStuffing);
+}
 } // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 53dfe3f..8208b3f 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -69,6 +69,7 @@
     ~MockTokenManager() override = default;
 
     MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+    MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
 };
 
 class MessageQueueTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 83ad737..29be5ab 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1545,38 +1545,112 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
-TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
-}
-
 TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_30);
-    const uid_t uid = 1234;
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 30});
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    const auto frameRate = Fps(30.f);
+    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
-    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.5f});
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.6f});
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_noLayers) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{};
+    ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f)).empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[0].vote = LayerVoteType::NoVote;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Min;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Max;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Heuristic;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_twoUids) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{
+            LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
+            LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
+    };
+
+    layers[0].name = "Test layer 1234";
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    layers[1].name = "Test layer 5678";
+    layers[1].desiredRefreshRate = Fps(30.0f);
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+
+    ASSERT_EQ(2, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(1, frameRateOverrides.count(5678));
+    ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+
+    layers[1].vote = LayerVoteType::Heuristic;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[1].ownerUid = 1234;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index c47b141..1bbe8e2 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -432,7 +432,7 @@
     EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
 }
 
-TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+TEST_P(SetFrameRateTest, SetAndGetReparentChildren) {
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
     const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 7f05a75..25aaa14 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -78,7 +78,7 @@
     }
 
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
+            Fps /*currentRefreshRate*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
@@ -156,8 +156,8 @@
     }
 
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) override {
-        return std::make_unique<mock::FrameTimeline>(timeStats);
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+        return std::make_unique<mock::FrameTimeline>(timeStats, surfaceFlingerPid);
     }
 
     using CreateBufferQueueFunction =
@@ -231,8 +231,7 @@
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
-        mFlinger->mVsyncConfiguration =
-                mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
         mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
@@ -372,14 +371,14 @@
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
-                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                             bool hasListenerCallbacks,
+                             int64_t desiredPresentTime, bool isAutoTimestamp,
+                             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                              std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
                                              applyToken, inputWindowCommands, desiredPresentTime,
-                                             uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
-                                             transactionId);
+                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             listenerCallbacks, transactionId);
     }
 
     auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
@@ -692,6 +691,7 @@
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
     void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
+    void triggerOnFrameRateOverridesChanged() {}
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bbcc0c9..ace370f 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -356,9 +356,9 @@
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
-    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(JankType::Display);
+    mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::None);
 
@@ -383,10 +383,10 @@
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -848,10 +848,10 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
 
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -987,9 +987,9 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
-    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(JankType::Display);
+    mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::None);
 
@@ -1062,10 +1062,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index c36d994..fa6ff30 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -97,7 +97,8 @@
         uint32_t flags = 0;
         sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
         InputWindowCommands inputWindowCommands;
-        int64_t desiredPresentTime = -1;
+        int64_t desiredPresentTime = 0;
+        bool isAutoTimestamp = true;
         int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
         client_cache_t uncacheBuffer;
         int64_t id = -1;
@@ -114,11 +115,13 @@
     }
 
     void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
-                     int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
+                     int64_t desiredPresentTime, bool isAutoTimestamp,
+                     int64_t frameTimelineVsyncId) {
         mTransactionNumber++;
         transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
         transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
         transaction.desiredPresentTime = desiredPresentTime;
+        transaction.isAutoTimestamp = isAutoTimestamp;
         transaction.frameTimelineVsyncId = frameTimelineVsyncId;
     }
 
@@ -129,13 +132,15 @@
         EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationTime = systemTime();
         mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transaction.id);
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
 
         // This transaction should not have been placed on the transaction queue.
         // If transaction is synchronous or syncs input windows, SF
@@ -164,13 +169,15 @@
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ time + s2ns(1), false,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transaction.id);
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
 
         nsecs_t returnedTime = systemTime();
         EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
@@ -189,20 +196,23 @@
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
         setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ time + s2ns(1), false,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
 
         // transaction that would not have gone on the pending thread if not
         // blocked
         TransactionInfo transactionB;
         setupSingle(transactionB, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
 
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
                                      transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
-                                     transactionA.desiredPresentTime, transactionA.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transactionA.id);
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
 
         // This thread should not have been blocked by the above transaction
         // (5s is the timeout period that applyTransactionState waits for SF to
@@ -213,8 +223,9 @@
         mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
                                      transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
-                                     transactionB.desiredPresentTime, transactionB.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transactionB.id);
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
 
         // this thread should have been blocked by the above transaction
         // if this is an animation, this thread should be blocked for 5s
@@ -256,12 +267,12 @@
             .WillOnce(Return(s2ns(2)));
     TransactionInfo transactionA; // transaction to go on pending queue
     setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                /*desiredPresentTime*/ s2ns(1), false, ISurfaceComposer::INVALID_VSYNC_ID);
     mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                 transactionA.id);
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
     ASSERT_EQ(1, transactionQueue.size());
@@ -279,8 +290,8 @@
     empty.applyToken = sp<IBinder>();
     mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
                                  empty.flags, empty.applyToken, empty.inputWindowCommands,
-                                 empty.desiredPresentTime, empty.uncacheBuffer,
-                                 mHasListenerCallbacks, mCallbacks, empty.id);
+                                 empty.desiredPresentTime, empty.isAutoTimestamp,
+                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 2a35f69..bb7578d 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -19,6 +19,7 @@
 
 #include <gmock/gmock.h>
 #include <log/log.h>
+#include <chrono>
 #include <thread>
 
 #include "Scheduler/VsyncConfiguration.h"
@@ -27,14 +28,15 @@
 
 namespace android::scheduler {
 
+using namespace std::chrono_literals;
+
 class TestableWorkDuration : public impl::WorkDuration {
 public:
     TestableWorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration,
                          nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
                          nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
-          : impl::WorkDuration({Fps(60.0f), Fps(90.0f)}, currentFps, sfDuration, appDuration,
-                               sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
-                               appEarlyGlDuration) {}
+          : impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
+                               appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
 };
 
 class WorkDurationTest : public testing::Test {
@@ -171,9 +173,9 @@
                          std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
                          std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
                          nsecs_t thresholdForNextVsync)
-          : impl::PhaseOffsets({Fps(60.0f), Fps(90.0f)}, Fps(60.0f), vsyncPhaseOffsetNs,
-                               sfVSyncPhaseOffsetNs, earlySfOffsetNs, earlyGpuSfOffsetNs,
-                               earlyAppOffsetNs, earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+          : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+                               earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+                               earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
                                highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
                                highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
                                highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
index f784df3..ff005a0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
@@ -19,8 +19,8 @@
 namespace android::mock {
 
 // Explicit default instantiation is recommended.
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
-      : android::frametimeline::impl::FrameTimeline(timeStats) {}
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid)
+      : android::frametimeline::impl::FrameTimeline(timeStats, surfaceFlingerPid) {}
 FrameTimeline::~FrameTimeline() = default;
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 6b12536..0a6a9f4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -26,14 +26,12 @@
     // No need to create mocks for SurfaceFrame and TokenManager yet. They are very small components
     // and do not have external dependencies like perfetto.
 public:
-    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid);
     ~FrameTimeline();
 
     MOCK_METHOD0(onBootFinished, void());
-    MOCK_METHOD2(addSurfaceFrame,
-                 void(std::shared_ptr<frametimeline::SurfaceFrame>,
-                      frametimeline::SurfaceFrame::PresentState));
-    MOCK_METHOD2(setSfWakeUp, void(int64_t, nsecs_t));
+    MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
+    MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, nsecs_t));
     MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 078d8e07..ba2e4db 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -33,6 +33,7 @@
     MOCK_CONST_METHOD0(isVisible, bool());
     MOCK_METHOD0(createClone, sp<Layer>());
     MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+    MOCK_CONST_METHOD0(getOwnerUid, uid_t());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index efaa9fa..453c93a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,7 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
+    MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
     MOCK_METHOD0(invalidate, void());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 72bc89c..ab19886 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -29,6 +29,7 @@
                       scheduler::RefreshRateConfigEvent));
     MOCK_METHOD0(repaintEverythingForHWC, void());
     MOCK_METHOD1(kernelTimerChanged, void(bool));
+    MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -37,6 +38,7 @@
                            scheduler::RefreshRateConfigEvent) override {}
     void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
+    void triggerOnFrameRateOverridesChanged() {}
 };
 
 } // namespace android::mock