Index: base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
diff --git a/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3f9060c19a9ca708c1f4a7929b9bf25756378017 |
--- /dev/null |
+++ b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
@@ -0,0 +1,109 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.base; |
+ |
+import android.util.Log; |
+import android.view.Choreographer; |
+import android.view.Choreographer.FrameCallback; |
+ |
+/** |
+ * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring |
+ * any jankiness of short Chrome Android animations. It is limited to few seconds of recording. |
+ */ |
+public class AnimationFrameTimeHistogram { |
+ private static final String TAG = "AnimationFrameTimeHistogram"; |
+ private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps. |
+ |
+ private final String mHistogramName; |
+ private Recorder mRecorder; |
+ |
+ /** |
+ * @param histogramName The histogram name that the recorded frame times will be saved. |
+ * This must be also defined in histograms.xml |
+ */ |
+ public AnimationFrameTimeHistogram(String histogramName) { |
+ mHistogramName = histogramName; |
+ } |
+ |
+ /** |
+ * Start recording frame times. The recording can fail if it exceeds a few seconds. |
+ */ |
+ public void startRecording() { |
+ mRecorder = new Recorder(); |
+ mRecorder.startRecording(); |
+ } |
+ |
+ /** |
+ * End recording and save it to histogram. It won't save histogram if the recording wasn't |
+ * successful. |
+ */ |
+ public void endRecording() { |
+ if (mRecorder.endRecording()) { |
+ nativeSaveHistogram(mHistogramName, |
+ mRecorder.getFrameTimesNs(), mRecorder.getFrameTimesCount()); |
+ } |
+ mRecorder = null; |
+ } |
+ |
+ /** |
+ * Record Android animation frame rate and return the result. |
+ */ |
+ private static class Recorder implements FrameCallback { |
+ private final Choreographer mChoreographer = Choreographer.getInstance(); |
+ private final long[] mFrameTimesNs = new long[MAX_FRAME_TIME_NUM]; |
+ private boolean mIsRecording; |
+ private long mPrevFrameTimeNs; |
+ private int mFrameTimesCount; |
+ |
+ private void startRecording() { |
+ mIsRecording = true; |
+ mFrameTimesCount = -1; |
+ mChoreographer.postFrameCallback(this); |
+ } |
+ |
+ /** |
+ * @return Whether the recording was successful. If successful, the result is available via |
+ * getFrameTimesNs and getFrameTimesCount. |
+ */ |
+ private boolean endRecording() { |
+ boolean succeeded = mIsRecording; |
+ mIsRecording = false; |
+ return succeeded; |
+ } |
+ |
+ private long[] getFrameTimesNs() { |
+ return mFrameTimesNs; |
+ } |
+ |
+ private int getFrameTimesCount() { |
+ return mFrameTimesCount; |
+ } |
+ |
+ @Override |
+ public void doFrame(long frameTimeNs) { |
+ if (!mIsRecording) { |
+ return; |
+ } |
+ |
+ if (mFrameTimesCount == mFrameTimesNs.length) { |
+ mIsRecording = false; |
+ Log.w(TAG, "Animation frame time recording reached the maximum number. It's either" |
+ + "the animation took too long or recording end is not called."); |
+ return; |
+ } |
+ |
+ long timeDiffNs = frameTimeNs - mPrevFrameTimeNs; |
+ mPrevFrameTimeNs = frameTimeNs; |
+ if (mFrameTimesCount > -1) { |
+ mFrameTimesNs[mFrameTimesCount] = timeDiffNs; |
+ } |
+ |
+ ++mFrameTimesCount; |
+ mChoreographer.postFrameCallback(this); |
+ } |
+ } |
+ |
+ private native void nativeSaveHistogram(String histogramName, long[] frameTimesNs, int count); |
+} |