Merge "Introduce ASurfaceTransaction_setOnCommit api" into sc-dev
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index c349024..b7eafcd 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -147,6 +147,28 @@
 typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
                                                __INTRODUCED_IN(29);
 
+
+/**
+ * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
+ * are ready to be presented. This callback will be invoked before the
+ * ASurfaceTransaction_OnComplete callback.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the callback.
+ * Present and release fences are not available for this callback. Querying them using
+ * ASurfaceTransactionStats_getPresentFenceFd and ASurfaceTransactionStats_getPreviousReleaseFenceFd
+ * will result in failure.
+ *
+ * THREADING
+ * The transaction committed callback can be invoked on any thread.
+ *
+ * Available since API level 31.
+ */
+typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats)
+                                               __INTRODUCED_IN(31);
+
 /**
  * Returns the timestamp of when the frame was latched by the framework. Once a frame is
  * latched by the framework, it is presented at the following hardware vsync.
@@ -161,6 +183,8 @@
  * The recipient of the callback takes ownership of the fence and is responsible for closing
  * it. If a device does not support present fences, a -1 will be returned.
  *
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
  * Available since API level 29.
  */
 int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
@@ -218,6 +242,8 @@
  * The client must ensure that all pending refs on a buffer are released before attempting to reuse
  * this buffer, otherwise synchronization errors may occur.
  *
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
  * Available since API level 29.
  */
 int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
