blast: in order no-op transaction callbacks

Transactions callbacks were being sent as soon as they were ready
instead of in order. To fix this, keep a deque of Transaction
callbacks and do not send a callback until all the callbacks
before it have been sent.

Bug: 128519264
Test: Transaction_test

Change-Id: Ia363b3aca85bc1cd71d0fd915de79b44f786e09f
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index ce88d7b..74cd4f1 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -76,7 +76,11 @@
 }
 
 status_t TransactionStats::writeToParcel(Parcel* output) const {
-    status_t err = output->writeInt64(latchTime);
+    status_t err = output->writeInt64Vector(callbackIds);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(latchTime);
     if (err != NO_ERROR) {
         return err;
     }
@@ -96,7 +100,11 @@
 }
 
 status_t TransactionStats::readFromParcel(const Parcel* input) {
-    status_t err = input->readInt64(&latchTime);
+    status_t err = input->readInt64Vector(&callbackIds);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&latchTime);
     if (err != NO_ERROR) {
         return err;
     }
@@ -120,16 +128,11 @@
     if (err != NO_ERROR) {
         return err;
     }
-
-    for (const auto& [callbackIds, stats] : transactionStats) {
+    for (const auto& stats : transactionStats) {
         err = output->writeParcelable(stats);
         if (err != NO_ERROR) {
             return err;
         }
-        err = output->writeInt64Vector(callbackIds);
-        if (err != NO_ERROR) {
-            return err;
-        }
     }
     return NO_ERROR;
 }
@@ -139,18 +142,11 @@
 
     for (int i = 0; i < transactionStats_size; i++) {
         TransactionStats stats;
-        std::vector<CallbackId> callbackIds;
-
         status_t err = input->readParcelable(&stats);
         if (err != NO_ERROR) {
             return err;
         }
-        err = input->readInt64Vector(&callbackIds);
-        if (err != NO_ERROR) {
-            return err;
-        }
-
-        transactionStats.emplace(callbackIds, stats);
+        transactionStats.push_back(stats);
     }
     return NO_ERROR;
 }
