blast: Send transaction callback

Send transaction callback to client via the
TransactionCompletedListener.

Test: Transaction_test
Bug: 80477568

Change-Id: Iac98780b1357b9cc54b93cc3c848013b28fab441
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 38ff59d..3ecd13c 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -30,6 +30,74 @@
 
 } // Anonymous namespace
 
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+    return output->writeStrongBinder(surfaceControl);
+}
+
+status_t SurfaceStats::readFromParcel(const Parcel* input) {
+    return input->readStrongBinder(&surfaceControl);
+}
+
+status_t TransactionStats::writeToParcel(Parcel* output) const {
+    return output->writeParcelableVector(surfaceStats);
+}
+
+status_t TransactionStats::readFromParcel(const Parcel* input) {
+    return input->readParcelableVector(&surfaceStats);
+}
+
+status_t ListenerStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size()));
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (const auto& [callbackIds, 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;
+}
+
+status_t ListenerStats::readFromParcel(const Parcel* input) {
+    int32_t transactionStats_size = input->readInt32();
+
+    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);
+    }
+    return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                         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));
+    return listenerStats;
+}
+
 class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
 public:
     explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
@@ -38,9 +106,10 @@
 
     ~BpTransactionCompletedListener() override;
 
-    void onTransactionCompleted() override {
-        callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionCompleted)>(
-                Tag::ON_TRANSACTION_COMPLETED);
+    void onTransactionCompleted(ListenerStats stats) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+                                                                  stats);
     }
 };
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 88c5742..e10bda4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -128,8 +128,7 @@
     mListening = true;
 }
 
-CallbackId TransactionCompletedListener::addCallback(
-        const TransactionCompletedCallbackWithContext& callback) {
+CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) {
     std::lock_guard<std::mutex> lock(mMutex);
     startListeningLocked();
 
@@ -138,8 +137,20 @@
     return callbackId;
 }
 
-void TransactionCompletedListener::onTransactionCompleted() {
-    return;
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+    std::lock_guard lock(mMutex);
+
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            const auto& callback = mCallbacks[callbackId];
+            if (!callback) {
+                ALOGE("cannot call null callback function, skipping");
+                continue;
+            }
+            callback(transactionStats);
+            mCallbacks.erase(callbackId);
+        }
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -201,6 +212,12 @@
             continue;
         }
 
+        // If the listener does not have any SurfaceControls set on this Transaction, send the
+        // callback now
+        if (surfaceControls.empty()) {
+            listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
+        }
+
         // If the listener has any SurfaceControls set on this Transaction update the surface state
         for (const auto& surfaceControl : surfaceControls) {
             layer_state_t* s = getLayerState(surfaceControl);
@@ -676,10 +693,10 @@
 
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
-        TransactionCompletedCallback callback, void* callbackContext) {
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
     auto listener = TransactionCompletedListener::getInstance();
 
-    auto callbackWithContext = std::bind(callback, callbackContext);
+    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1);
 
     CallbackId callbackId = listener->addCallback(callbackWithContext);
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 0a61cd1..3c4c393 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -17,18 +17,70 @@
 #pragma once
 
 #include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
 #include <binder/SafeInterface.h>
 
+#include <utils/Timers.h>
+
 #include <cstdint>
+#include <unordered_map>
 #include <unordered_set>
 
 namespace android {
 
+class ITransactionCompletedListener;
+
+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;
+    status_t readFromParcel(const Parcel* input) override;
+
+    SurfaceStats() = default;
+    explicit SurfaceStats(const sp<IBinder>& sc) : surfaceControl(sc) {}
+
+    sp<IBinder> surfaceControl;
+};
+
+class TransactionStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    std::vector<SurfaceStats> surfaceStats;
+};
+
+class ListenerStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                     const std::unordered_set<CallbackId>& callbackIds);
+
+    sp<ITransactionCompletedListener> listener;
+    std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+};
+
 class ITransactionCompletedListener : public IInterface {
 public:
     DECLARE_META_INTERFACE(TransactionCompletedListener)
 
-    virtual void onTransactionCompleted() = 0;
+    virtual void onTransactionCompleted(ListenerStats stats) = 0;
 };
 
 class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
@@ -40,8 +92,6 @@
                         uint32_t flags = 0) override;
 };
 
-using CallbackId = int64_t;
-
 class ListenerCallbacks {
 public:
     ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2ed4f8f..1cafb77 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -52,8 +52,9 @@
 
 // ---------------------------------------------------------------------------
 
-using TransactionCompletedCallback = std::function<void(void* /*context*/)>;
-using TransactionCompletedCallbackWithContext = std::function<void()>;
+using TransactionCompletedCallbackTakesContext =
+        std::function<void(void* /*context*/, const TransactionStats&)>;
+using TransactionCompletedCallback = std::function<void(const TransactionStats&)>;
 
 class TransactionCompletedListener : public BnTransactionCompletedListener {
     TransactionCompletedListener();
@@ -66,7 +67,7 @@
 
     CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
 
-    std::map<CallbackId, TransactionCompletedCallbackWithContext> mCallbacks GUARDED_BY(mMutex);
+    std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex);
 
 public:
     static sp<TransactionCompletedListener> getInstance();
@@ -74,10 +75,10 @@
 
     void startListeningLocked() REQUIRES(mMutex);
 
-    CallbackId addCallback(const TransactionCompletedCallbackWithContext& callback);
+    CallbackId addCallback(const TransactionCompletedCallback& callback);
 
     // Overrides BnTransactionCompletedListener's onTransactionCompleted
-    void onTransactionCompleted() override;
+    void onTransactionCompleted(ListenerStats stats) override;
 };
 
 // ---------------------------------------------------------------------------
@@ -303,8 +304,8 @@
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
 
-        Transaction& addTransactionCompletedCallback(TransactionCompletedCallback callback,
-                                                     void* callbackContext);
+        Transaction& addTransactionCompletedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
 
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.