Add Shared timeline jank classification listener (1/2)
Adds the ability to register a listener that gets informed about
SF' jank classifications via the TransactionCompleted interface
Bug: 17475548
Test: FrameTimelineTest
Test: Register listener, ensure data flows back
Change-Id: Ie42c508da605c03569eadab6ab18b7315b35d247
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 69f7894..2dacae1 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
+#include <gui/LayerState.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
namespace android {
@@ -90,61 +92,63 @@
return err;
}
-status_t SurfaceStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeStrongBinder(surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->writeInt64(acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
- if (previousReleaseFence) {
- err = output->writeBool(true);
- if (err != NO_ERROR) {
- return err;
- }
- err = output->write(*previousReleaseFence);
- } else {
- err = output->writeBool(false);
- }
- err = output->writeUint32(transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+JankData::JankData() :
+ frameVsyncId(ISurfaceComposer::INVALID_VSYNC_ID),
+ jankType(JankType::None) {
+}
- err = output->writeParcelable(eventStats);
- return err;
+status_t JankData::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeInt64, frameVsyncId);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(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);
+ return NO_ERROR;
+}
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+ SAFE_PARCEL(output->writeStrongBinder, surfaceControl);
+ SAFE_PARCEL(output->writeInt64, acquireTime);
+ if (previousReleaseFence) {
+ SAFE_PARCEL(output->writeBool, true);
+ SAFE_PARCEL(output->write, *previousReleaseFence);
+ } else {
+ SAFE_PARCEL(output->writeBool, false);
+ }
+ SAFE_PARCEL(output->writeUint32, transformHint);
+ SAFE_PARCEL(output->writeParcelable, eventStats);
+ SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
+ for (const auto& data : jankData) {
+ SAFE_PARCEL(output->writeParcelable, data);
+ }
+ return NO_ERROR;
}
status_t SurfaceStats::readFromParcel(const Parcel* input) {
- status_t err = input->readStrongBinder(&surfaceControl);
- if (err != NO_ERROR) {
- return err;
- }
- err = input->readInt64(&acquireTime);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readStrongBinder, &surfaceControl);
+ SAFE_PARCEL(input->readInt64, &acquireTime);
bool hasFence = false;
- err = input->readBool(&hasFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readBool, &hasFence);
if (hasFence) {
previousReleaseFence = new Fence();
- err = input->read(*previousReleaseFence);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->read, *previousReleaseFence);
}
- err = input->readUint32(&transformHint);
- if (err != NO_ERROR) {
- return err;
- }
+ SAFE_PARCEL(input->readUint32, &transformHint);
+ SAFE_PARCEL(input->readParcelable, &eventStats);
- err = input->readParcelable(&eventStats);
- return err;
+ int32_t jankData_size = 0;
+ SAFE_PARCEL_READ_SIZE(input->readInt32, &jankData_size, input->dataSize());
+ for (int i = 0; i < jankData_size; i++) {
+ JankData data;
+ SAFE_PARCEL(input->readParcelable, &data);
+ jankData.push_back(data);
+ }
+ return NO_ERROR;
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 47a08ab..9ed7d1c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -125,6 +125,9 @@
return DefaultComposerClient::getComposerClient();
}
+JankDataListener::~JankDataListener() {
+}
+
// ---------------------------------------------------------------------------
// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -174,6 +177,23 @@
return callbackId;
}
+void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
+ sp<SurfaceControl> surfaceControl) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mJankListeners.insert({surfaceControl->getHandle(), listener});
+}
+
+void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
+ if (it->second == listener) {
+ it = mJankListeners.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
void TransactionCompletedListener::addSurfaceControlToCallbacks(
const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId>& callbackIds) {
@@ -189,6 +209,7 @@
void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+ std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -204,6 +225,7 @@
* sp<SurfaceControl> that could possibly exist for the callbacks.
*/
callbacksMap = mCallbacks;
+ jankListenersMap = mJankListeners;
for (const auto& transactionStats : listenerStats.transactionStats) {
for (auto& callbackId : transactionStats.callbackIds) {
mCallbacks.erase(callbackId);
@@ -236,6 +258,13 @@
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
}
+ for (const auto& surfaceStats : transactionStats.surfaceStats) {
+ if (surfaceStats.jankData.empty()) continue;
+ for (auto it = jankListenersMap.find(surfaceStats.surfaceControl);
+ it != jankListenersMap.end(); it++) {
+ it->second->onJankDataAvailable(surfaceStats.jankData);
+ }
+ }
}
}
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index c58634b..26b3840 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -16,6 +16,8 @@
#pragma once
+#include "JankInfo.h"
+
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -57,6 +59,27 @@
nsecs_t dequeueReadyTime;
};
+/**
+ * Jank information representing SurfaceFlinger's jank classification about frames for a specific
+ * surface.
+ */
+class JankData : public Parcelable {
+public:
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ JankData();
+ JankData(int64_t frameVsyncId, JankType jankType)
+ : frameVsyncId(frameVsyncId),
+ jankType(jankType) {}
+
+ // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
+ int64_t frameVsyncId;
+
+ // The type of jank occurred
+ JankType jankType;
+};
+
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -64,18 +87,21 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats frameEventStats)
+ uint32_t hint, FrameEventHistoryStats frameEventStats,
+ std::vector<JankData> jankData)
: surfaceControl(sc),
acquireTime(time),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- eventStats(frameEventStats) {}
+ eventStats(frameEventStats),
+ jankData(std::move(jankData)) {}
sp<IBinder> surfaceControl;
nsecs_t acquireTime = -1;
sp<Fence> previousReleaseFence;
uint32_t transformHint = 0;
FrameEventHistoryStats eventStats;
+ std::vector<JankData> jankData;
};
class TransactionStats : public Parcelable {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2eb97f2..f1845ee 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -619,6 +619,12 @@
// ---------------------------------------------------------------------------
+class JankDataListener : public VirtualLightRefBase {
+public:
+ virtual ~JankDataListener() = 0;
+ virtual void onJankDataAvailable(const std::vector<JankData>& jankData) = 0;
+};
+
class TransactionCompletedListener : public BnTransactionCompletedListener {
TransactionCompletedListener();
@@ -637,6 +643,7 @@
};
std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+ std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
public:
static sp<TransactionCompletedListener> getInstance();
@@ -652,6 +659,18 @@
void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId>& callbackIds);
+ /*
+ * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
+ * surface. Jank classifications arrive as part of the transaction callbacks about previous
+ * frames submitted to this Surface.
+ */
+ void addJankListener(const sp<JankDataListener>& listener, sp<SurfaceControl> surfaceControl);
+
+ /**
+ * Removes a jank listener previously added to addJankCallback.
+ */
+ void removeJankListener(const sp<JankDataListener>& listener);
+
// Overrides BnTransactionCompletedListener's onTransactionCompleted
void onTransactionCompleted(ListenerStats stats) override;
};