@@ -159,11 +155,8 @@
                                          const std::unordered_set<CallbackId>& callbackIds) {
     ListenerStats listenerStats;
     listenerStats.listener = listener;
-    TransactionStats transactionStats;
-    listenerStats.transactionStats.emplace(std::piecewise_construct,
-                                           std::forward_as_tuple(callbackIds.begin(),
-                                                                 callbackIds.end()),
-                                           std::forward_as_tuple(transactionStats));
+    listenerStats.transactionStats.emplace_back(callbackIds);
+
     return listenerStats;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c627898..83cf40c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -203,15 +203,15 @@
      * that could possibly exist for the callbacks.
      */
     std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
-    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
-        for (auto callbackId : callbackIds) {
+    for (const auto& transactionStats : listenerStats.transactionStats) {
+        for (auto callbackId : transactionStats.callbackIds) {
             auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
             surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
         }
     }
 
-    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
-        for (auto callbackId : callbackIds) {
+    for (const auto& transactionStats : listenerStats.transactionStats) {
+        for (auto callbackId : transactionStats.callbackIds) {
             auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
             if (!callbackFunction) {
                 ALOGE("cannot call null callback function, skipping");
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 29ab026..774ad46 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -34,18 +34,6 @@
 
 using CallbackId = int64_t;
 
-struct CallbackIdsHash {
-    // CallbackId vectors have several properties that let us get away with this simple hash.
-    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
-    // empty we can still hash 0.
-    // 2) CallbackId vectors for the same listener either are identical or contain none of the
-    // 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.size() == 0) ? 0 : callbackIds.front());
-    }
-};
-
 class SurfaceStats : public Parcelable {
 public:
     status_t writeToParcel(Parcel* output) const override;
@@ -65,6 +53,15 @@
     status_t writeToParcel(Parcel* output) const override;
     status_t readFromParcel(const Parcel* input) override;
 
+    TransactionStats() = default;
+    TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
+    TransactionStats(const std::unordered_set<CallbackId>& ids)
+          : callbackIds(ids.begin(), ids.end()) {}
+    TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
+                     const std::vector<SurfaceStats>& surfaces)
+          : callbackIds(ids), latchTime(latch), presentFence(present), surfaceStats(surfaces) {}
+
+    std::vector<CallbackId> callbackIds;
     nsecs_t latchTime = -1;
     sp<Fence> presentFence = nullptr;
     std::vector<SurfaceStats> surfaceStats;
@@ -79,7 +76,7 @@
                                      const std::unordered_set<CallbackId>& callbackIds);
 
     sp<ITransactionCompletedListener> listener;
-    std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+    std::vector<TransactionStats> transactionStats;
 };
 
 class ITransactionCompletedListener : public IInterface {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fae4b81..2aac8f0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3651,18 +3651,22 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
+    // In case the client has sent a Transaction that should receive callbacks but without any
+    // SurfaceControls that should be included in the callback, send the listener and callbackIds
+    // to the callback thread so it can send an empty callback
+    if (!listenerCallbacks.empty()) {
+        mTransactionCompletedThread.run();
+    }
+    for (const auto& [listener, callbackIds] : listenerCallbacks) {
+        mTransactionCompletedThread.addCallback(listener, callbackIds);
+    }
+
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
         clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
                                                  postTime, privileged);
     }
 
-    // In case the client has sent a Transaction that should receive callbacks but without any
-    // SurfaceControls that should be included in the callback, send the listener and callbackIds
-    // to the callback thread so it can send an empty callback
-    for (const auto& [listener, callbackIds] : listenerCallbacks) {
-        mTransactionCompletedThread.addCallback(listener, callbackIds);
-    }
     // If the state doesn't require a traversal and there are callbacks, send them now
     if (!(clientStateFlags & eTraversalNeeded)) {
         mTransactionCompletedThread.sendCallbacks();
@@ -4019,7 +4023,6 @@
     }
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
-        mTransactionCompletedThread.run();
         for (const auto& [listener, callbackIds] : listenerCallbacks) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 6e0c1e2..1d2217d 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -29,6 +29,18 @@
 
 namespace android {
 
+// Returns 0 if they are equal
+//         <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
+static int compareCallbackIds(const std::vector<CallbackId>& c1, const std::vector<CallbackId> c2) {
+    if (c1.empty()) {
+        return !c2.empty();
+    }
+    return c1.front() - c2.front();
+}
+
 TransactionCompletedThread::~TransactionCompletedThread() {
     std::lock_guard lockThread(mThreadMutex);
 
@@ -44,8 +56,8 @@
 
     {
         std::lock_guard lock(mMutex);
-        for (const auto& [listener, listenerStats] : mListenerStats) {
-            listener->unlinkToDeath(mDeathRecipient);
+        for (const auto& [listener, transactionStats] : mCompletedTransactions) {
+            IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
         }
     }
 }
@@ -62,85 +74,127 @@
     mThread = std::thread(&TransactionCompletedThread::threadMain, this);
 }
 
-void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
+status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
+                                                 const std::vector<CallbackId>& callbackIds) {
     std::lock_guard lock(mMutex);
-
-    sp<IBinder> listener = IInterface::asBinder(handle->listener);
-    const auto& callbackIds = handle->callbackIds;
-
-    mPendingTransactions[listener][callbackIds]++;
-}
-
-void TransactionCompletedThread::addPresentedCallbackHandles(
-        const std::deque<sp<CallbackHandle>>& handles) {
-    std::lock_guard lock(mMutex);
-
-    for (const auto& handle : handles) {
-        auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
-        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 {
-            ALOGE("there are more latched callbacks than there were registered callbacks");
-        }
-
-        addCallbackHandle(handle);
-    }
-}
-
-void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
-    std::lock_guard lock(mMutex);
-    addCallbackHandle(handle);
-}
-
-void TransactionCompletedThread::addCallback(
-        const sp<ITransactionCompletedListener>& transactionListener,
-        const std::vector<CallbackId>& callbackIds) {
-    std::lock_guard lock(mMutex);
-    addCallbackLocked(transactionListener, callbackIds);
-}
-
-status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
-    status_t err = addCallbackLocked(handle->listener, handle->callbackIds);
-    if (err != NO_ERROR) {
-        ALOGE("cannot add callback, err: %d", err);
-        return err;
+    if (!mRunning) {
+        ALOGE("cannot add callback because the callback thread isn't running");
+        return BAD_VALUE;
     }
 
-    const sp<IBinder> listener = IInterface::asBinder(handle->listener);
-    auto& listenerStats = mListenerStats[listener];
-    auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
-
-    transactionStats.latchTime = handle->latchTime;
-    transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
-                                               handle->previousReleaseFence);
-    return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::addCallbackLocked(
-        const sp<ITransactionCompletedListener>& transactionListener,
-        const std::vector<CallbackId>& callbackIds) {
-    const sp<IBinder> listener = IInterface::asBinder(transactionListener);
-    // If we don't already have a reference to this listener, linkToDeath so we get a notification
-    // if it dies.
-    if (mListenerStats.count(listener) == 0) {
-        status_t err = listener->linkToDeath(mDeathRecipient);
+    if (mCompletedTransactions.count(listener) == 0) {
+        status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
         if (err != NO_ERROR) {
             ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
             return err;
         }
     }
 
-    auto& listenerStats = mListenerStats[listener];
-    listenerStats.listener = transactionListener;
-    listenerStats.transactionStats[callbackIds];
+    auto& transactionStatsDeque = mCompletedTransactions[listener];
+    transactionStatsDeque.emplace_back(callbackIds);
+    return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::registerPendingCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+    if (!mRunning) {
+        ALOGE("cannot register callback handle because the callback thread isn't running");
+        return BAD_VALUE;
+    }
+
+    // If we can't find the transaction stats something has gone wrong. The client should call
+    // addCallback before trying to register a pending callback handle.
+    TransactionStats* transactionStats;
+    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+    if (err != NO_ERROR) {
+        ALOGE("cannot find transaction stats");
+        return err;
+    }
+
+    mPendingTransactions[handle->listener][handle->callbackIds]++;
+    return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addPresentedCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles) {
+    std::lock_guard lock(mMutex);
+    if (!mRunning) {
+        ALOGE("cannot add presented callback handle because the callback thread isn't running");
+        return BAD_VALUE;
+    }
+
+    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");
+            }
+        } else {
+            ALOGW("cannot find listener in mPendingTransactions");
+        }
+
+        status_t err = addCallbackHandle(handle);
+        if (err != NO_ERROR) {
+            ALOGE("could not add callback handle");
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+    if (!mRunning) {
+        ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
+        return BAD_VALUE;
+    }
+
+    return addCallbackHandle(handle);
+}
+
+status_t TransactionCompletedThread::findTransactionStats(
+        const sp<ITransactionCompletedListener>& listener,
+        const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+    auto& transactionStatsDeque = mCompletedTransactions[listener];
+
+    // Search back to front because the most recent transactions are at the back of the deque
+    auto itr = transactionStatsDeque.rbegin();
+    for (; itr != transactionStatsDeque.rend(); itr++) {
+        if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+            *outTransactionStats = &(*itr);
+            return NO_ERROR;
+        }
+    }
+
+    ALOGE("could not find transaction stats");
+    return BAD_VALUE;
+}
+
+status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+    // If we can't find the transaction stats something has gone wrong. The client should call
+    // addCallback before trying to add a presnted callback handle.
+    TransactionStats* transactionStats;
+    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    transactionStats->latchTime = handle->latchTime;
+    transactionStats->surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+                                                handle->previousReleaseFence);
     return NO_ERROR;
 }
 
