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..ad5cdd815bfcf81fe85952085f4f1656b04f185a |
--- /dev/null |
+++ b/base/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java |
@@ -0,0 +1,145 @@ |
+// 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.animation.Animator; |
+import android.animation.Animator.AnimatorListener; |
+import android.animation.AnimatorListenerAdapter; |
+import android.animation.TimeAnimator; |
+import android.animation.TimeAnimator.TimeListener; |
+import android.util.Log; |
+ |
+/** |
+ * 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 Recorder mRecorder = new Recorder(); |
+ private final String mHistogramName; |
+ |
+ /** |
+ * @param histogramName The histogram name that the recorded frame times will be saved. |
+ * This must be also defined in histograms.xml |
+ * @return An AnimatorListener instance that records frame time histogram on start and end |
+ * automatically. |
+ */ |
+ public static AnimatorListener getAnimatorRecorder(final String histogramName) { |
+ return new AnimatorListenerAdapter() { |
+ private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram = |
+ new AnimationFrameTimeHistogram(histogramName); |
+ |
+ @Override |
+ public void onAnimationStart(Animator animation) { |
+ mAnimationFrameTimeHistogram.startRecording(); |
+ } |
+ |
+ @Override |
+ public void onAnimationEnd(Animator animation) { |
+ mAnimationFrameTimeHistogram.endRecording(); |
+ } |
+ |
+ @Override |
+ public void onAnimationCancel(Animator animation) { |
+ mAnimationFrameTimeHistogram.endRecording(); |
+ } |
+ }; |
+ } |
+ |
+ /** |
+ * @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.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.getFrameTimesMs(), mRecorder.getFrameTimesCount()); |
+ } |
+ mRecorder.cleanUp(); |
+ } |
+ |
+ /** |
+ * Record Android animation frame rate and return the result. |
+ */ |
+ private static class Recorder implements TimeListener { |
+ // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal |
+ // workload. |
+ private final TimeAnimator mAnimator = new TimeAnimator(); |
+ private long[] mFrameTimesMs; |
+ private int mFrameTimesCount; |
+ |
+ private Recorder() { |
+ mAnimator.setTimeListener(this); |
+ } |
+ |
+ private void startRecording() { |
+ assert !mAnimator.isRunning(); |
+ mFrameTimesCount = 0; |
+ mFrameTimesMs = new long[MAX_FRAME_TIME_NUM]; |
+ mAnimator.start(); |
+ } |
+ |
+ /** |
+ * @return Whether the recording was successful. If successful, the result is available via |
+ * getFrameTimesNs and getFrameTimesCount. |
+ */ |
+ private boolean endRecording() { |
+ boolean succeeded = mAnimator.isStarted(); |
+ mAnimator.end(); |
+ return succeeded; |
+ } |
+ |
+ private long[] getFrameTimesMs() { |
+ return mFrameTimesMs; |
+ } |
+ |
+ private int getFrameTimesCount() { |
+ return mFrameTimesCount; |
+ } |
+ |
+ /** |
+ * Deallocates the temporary buffer to record frame times. Must be called after ending |
+ * the recording and getting the result. |
+ */ |
+ private void cleanUp() { |
+ mFrameTimesMs = null; |
+ } |
+ |
+ @Override |
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { |
+ if (mFrameTimesCount == mFrameTimesMs.length) { |
+ mAnimator.end(); |
+ cleanUp(); |
+ 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; |
+ } |
+ |
+ // deltaTime is 0 for the first frame. |
+ if (deltaTime > 0) { |
+ mFrameTimesMs[mFrameTimesCount++] = deltaTime; |
+ } |
+ } |
+ } |
+ |
+ private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count); |
+} |