@@ -236,6 +262,16 @@
                                        ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
 
 /**
+ * Sets the callback that will be invoked when the updates from this transaction are applied and are
+ * ready to be presented. This callback will be invoked before the ASurfaceTransaction_OnComplete
+ * callback.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context,
+                                    ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31);
+
+/**
  * Reparents the \a surface_control from its old parent to the \a new_parent surface control.
  * Any children of the reparented \a surface_control will remain children of the \a surface_control.
  *
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6ca2487..53721cf 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -99,7 +99,7 @@
         SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
         for (const auto& [listener, callbackIds] : listenerCallbacks) {
             SAFE_PARCEL(data.writeStrongBinder, listener);
-            SAFE_PARCEL(data.writeInt64Vector, callbackIds);
+            SAFE_PARCEL(data.writeParcelableVector, callbackIds);
         }
 
         SAFE_PARCEL(data.writeUint64, transactionId);
@@ -1268,7 +1268,7 @@
             for (int32_t i = 0; i < listenersSize; i++) {
                 SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
                 std::vector<CallbackId> callbackIds;
-                SAFE_PARCEL(data.readInt64Vector, &callbackIds);
+                SAFE_PARCEL(data.readParcelableVector, &callbackIds);
                 listenerCallbacks.emplace_back(tmpBinder, callbackIds);
             }
 
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index b42793b..f74f91e 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -152,7 +152,7 @@
 }
 
 status_t TransactionStats::writeToParcel(Parcel* output) const {
-    status_t err = output->writeInt64Vector(callbackIds);
+    status_t err = output->writeParcelableVector(callbackIds);
     if (err != NO_ERROR) {
         return err;
     }
@@ -176,7 +176,7 @@
 }
 
 status_t TransactionStats::readFromParcel(const Parcel* input) {
-    status_t err = input->readInt64Vector(&callbackIds);
+    status_t err = input->readParcelableVector(&callbackIds);
     if (err != NO_ERROR) {
         return err;
     }
@@ -227,8 +227,9 @@
     return NO_ERROR;
 }
 
-ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
-                                         const std::unordered_set<CallbackId>& callbackIds) {
+ListenerStats ListenerStats::createEmpty(
+        const sp<IBinder>& listener,
+        const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
     ListenerStats listenerStats;
     listenerStats.listener = listener;
     listenerStats.transactionStats.emplace_back(callbackIds);
@@ -278,4 +279,28 @@
     }
 }
 
+ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
+    std::vector<CallbackId> filteredCallbackIds;
+    for (const auto& callbackId : callbackIds) {
+        if (callbackId.type == type) {
+            filteredCallbackIds.push_back(callbackId);
+        }
+    }
+    return ListenerCallbacks(transactionCompletedListener, filteredCallbackIds);
+}
+
+status_t CallbackId::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(output->writeInt64, id);
+    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+    return NO_ERROR;
+}
+
+status_t CallbackId::readFromParcel(const Parcel* input) {
+    SAFE_PARCEL(input->readInt64, &id);
+    int32_t typeAsInt;
+    SAFE_PARCEL(input->readInt32, &typeAsInt);
+    type = static_cast<CallbackId::Type>(typeAsInt);
+    return NO_ERROR;
+}
+
 }; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 55ed7fe..517b49e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -140,7 +140,7 @@
 
     for (auto listener : listeners) {
         SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
-        SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds);
+        SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
     }
     SAFE_PARCEL(output.writeFloat, shadowRadius);
     SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
@@ -258,7 +258,7 @@
         sp<IBinder> listener;
         std::vector<CallbackId> callbackIds;
         SAFE_PARCEL(input.readNullableStrongBinder, &listener);
-        SAFE_PARCEL(input.readInt64Vector, &callbackIds);
+        SAFE_PARCEL(input.readParcelableVector, &callbackIds);
         listeners.emplace_back(listener, callbackIds);
     }
     SAFE_PARCEL(input.readFloat, &shadowRadius);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9b2e10b..83124cf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -139,7 +139,7 @@
 // 0 is an invalid callback id
 TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
 
-CallbackId TransactionCompletedListener::getNextIdLocked() {
+int64_t TransactionCompletedListener::getNextIdLocked() {
     return mCallbackIdCounter++;
 }
 
@@ -163,13 +163,13 @@
 CallbackId TransactionCompletedListener::addCallbackFunction(
         const TransactionCompletedCallback& callbackFunction,
         const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
-                surfaceControls) {
+                surfaceControls,
+        CallbackId::Type callbackType) {
     std::lock_guard<std::mutex> lock(mMutex);
     startListeningLocked();
 
-    CallbackId callbackId = getNextIdLocked();
+    CallbackId callbackId(getNextIdLocked(), callbackType);
     mCallbacks[callbackId].callbackFunction = callbackFunction;
-
     auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
 
     for (const auto& surfaceControl : surfaceControls) {
@@ -228,7 +228,7 @@
 
 void TransactionCompletedListener::addSurfaceControlToCallbacks(
         const sp<SurfaceControl>& surfaceControl,
-        const std::unordered_set<CallbackId>& callbackIds) {
+        const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     for (auto callbackId : callbackIds) {
@@ -240,7 +240,7 @@
 }
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
-    std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
     std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
     std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
     {
@@ -267,7 +267,36 @@
         }
     }
     for (const auto& transactionStats : listenerStats.transactionStats) {
+        // handle on commit callbacks
         for (auto callbackId : transactionStats.callbackIds) {
+            if (callbackId.type != CallbackId::Type::ON_COMMIT) {
+                continue;
+            }
+            auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
+            if (!callbackFunction) {
+                ALOGE("cannot call null callback function, skipping");
+                continue;
+            }
+            std::vector<SurfaceControlStats> surfaceControlStats;
+            for (const auto& surfaceStats : transactionStats.surfaceStats) {
+                surfaceControlStats
+                        .emplace_back(callbacksMap[callbackId]
+                                              .surfaceControls[surfaceStats.surfaceControl],
+                                      transactionStats.latchTime, surfaceStats.acquireTime,
+                                      transactionStats.presentFence,
+                                      surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+                                      surfaceStats.eventStats);
+            }
+
+            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+                             surfaceControlStats);
+        }
+
+        // handle on complete callbacks
+        for (auto callbackId : transactionStats.callbackIds) {
+            if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+                continue;
+            }
             auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
             if (!callbackFunction) {
                 ALOGE("cannot call null callback function, skipping");
@@ -542,7 +571,9 @@
             return BAD_VALUE;
         }
         for (size_t j = 0; j < numCallbackIds; j++) {
-            listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+            CallbackId id;
+            parcel->readParcelable(&id);
+            listenerCallbacks[listener].callbackIds.insert(id);
         }
         size_t numSurfaces = parcel->readUint32();
         if (numSurfaces > parcel->dataSize()) {
@@ -628,7 +659,7 @@
         parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
         parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
         for (auto callbackId : callbackInfo.callbackIds) {
-            parcel->writeInt64(callbackId);
+            parcel->writeParcelable(callbackId);
         }
         parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
         for (auto surfaceControl : callbackInfo.surfaceControls) {
@@ -1389,9 +1420,9 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
-        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext,
+        CallbackId::Type callbackType) {
     auto listener = TransactionCompletedListener::getInstance();
 
     auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
@@ -1399,13 +1430,26 @@
     const auto& surfaceControls =
             mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
 
-    CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+    CallbackId callbackId =
+            listener->addCallbackFunction(callbackWithContext, surfaceControls, callbackType);
 
     mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
             callbackId);
     return *this;
 }
 
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE);
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCommittedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
         const sp<SurfaceControl>& sc) {
     layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 098760e..2d71194 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -36,7 +36,22 @@
 class ITransactionCompletedListener;
 class ListenerCallbacks;
 
-using CallbackId = int64_t;
+class CallbackId : public Parcelable {
+public:
+    int64_t id;
+    enum class Type : int32_t { ON_COMPLETE, ON_COMMIT } type;
+
+    CallbackId() {}
+    CallbackId(int64_t id, Type type) : id(id), type(type) {}
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    bool operator==(const CallbackId& rhs) const { return id == rhs.id && type == rhs.type; }
+};
+
+struct CallbackIdHash {
+    std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
+};
 
 class FrameEventHistoryStats : public Parcelable {
 public:
@@ -112,7 +127,7 @@
 
     TransactionStats() = default;
     TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
-    TransactionStats(const std::unordered_set<CallbackId>& ids)
+    TransactionStats(const std::unordered_set<CallbackId, CallbackIdHash>& ids)
           : callbackIds(ids.begin(), ids.end()) {}
     TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
                      const std::vector<SurfaceStats>& surfaces)
@@ -129,8 +144,9 @@
     status_t writeToParcel(Parcel* output) const override;
     status_t readFromParcel(const Parcel* input) override;
 
-    static ListenerStats createEmpty(const sp<IBinder>& listener,
-                                     const std::unordered_set<CallbackId>& callbackIds);
+    static ListenerStats createEmpty(
+            const sp<IBinder>& listener,
+            const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
 
     sp<IBinder> listener;
     std::vector<TransactionStats> transactionStats;
@@ -156,7 +172,8 @@
 
 class ListenerCallbacks {
 public:
-    ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
+    ListenerCallbacks(const sp<IBinder>& listener,
+                      const std::unordered_set<CallbackId, CallbackIdHash>& callbacks)
           : transactionCompletedListener(listener),
             callbackIds(callbacks.begin(), callbacks.end()) {}
 
@@ -170,9 +187,12 @@
         if (callbackIds.empty()) {
             return rhs.callbackIds.empty();
         }
-        return callbackIds.front() == rhs.callbackIds.front();
+        return callbackIds.front().id == rhs.callbackIds.front().id;
     }
 
+    // Returns a new ListenerCallbacks filtered by type
+    ListenerCallbacks filter(CallbackId::Type type) const;
+
     sp<IBinder> transactionCompletedListener;
     std::vector<CallbackId> callbackIds;
 };
@@ -191,7 +211,7 @@
     // same members. It is sufficient to just check the first CallbackId in the vectors. If
     // they match, they are the same. If they do not match, they are not the same.
     std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
-        return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+        return std::hash<int64_t>{}((callbackIds.empty()) ? 0 : callbackIds.front().id);
     }
 };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d3f6ff0..5bbd8e3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -336,7 +336,7 @@
     struct CallbackInfo {
         // All the callbacks that have been requested for a TransactionCompletedListener in the
         // Transaction
-        std::unordered_set<CallbackId> callbackIds;
+        std::unordered_set<CallbackId, CallbackIdHash> callbackIds;
         // All the SurfaceControls that have been modified in this TransactionCompletedListener's
         // process that require a callback if there is one or more callbackIds set.
         std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
@@ -484,9 +484,15 @@
         // Sets information about the priority of the frame.
         Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
 
+        Transaction& addTransactionCallback(TransactionCompletedCallbackTakesContext callback,
+                                            void* callbackContext, CallbackId::Type callbackType);
+
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
 
+        Transaction& addTransactionCommittedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
         // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
@@ -621,14 +627,13 @@
 class TransactionCompletedListener : public BnTransactionCompletedListener {
     TransactionCompletedListener();
 
-    CallbackId getNextIdLocked() REQUIRES(mMutex);
+    int64_t getNextIdLocked() REQUIRES(mMutex);
 
     std::mutex mMutex;
 
     bool mListening GUARDED_BY(mMutex) = false;
 
-    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
+    int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
     struct CallbackTranslation {
         TransactionCompletedCallback callbackFunction;
         std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
@@ -646,7 +651,8 @@
         SurfaceStatsCallback callback;
     };
 
-    std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
+            GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
     std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
             mReleaseBufferCallbacks GUARDED_BY(mMutex);
@@ -662,10 +668,12 @@
     CallbackId addCallbackFunction(
             const TransactionCompletedCallback& callbackFunction,
             const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
-                    surfaceControls);
+                    surfaceControls,
+            CallbackId::Type callbackType);
 
-    void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
-                                      const std::unordered_set<CallbackId>& callbackIds);
+    void addSurfaceControlToCallbacks(
+            const sp<SurfaceControl>& surfaceControl,
+            const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
 
     /*
      * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a2915c9..7a5b20d 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -674,6 +674,11 @@
                                           latchTime);
     }
 
+    std::deque<sp<CallbackHandle>> remainingHandles;
+    mFlinger->getTransactionCallbackInvoker()
+            .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+    mDrawingState.callbackHandles = remainingHandles;
+
     mCurrentStateModified = false;
 
     return NO_ERROR;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9da23a6..2da8ed4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2034,6 +2034,9 @@
     ATRACE_CALL();
     bool refreshNeeded = handlePageFlip();
 
+    // Send on commit callbacks
+    mTransactionCallbackInvoker.sendCallbacks();
+
     if (mVisibleRegionsDirty) {
         computeLayerBounds();
     }
@@ -3763,14 +3766,30 @@
 uint32_t SurfaceFlinger::setClientStateLocked(
         const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
         int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
-        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
+        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
     const layer_state_t& s = composerState.state;
     const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
+
+    std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
+        // Starts a registration but separates the callback ids according to callback type. This
+        // allows the callback invoker to send on latch callbacks earlier.
         // note that startRegistration will not re-register if the listener has
         // already be registered for a prior surface control
-        mTransactionCallbackInvoker.startRegistration(listener);
-        listenerCallbacks.insert(listener);
+
+        ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+        if (!onCommitCallbacks.callbackIds.empty()) {
+            mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
+            filteredListeners.push_back(onCommitCallbacks);
+            outListenerCallbacks.insert(onCommitCallbacks);
+        }
+
+        ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+        if (!onCompleteCallbacks.callbackIds.empty()) {
+            mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
+            filteredListeners.push_back(onCompleteCallbacks);
+            outListenerCallbacks.insert(onCompleteCallbacks);
+        }
     }
 
     const uint64_t what = s.what;
@@ -4031,8 +4050,8 @@
         }
     }
     std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
-        for (auto& [listener, callbackIds] : s.listeners) {
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+        for (auto& [listener, callbackIds] : filteredListeners) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3590e76..4f4c02b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -36,13 +36,17 @@
 //         <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
 //         >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
 //
-// See CallbackIdsHash for a explaniation of why this works
+// See CallbackIdsHash for a explanation of why this works
 static int compareCallbackIds(const std::vector<CallbackId>& c1,
                               const std::vector<CallbackId>& c2) {
     if (c1.empty()) {
         return !c2.empty();
     }
-    return c1.front() - c2.front();
+    return c1.front().id - c2.front().id;
+}
+
+static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) {
+    return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
 }
 
 TransactionCallbackInvoker::~TransactionCallbackInvoker() {
@@ -114,39 +118,69 @@
     return NO_ERROR;
 }
 
+status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+                                                            const std::vector<JankData>& jankData) {
+    auto listener = mPendingTransactions.find(handle->listener);
+    if (listener != mPendingTransactions.end()) {
+        auto& pendingCallbacks = listener->second;
+        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+        if (pendingCallback != pendingCallbacks.end()) {
+            auto& pendingCount = pendingCallback->second;
+
+            // Decrease the pending count for this listener
+            if (--pendingCount == 0) {
+                pendingCallbacks.erase(pendingCallback);
+            }
+        } else {
+            ALOGW("there are more latched callbacks than there were registered callbacks");
+        }
+        if (listener->second.size() == 0) {
+            mPendingTransactions.erase(listener);
+        }
+    } else {
+        ALOGW("cannot find listener in mPendingTransactions");
+    }
+
+    status_t err = addCallbackHandle(handle, jankData);
+    if (err != NO_ERROR) {
+        ALOGE("could not add callback handle");
+        return err;
+    }
+    return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles,
+        std::deque<sp<CallbackHandle>>& outRemainingHandles) {
+    if (handles.empty()) {
+        return NO_ERROR;
+    }
+    std::lock_guard lock(mMutex);
+    const std::vector<JankData>& jankData = std::vector<JankData>();
+    for (const auto& handle : handles) {
+        if (!containsOnCommitCallbacks(handle->callbackIds)) {
+            outRemainingHandles.push_back(handle);
+            continue;
+        }
+        status_t err = finalizeCallbackHandle(handle, jankData);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
 status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
     if (handles.empty()) {
         return NO_ERROR;
     }
     std::lock_guard lock(mMutex);
-
     for (const auto& handle : handles) {
-        auto listener = mPendingTransactions.find(handle->listener);
-        if (listener != mPendingTransactions.end()) {
-            auto& pendingCallbacks = listener->second;
-            auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
-            if (pendingCallback != pendingCallbacks.end()) {
-                auto& pendingCount = pendingCallback->second;
-
-                // Decrease the pending count for this listener
-                if (--pendingCount == 0) {
-                    pendingCallbacks.erase(pendingCallback);
-                }
-            } else {
-                ALOGW("there are more latched callbacks than there were registered callbacks");
-            }
-            if (listener->second.size() == 0) {
-                mPendingTransactions.erase(listener);
-            }
-        } else {
-            ALOGW("cannot find listener in mPendingTransactions");
-        }
-
-        status_t err = addCallbackHandle(handle, jankData);
+        status_t err = finalizeCallbackHandle(handle, jankData);
         if (err != NO_ERROR) {
-            ALOGE("could not add callback handle");
             return err;
         }
     }
@@ -243,7 +277,8 @@
             }
 
             // If the transaction has been latched
-            if (transactionStats.latchTime >= 0) {
+            if (transactionStats.latchTime >= 0 &&
+                !containsOnCommitCallbacks(transactionStats.callbackIds)) {
                 if (!mPresentFence) {
                     break;
                 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index caa8a4f..184b151 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -74,6 +74,8 @@
     // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
     status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                             const std::vector<JankData>& jankData);
+    status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+                                             std::deque<sp<CallbackHandle>>& outRemainingHandles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
@@ -95,6 +97,9 @@
     status_t addCallbackHandle(const sp<CallbackHandle>& handle,
                                const std::vector<JankData>& jankData) REQUIRES(mMutex);
 
+    status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+                                    const std::vector<JankData>& jankData) REQUIRES(mMutex);
+
     class CallbackDeathRecipient : public IBinder::DeathRecipient {
     public:
         // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.