@@ -163,40 +217,46 @@
         mConditionVariable.wait(mMutex);
 
         // For each listener
-        auto it = mListenerStats.begin();
-        while (it != mListenerStats.end()) {
-            auto& [listener, listenerStats] = *it;
+        auto completedTransactionsItr = mCompletedTransactions.begin();
+        while (completedTransactionsItr != mCompletedTransactions.end()) {
+            auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+            ListenerStats listenerStats;
+            listenerStats.listener = listener;
 
             // For each transaction
-            bool sendCallback = true;
-            for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
-                // If we are still waiting on the callback handles for this transaction, skip it
-                if (mPendingTransactions[listener].count(callbackIds) != 0) {
-                    sendCallback = false;
+            auto transactionStatsItr = transactionStatsDeque.begin();
+            while (transactionStatsItr != transactionStatsDeque.end()) {
+                auto& transactionStats = *transactionStatsItr;
+
+                // If we are still waiting on the callback handles for this transaction, stop
+                // here because all transaction callbacks for the same listener must come in order
+                if (mPendingTransactions[listener].count(transactionStats.callbackIds) != 0) {
                     break;
                 }
 
                 // If the transaction has been latched
                 if (transactionStats.latchTime >= 0) {
                     if (!mPresentFence) {
-                        sendCallback = false;
                         break;
                     }
                     transactionStats.presentFence = mPresentFence;
                 }
+
+                // Remove the transaction from completed to the callback
+                listenerStats.transactionStats.push_back(std::move(transactionStats));
+                transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
             }
-            // If the listener has no pending transactions and all latched transactions have been
-            // presented
-            if (sendCallback) {
+            // If the listener has completed transactions
+            if (!listenerStats.transactionStats.empty()) {
                 // If the listener is still alive
-                if (listener->isBinderAlive()) {
+                if (IInterface::asBinder(listener)->isBinderAlive()) {
                     // Send callback
                     listenerStats.listener->onTransactionCompleted(listenerStats);
-                    listener->unlinkToDeath(mDeathRecipient);
+                    IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
                 }
-                it = mListenerStats.erase(it);
+                completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
             } else {
-                it++;
+                completedTransactionsItr++;
             }
         }
 
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 88808fd..09feb75 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -30,6 +30,18 @@
 
 namespace android {
 
+struct CallbackIdsHash {
+    // CallbackId vectors have several properties that let us get away with this simple hash.
+    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+    // empty we can still hash 0.
+    // 2) CallbackId vectors for the same listener either are identical or contain none of the
+    // 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());
+    }
+};
+
 class CallbackHandle : public RefBase {
 public:
     CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
@@ -51,23 +63,24 @@
 
     void run();
 
+    // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
+    // to be included in the callback. This functions should be call before attempting to add any
+    // callback handles.
+    status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
+                         const std::vector<CallbackId>& callbackIds);
+
     // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
     // that needs to be latched and presented this frame. This function should be called once the
     // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
     // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
     // presented.
-    void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+    status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
     // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
-    void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+    status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
-    void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
-
-    // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
-    // to be included in the callback.
-    void addCallback(const sp<ITransactionCompletedListener>& transactionListener,
-                     const std::vector<CallbackId>& callbackIds);
+    status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
@@ -76,9 +89,11 @@
 private:
     void threadMain();
 
+    status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+                                  const std::vector<CallbackId>& callbackIds,
+                                  TransactionStats** outTransactionStats) REQUIRES(mMutex);
+
     status_t addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
-    status_t addCallbackLocked(const sp<ITransactionCompletedListener>& transactionListener,
-                               const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
 
     class ThreadDeathRecipient : public IBinder::DeathRecipient {
     public:
@@ -91,9 +106,10 @@
     };
     sp<ThreadDeathRecipient> mDeathRecipient;
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& strongPointer) const {
-            return std::hash<IBinder*>{}(strongPointer.get());
+    struct ITransactionCompletedListenerHash {
+        std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
+            return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
+                                                    : nullptr);
         }
     };
 
@@ -106,12 +122,13 @@
     std::condition_variable_any mConditionVariable;
 
     std::unordered_map<
-            sp<IBinder /*listener*/>,
+            sp<ITransactionCompletedListener>,
             std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
-            IBinderHash>
+            ITransactionCompletedListenerHash>
             mPendingTransactions GUARDED_BY(mMutex);
-    std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
-            GUARDED_BY(mMutex);
+    std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
+                       ITransactionCompletedListenerHash>
+            mCompletedTransactions GUARDED_BY(mMutex);
 
     bool mRunning GUARDED_BY(mMutex) = false;
     bool mKeepRunning GUARDED_BY(mMutex) = true;