summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/SurfaceControl.java91
-rw-r--r--core/jni/android_view_SurfaceControl.cpp96
2 files changed, 187 insertions, 0 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d7ee6ad15166..81e0852fdfa2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -26,6 +26,7 @@ import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.NAME;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,12 +55,15 @@ import android.util.proto.ProtoOutputStream;
import android.view.Surface.OutOfResourcesException;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.VirtualRefBasePtr;
import dalvik.system.CloseGuard;
import libcore.util.NativeAllocationRegistry;
import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -224,6 +228,10 @@ public final class SurfaceControl implements Parcelable {
IBinder focusedToken, int displayId);
private static native void nativeSetFrameTimelineVsync(long transactionObj,
long frameTimelineVsyncId);
+ private static native void nativeAddJankDataListener(long nativeListener,
+ long nativeSurfaceControl);
+ private static native void nativeRemoveJankDataListener(long nativeListener);
+ private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener);
@Nullable
@GuardedBy("mLock")
@@ -249,6 +257,73 @@ public final class SurfaceControl implements Parcelable {
void onReparent(@NonNull Transaction transaction, @Nullable SurfaceControl parent);
}
+ /**
+ * Jank information to be fed back via {@link OnJankDataListener}.
+ * @hide
+ */
+ public static class JankData {
+
+ /** @hide */
+ @IntDef(flag = true, value = {JANK_NONE,
+ JANK_DISPLAY,
+ JANK_SURFACEFLINGER_DEADLINE_MISSED,
+ JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
+ JANK_APP_DEADLINE_MISSED,
+ JANK_PREDICTION_EXPIRED,
+ JANK_SURFACEFLINGER_EARLY_LATCH})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface JankType {}
+
+ // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
+
+ // No Jank
+ public static final int JANK_NONE = 0x0;
+
+ // Jank not related to SurfaceFlinger or the App
+ public static final int JANK_DISPLAY = 0x1;
+ // SF took too long on the CPU
+ public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
+ // SF took too long on the GPU
+ public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
+ // Either App or GPU took too long on the frame
+ public static final int JANK_APP_DEADLINE_MISSED = 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.
+ public static final int JANK_PREDICTION_EXPIRED = 0x10;
+ // Latching a buffer early might cause an early present of the frame
+ public static final int JANK_SURFACEFLINGER_EARLY_LATCH = 0x20;
+
+ public JankData(long frameVsyncId, @JankType int jankType) {
+ this.frameVsyncId = frameVsyncId;
+ this.jankType = jankType;
+ }
+
+ public final long frameVsyncId;
+ public final @JankType int jankType;
+ }
+
+ /**
+ * Listener interface to be informed about SurfaceFlinger's jank classification for a specific
+ * surface.
+ *
+ * @see JankData
+ * @see #addJankDataListener
+ * @hide
+ */
+ public static abstract class OnJankDataListener {
+ private final VirtualRefBasePtr mNativePtr;
+
+ public OnJankDataListener() {
+ mNativePtr = new VirtualRefBasePtr(nativeCreateJankDataListenerWrapper(this));
+ }
+
+ /**
+ * Called when new jank classifications are available.
+ */
+ public abstract void onJankDataAvailable(JankData[] jankStats);
+ }
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
@@ -2519,6 +2594,22 @@ public final class SurfaceControl implements Parcelable {
nativeSetGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
+ /**
+ * Adds a callback to be informed about SF's jank classification for a specific surface.
+ * @hide
+ */
+ public static void addJankDataListener(OnJankDataListener listener, SurfaceControl surface) {
+ nativeAddJankDataListener(listener.mNativePtr.get(), surface.mNativeObject);
+ }
+
+ /**
+ * Removes a jank callback previously added with {@link #addJankDataListener}
+ * @hide
+ */
+ public static void removeJankDataListener(OnJankDataListener listener) {
+ nativeRemoveJankDataListener(listener.mNativePtr.get());
+ }
+
/**
* An atomic set of changes to a set of SurfaceControl.
*/
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6ec656c16790..a21545c21011 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -52,6 +52,7 @@
#include <ui/Rect.h>
#include <ui/Region.h>
#include <utils/Log.h>
+#include <utils/LightRefBase.h>
// ----------------------------------------------------------------------------
@@ -206,6 +207,16 @@ static struct {
jfieldID appRequestRefreshRateMax;
} gDesiredDisplayConfigSpecsClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID onJankDataAvailable;
+} gJankDataListenerClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gJankDataClassInfo;
+
class JNamedColorSpace {
public:
// ColorSpace.Named.SRGB.ordinal() = 0;
@@ -1601,6 +1612,73 @@ static void nativeSetFrameTimelineVsync(JNIEnv* env, jclass clazz, jlong transac
transaction->setFrameTimelineVsync(frameTimelineVsyncId);
}
+class JankDataListenerWrapper : public JankDataListener {
+public:
+ JankDataListenerWrapper(JNIEnv* env, jobject onJankDataListenerObject) {
+ mOnJankDataListenerWeak = env->NewWeakGlobalRef(onJankDataListenerObject);
+ env->GetJavaVM(&mVm);
+ }
+
+ ~JankDataListenerWrapper() {
+ JNIEnv* env = getEnv();
+ env->DeleteWeakGlobalRef(mOnJankDataListenerWeak);
+ }
+
+ void onJankDataAvailable(const std::vector<JankData>& jankData) {
+ JNIEnv* env = getEnv();
+
+ jobject target = env->NewLocalRef(mOnJankDataListenerWeak);
+ if (target == nullptr) return;
+
+ jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
+ gJankDataClassInfo.clazz, nullptr);
+ for (int i = 0; i < jankData.size(); i++) {
+ jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
+ gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
+ env->SetObjectArrayElement(jJankDataArray, i, jJankData);
+ }
+ env->CallVoidMethod(target,
+ gJankDataListenerClassInfo.onJankDataAvailable,
+ jJankDataArray);
+ env->DeleteLocalRef(target);
+ }
+
+private:
+
+ JNIEnv* getEnv() {
+ JNIEnv* env;
+ mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ return env;
+ }
+
+ JavaVM* mVm;
+ jobject mOnJankDataListenerWeak;
+};
+
+static void nativeAddJankDataListener(JNIEnv* env, jclass clazz,
+ jlong jankDataCallbackListenerPtr,
+ jlong nativeSurfaceControl) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(nativeSurfaceControl));
+ if (surface == nullptr) {
+ return;
+ }
+ JankDataListenerWrapper* wrapper =
+ reinterpret_cast<JankDataListenerWrapper*>(jankDataCallbackListenerPtr);
+ TransactionCompletedListener::getInstance()->addJankListener(wrapper, surface);
+}
+
+static void nativeRemoveJankDataListener(JNIEnv* env, jclass clazz,
+ jlong jankDataCallbackListenerPtr) {
+ JankDataListenerWrapper* wrapper =
+ reinterpret_cast<JankDataListenerWrapper*>(jankDataCallbackListenerPtr);
+ TransactionCompletedListener::getInstance()->removeJankListener(wrapper);
+}
+
+static jlong nativeCreateJankDataListenerWrapper(JNIEnv* env, jclass clazz,
+ jobject jankDataListenerObject) {
+ return reinterpret_cast<jlong>(new JankDataListenerWrapper(env, jankDataListenerObject));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1785,6 +1863,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetFocusedWindow},
{"nativeSetFrameTimelineVsync", "(JJ)V",
(void*)nativeSetFrameTimelineVsync },
+ {"nativeAddJankDataListener", "(JJ)V",
+ (void*)nativeAddJankDataListener },
+ {"nativeRemoveJankDataListener", "(J)V",
+ (void*)nativeRemoveJankDataListener },
+ {"nativeCreateJankDataListenerWrapper", "(Landroid/view/SurfaceControl$OnJankDataListener;)J",
+ (void*)nativeCreateJankDataListenerWrapper },
// clang-format on
};
@@ -1966,6 +2050,18 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
"(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
+
+ jclass jankDataClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$JankData");
+ gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
+ gJankDataClassInfo.ctor =
+ GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JI)V");
+ jclass onJankDataListenerClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
+ gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
+ gJankDataListenerClassInfo.onJankDataAvailable =
+ GetMethodIDOrDie(env, onJankDataListenerClazz, "onJankDataAvailable",
+ "([Landroid/view/SurfaceControl$JankData;)V");
return err;
}