• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
20 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
21 
22 import android.hardware.display.DisplayManagerGlobal;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.os.SystemClock;
27 import android.os.SystemProperties;
28 import android.os.Trace;
29 import android.util.Log;
30 import android.util.TimeUtils;
31 import android.view.animation.AnimationUtils;
32 
33 import java.io.PrintWriter;
34 
35 /**
36  * Coordinates the timing of animations, input and drawing.
37  * <p>
38  * The choreographer receives timing pulses (such as vertical synchronization)
39  * from the display subsystem then schedules work to occur as part of rendering
40  * the next display frame.
41  * </p><p>
42  * Applications typically interact with the choreographer indirectly using
43  * higher level abstractions in the animation framework or the view hierarchy.
44  * Here are some examples of things you can do using the higher-level APIs.
45  * </p>
46  * <ul>
47  * <li>To post an animation to be processed on a regular time basis synchronized with
48  * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
49  * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
50  * frame, use {@link View#postOnAnimation}.</li>
51  * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
52  * frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
53  * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
54  * next display frame, use {@link View#postInvalidateOnAnimation()} or
55  * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
56  * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
57  * sync with display frame rendering, do nothing.  This already happens automatically.
58  * {@link View#onDraw} will be called at the appropriate time.</li>
59  * </ul>
60  * <p>
61  * However, there are a few cases where you might want to use the functions of the
62  * choreographer directly in your application.  Here are some examples.
63  * </p>
64  * <ul>
65  * <li>If your application does its rendering in a different thread, possibly using GL,
66  * or does not use the animation framework or view hierarchy at all
67  * and you want to ensure that it is appropriately synchronized with the display, then use
68  * {@link Choreographer#postFrameCallback}.</li>
69  * <li>... and that's about it.</li>
70  * </ul>
71  * <p>
72  * Each {@link Looper} thread has its own choreographer.  Other threads can
73  * post callbacks to run on the choreographer but they will run on the {@link Looper}
74  * to which the choreographer belongs.
75  * </p>
76  */
77 public final class Choreographer {
78     private static final String TAG = "Choreographer";
79 
80     // Prints debug messages about jank which was detected (low volume).
81     private static final boolean DEBUG_JANK = false;
82 
83     // Prints debug messages about every frame and callback registered (high volume).
84     private static final boolean DEBUG_FRAMES = false;
85 
86     // The default amount of time in ms between animation frames.
87     // When vsync is not enabled, we want to have some idea of how long we should
88     // wait before posting the next animation message.  It is important that the
89     // default value be less than the true inter-frame delay on all devices to avoid
90     // situations where we might skip frames by waiting too long (we must compensate
91     // for jitter and hardware variations).  Regardless of this value, the animation
92     // and display loop is ultimately rate-limited by how fast new graphics buffers can
93     // be dequeued.
94     private static final long DEFAULT_FRAME_DELAY = 10;
95 
96     // The number of milliseconds between animation frames.
97     private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
98 
99     // Thread local storage for the choreographer.
100     private static final ThreadLocal<Choreographer> sThreadInstance =
101             new ThreadLocal<Choreographer>() {
102         @Override
103         protected Choreographer initialValue() {
104             Looper looper = Looper.myLooper();
105             if (looper == null) {
106                 throw new IllegalStateException("The current thread must have a looper!");
107             }
108             return new Choreographer(looper, VSYNC_SOURCE_APP);
109         }
110     };
111 
112     // Thread local storage for the SF choreographer.
113     private static final ThreadLocal<Choreographer> sSfThreadInstance =
114             new ThreadLocal<Choreographer>() {
115                 @Override
116                 protected Choreographer initialValue() {
117                     Looper looper = Looper.myLooper();
118                     if (looper == null) {
119                         throw new IllegalStateException("The current thread must have a looper!");
120                     }
121                     return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
122                 }
123             };
124 
125     // Enable/disable vsync for animations and drawing.
126     private static final boolean USE_VSYNC = SystemProperties.getBoolean(
127             "debug.choreographer.vsync", true);
128 
129     // Enable/disable using the frame time instead of returning now.
130     private static final boolean USE_FRAME_TIME = SystemProperties.getBoolean(
131             "debug.choreographer.frametime", true);
132 
133     // Set a limit to warn about skipped frames.
134     // Skipped frames imply jank.
135     private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
136             "debug.choreographer.skipwarning", 30);
137 
138     private static final int MSG_DO_FRAME = 0;
139     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
140     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
141 
142     // All frame callbacks posted by applications have this token.
143     private static final Object FRAME_CALLBACK_TOKEN = new Object() {
144         public String toString() { return "FRAME_CALLBACK_TOKEN"; }
145     };
146 
147     private final Object mLock = new Object();
148 
149     private final Looper mLooper;
150     private final FrameHandler mHandler;
151 
152     // The display event receiver can only be accessed by the looper thread to which
153     // it is attached.  We take care to ensure that we post message to the looper
154     // if appropriate when interacting with the display event receiver.
155     private final FrameDisplayEventReceiver mDisplayEventReceiver;
156 
157     private CallbackRecord mCallbackPool;
158 
159     private final CallbackQueue[] mCallbackQueues;
160 
161     private boolean mFrameScheduled;
162     private boolean mCallbacksRunning;
163     private long mLastFrameTimeNanos;
164     private long mFrameIntervalNanos;
165     private boolean mDebugPrintNextFrameTimeDelta;
166 
167     /**
168      * Contains information about the current frame for jank-tracking,
169      * mainly timings of key events along with a bit of metadata about
170      * view tree state
171      *
172      * TODO: Is there a better home for this? Currently Choreographer
173      * is the only one with CALLBACK_ANIMATION start time, hence why this
174      * resides here.
175      *
176      * @hide
177      */
178     FrameInfo mFrameInfo = new FrameInfo();
179 
180     /**
181      * Must be kept in sync with CALLBACK_* ints below, used to index into this array.
182      * @hide
183      */
184     private static final String[] CALLBACK_TRACE_TITLES = {
185             "input", "animation", "traversal", "commit"
186     };
187 
188     /**
189      * Callback type: Input callback.  Runs first.
190      * @hide
191      */
192     public static final int CALLBACK_INPUT = 0;
193 
194     /**
195      * Callback type: Animation callback.  Runs before traversals.
196      * @hide
197      */
198     public static final int CALLBACK_ANIMATION = 1;
199 
200     /**
201      * Callback type: Traversal callback.  Handles layout and draw.  Runs
202      * after all other asynchronous messages have been handled.
203      * @hide
204      */
205     public static final int CALLBACK_TRAVERSAL = 2;
206 
207     /**
208      * Callback type: Commit callback.  Handles post-draw operations for the frame.
209      * Runs after traversal completes.  The {@link #getFrameTime() frame time} reported
210      * during this callback may be updated to reflect delays that occurred while
211      * traversals were in progress in case heavy layout operations caused some frames
212      * to be skipped.  The frame time reported during this callback provides a better
213      * estimate of the start time of the frame in which animations (and other updates
214      * to the view hierarchy state) actually took effect.
215      * @hide
216      */
217     public static final int CALLBACK_COMMIT = 3;
218 
219     private static final int CALLBACK_LAST = CALLBACK_COMMIT;
220 
Choreographer(Looper looper, int vsyncSource)221     private Choreographer(Looper looper, int vsyncSource) {
222         mLooper = looper;
223         mHandler = new FrameHandler(looper);
224         mDisplayEventReceiver = USE_VSYNC
225                 ? new FrameDisplayEventReceiver(looper, vsyncSource)
226                 : null;
227         mLastFrameTimeNanos = Long.MIN_VALUE;
228 
229         mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
230 
231         mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
232         for (int i = 0; i <= CALLBACK_LAST; i++) {
233             mCallbackQueues[i] = new CallbackQueue();
234         }
235     }
236 
getRefreshRate()237     private static float getRefreshRate() {
238         DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
239                 Display.DEFAULT_DISPLAY);
240         return di.getMode().getRefreshRate();
241     }
242 
243     /**
244      * Gets the choreographer for the calling thread.  Must be called from
245      * a thread that already has a {@link android.os.Looper} associated with it.
246      *
247      * @return The choreographer for this thread.
248      * @throws IllegalStateException if the thread does not have a looper.
249      */
getInstance()250     public static Choreographer getInstance() {
251         return sThreadInstance.get();
252     }
253 
254     /**
255      * @hide
256      */
getSfInstance()257     public static Choreographer getSfInstance() {
258         return sSfThreadInstance.get();
259     }
260 
261     /** Destroys the calling thread's choreographer
262      * @hide
263      */
releaseInstance()264     public static void releaseInstance() {
265         Choreographer old = sThreadInstance.get();
266         sThreadInstance.remove();
267         old.dispose();
268     }
269 
dispose()270     private void dispose() {
271         mDisplayEventReceiver.dispose();
272     }
273 
274     /**
275      * The amount of time, in milliseconds, between each frame of the animation.
276      * <p>
277      * This is a requested time that the animation will attempt to honor, but the actual delay
278      * between frames may be different, depending on system load and capabilities. This is a static
279      * function because the same delay will be applied to all animations, since they are all
280      * run off of a single timing loop.
281      * </p><p>
282      * The frame delay may be ignored when the animation system uses an external timing
283      * source, such as the display refresh rate (vsync), to govern animations.
284      * </p>
285      *
286      * @return the requested time between frames, in milliseconds
287      * @hide
288      */
getFrameDelay()289     public static long getFrameDelay() {
290         return sFrameDelay;
291     }
292 
293     /**
294      * The amount of time, in milliseconds, between each frame of the animation.
295      * <p>
296      * This is a requested time that the animation will attempt to honor, but the actual delay
297      * between frames may be different, depending on system load and capabilities. This is a static
298      * function because the same delay will be applied to all animations, since they are all
299      * run off of a single timing loop.
300      * </p><p>
301      * The frame delay may be ignored when the animation system uses an external timing
302      * source, such as the display refresh rate (vsync), to govern animations.
303      * </p>
304      *
305      * @param frameDelay the requested time between frames, in milliseconds
306      * @hide
307      */
setFrameDelay(long frameDelay)308     public static void setFrameDelay(long frameDelay) {
309         sFrameDelay = frameDelay;
310     }
311 
312     /**
313      * Subtracts typical frame delay time from a delay interval in milliseconds.
314      * <p>
315      * This method can be used to compensate for animation delay times that have baked
316      * in assumptions about the frame delay.  For example, it's quite common for code to
317      * assume a 60Hz frame time and bake in a 16ms delay.  When we call
318      * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
319      * posting the animation callback but let the animation timer take care of the remaining
320      * frame delay time.
321      * </p><p>
322      * This method is somewhat conservative about how much of the frame delay it
323      * subtracts.  It uses the same value returned by {@link #getFrameDelay} which by
324      * default is 10ms even though many parts of the system assume 16ms.  Consequently,
325      * we might still wait 6ms before posting an animation callback that we want to run
326      * on the next frame, but this is much better than waiting a whole 16ms and likely
327      * missing the deadline.
328      * </p>
329      *
330      * @param delayMillis The original delay time including an assumed frame delay.
331      * @return The adjusted delay time with the assumed frame delay subtracted out.
332      * @hide
333      */
subtractFrameDelay(long delayMillis)334     public static long subtractFrameDelay(long delayMillis) {
335         final long frameDelay = sFrameDelay;
336         return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
337     }
338 
339     /**
340      * @return The refresh rate as the nanoseconds between frames
341      * @hide
342      */
getFrameIntervalNanos()343     public long getFrameIntervalNanos() {
344         return mFrameIntervalNanos;
345     }
346 
dump(String prefix, PrintWriter writer)347     void dump(String prefix, PrintWriter writer) {
348         String innerPrefix = prefix + "  ";
349         writer.print(prefix); writer.println("Choreographer:");
350         writer.print(innerPrefix); writer.print("mFrameScheduled=");
351                 writer.println(mFrameScheduled);
352         writer.print(innerPrefix); writer.print("mLastFrameTime=");
353                 writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000));
354     }
355 
356     /**
357      * Posts a callback to run on the next frame.
358      * <p>
359      * The callback runs once then is automatically removed.
360      * </p>
361      *
362      * @param callbackType The callback type.
363      * @param action The callback action to run during the next frame.
364      * @param token The callback token, or null if none.
365      *
366      * @see #removeCallbacks
367      * @hide
368      */
postCallback(int callbackType, Runnable action, Object token)369     public void postCallback(int callbackType, Runnable action, Object token) {
370         postCallbackDelayed(callbackType, action, token, 0);
371     }
372 
373     /**
374      * Posts a callback to run on the next frame after the specified delay.
375      * <p>
376      * The callback runs once then is automatically removed.
377      * </p>
378      *
379      * @param callbackType The callback type.
380      * @param action The callback action to run during the next frame after the specified delay.
381      * @param token The callback token, or null if none.
382      * @param delayMillis The delay time in milliseconds.
383      *
384      * @see #removeCallback
385      * @hide
386      */
postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis)387     public void postCallbackDelayed(int callbackType,
388             Runnable action, Object token, long delayMillis) {
389         if (action == null) {
390             throw new IllegalArgumentException("action must not be null");
391         }
392         if (callbackType < 0 || callbackType > CALLBACK_LAST) {
393             throw new IllegalArgumentException("callbackType is invalid");
394         }
395 
396         postCallbackDelayedInternal(callbackType, action, token, delayMillis);
397     }
398 
postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis)399     private void postCallbackDelayedInternal(int callbackType,
400             Object action, Object token, long delayMillis) {
401         if (DEBUG_FRAMES) {
402             Log.d(TAG, "PostCallback: type=" + callbackType
403                     + ", action=" + action + ", token=" + token
404                     + ", delayMillis=" + delayMillis);
405         }
406 
407         synchronized (mLock) {
408             final long now = SystemClock.uptimeMillis();
409             final long dueTime = now + delayMillis;
410             mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
411 
412             if (dueTime <= now) {
413                 scheduleFrameLocked(now);
414             } else {
415                 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
416                 msg.arg1 = callbackType;
417                 msg.setAsynchronous(true);
418                 mHandler.sendMessageAtTime(msg, dueTime);
419             }
420         }
421     }
422 
423     /**
424      * Removes callbacks that have the specified action and token.
425      *
426      * @param callbackType The callback type.
427      * @param action The action property of the callbacks to remove, or null to remove
428      * callbacks with any action.
429      * @param token The token property of the callbacks to remove, or null to remove
430      * callbacks with any token.
431      *
432      * @see #postCallback
433      * @see #postCallbackDelayed
434      * @hide
435      */
removeCallbacks(int callbackType, Runnable action, Object token)436     public void removeCallbacks(int callbackType, Runnable action, Object token) {
437         if (callbackType < 0 || callbackType > CALLBACK_LAST) {
438             throw new IllegalArgumentException("callbackType is invalid");
439         }
440 
441         removeCallbacksInternal(callbackType, action, token);
442     }
443 
removeCallbacksInternal(int callbackType, Object action, Object token)444     private void removeCallbacksInternal(int callbackType, Object action, Object token) {
445         if (DEBUG_FRAMES) {
446             Log.d(TAG, "RemoveCallbacks: type=" + callbackType
447                     + ", action=" + action + ", token=" + token);
448         }
449 
450         synchronized (mLock) {
451             mCallbackQueues[callbackType].removeCallbacksLocked(action, token);
452             if (action != null && token == null) {
453                 mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action);
454             }
455         }
456     }
457 
458     /**
459      * Posts a frame callback to run on the next frame.
460      * <p>
461      * The callback runs once then is automatically removed.
462      * </p>
463      *
464      * @param callback The frame callback to run during the next frame.
465      *
466      * @see #postFrameCallbackDelayed
467      * @see #removeFrameCallback
468      */
postFrameCallback(FrameCallback callback)469     public void postFrameCallback(FrameCallback callback) {
470         postFrameCallbackDelayed(callback, 0);
471     }
472 
473     /**
474      * Posts a frame callback to run on the next frame after the specified delay.
475      * <p>
476      * The callback runs once then is automatically removed.
477      * </p>
478      *
479      * @param callback The frame callback to run during the next frame.
480      * @param delayMillis The delay time in milliseconds.
481      *
482      * @see #postFrameCallback
483      * @see #removeFrameCallback
484      */
postFrameCallbackDelayed(FrameCallback callback, long delayMillis)485     public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
486         if (callback == null) {
487             throw new IllegalArgumentException("callback must not be null");
488         }
489 
490         postCallbackDelayedInternal(CALLBACK_ANIMATION,
491                 callback, FRAME_CALLBACK_TOKEN, delayMillis);
492     }
493 
494     /**
495      * Removes a previously posted frame callback.
496      *
497      * @param callback The frame callback to remove.
498      *
499      * @see #postFrameCallback
500      * @see #postFrameCallbackDelayed
501      */
removeFrameCallback(FrameCallback callback)502     public void removeFrameCallback(FrameCallback callback) {
503         if (callback == null) {
504             throw new IllegalArgumentException("callback must not be null");
505         }
506 
507         removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN);
508     }
509 
510     /**
511      * Gets the time when the current frame started.
512      * <p>
513      * This method provides the time in milliseconds when the frame started being rendered.
514      * The frame time provides a stable time base for synchronizing animations
515      * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
516      * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
517      * time helps to reduce inter-frame jitter because the frame time is fixed at the time
518      * the frame was scheduled to start, regardless of when the animations or drawing
519      * callback actually runs.  All callbacks that run as part of rendering a frame will
520      * observe the same frame time so using the frame time also helps to synchronize effects
521      * that are performed by different callbacks.
522      * </p><p>
523      * Please note that the framework already takes care to process animations and
524      * drawing using the frame time as a stable time base.  Most applications should
525      * not need to use the frame time information directly.
526      * </p><p>
527      * This method should only be called from within a callback.
528      * </p>
529      *
530      * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base.
531      *
532      * @throws IllegalStateException if no frame is in progress.
533      * @hide
534      */
getFrameTime()535     public long getFrameTime() {
536         return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
537     }
538 
539     /**
540      * Same as {@link #getFrameTime()} but with nanosecond precision.
541      *
542      * @return The frame start time, in the {@link System#nanoTime()} time base.
543      *
544      * @throws IllegalStateException if no frame is in progress.
545      * @hide
546      */
getFrameTimeNanos()547     public long getFrameTimeNanos() {
548         synchronized (mLock) {
549             if (!mCallbacksRunning) {
550                 throw new IllegalStateException("This method must only be called as "
551                         + "part of a callback while a frame is in progress.");
552             }
553             return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
554         }
555     }
556 
557     /**
558      * Like {@link #getLastFrameTimeNanos}, but always returns the last frame time, not matter
559      * whether callbacks are currently running.
560      * @return The frame start time of the last frame, in the {@link System#nanoTime()} time base.
561      * @hide
562      */
getLastFrameTimeNanos()563     public long getLastFrameTimeNanos() {
564         synchronized (mLock) {
565             return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
566         }
567     }
568 
scheduleFrameLocked(long now)569     private void scheduleFrameLocked(long now) {
570         if (!mFrameScheduled) {
571             mFrameScheduled = true;
572             if (USE_VSYNC) {
573                 if (DEBUG_FRAMES) {
574                     Log.d(TAG, "Scheduling next frame on vsync.");
575                 }
576 
577                 // If running on the Looper thread, then schedule the vsync immediately,
578                 // otherwise post a message to schedule the vsync from the UI thread
579                 // as soon as possible.
580                 if (isRunningOnLooperThreadLocked()) {
581                     scheduleVsyncLocked();
582                 } else {
583                     Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
584                     msg.setAsynchronous(true);
585                     mHandler.sendMessageAtFrontOfQueue(msg);
586                 }
587             } else {
588                 final long nextFrameTime = Math.max(
589                         mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
590                 if (DEBUG_FRAMES) {
591                     Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
592                 }
593                 Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
594                 msg.setAsynchronous(true);
595                 mHandler.sendMessageAtTime(msg, nextFrameTime);
596             }
597         }
598     }
599 
doFrame(long frameTimeNanos, int frame)600     void doFrame(long frameTimeNanos, int frame) {
601         final long startNanos;
602         synchronized (mLock) {
603             if (!mFrameScheduled) {
604                 return; // no work to do
605             }
606 
607             if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
608                 mDebugPrintNextFrameTimeDelta = false;
609                 Log.d(TAG, "Frame time delta: "
610                         + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
611             }
612 
613             long intendedFrameTimeNanos = frameTimeNanos;
614             startNanos = System.nanoTime();
615             final long jitterNanos = startNanos - frameTimeNanos;
616             if (jitterNanos >= mFrameIntervalNanos) {
617                 final long skippedFrames = jitterNanos / mFrameIntervalNanos;
618                 if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
619                     Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
620                             + "The application may be doing too much work on its main thread.");
621                 }
622                 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
623                 if (DEBUG_JANK) {
624                     Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
625                             + "which is more than the frame interval of "
626                             + (mFrameIntervalNanos * 0.000001f) + " ms!  "
627                             + "Skipping " + skippedFrames + " frames and setting frame "
628                             + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
629                 }
630                 frameTimeNanos = startNanos - lastFrameOffset;
631             }
632 
633             if (frameTimeNanos < mLastFrameTimeNanos) {
634                 if (DEBUG_JANK) {
635                     Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
636                             + "previously skipped frame.  Waiting for next vsync.");
637                 }
638                 scheduleVsyncLocked();
639                 return;
640             }
641 
642             mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
643             mFrameScheduled = false;
644             mLastFrameTimeNanos = frameTimeNanos;
645         }
646 
647         try {
648             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
649             AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
650 
651             mFrameInfo.markInputHandlingStart();
652             doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
653 
654             mFrameInfo.markAnimationsStart();
655             doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
656 
657             mFrameInfo.markPerformTraversalsStart();
658             doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
659 
660             doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
661         } finally {
662             AnimationUtils.unlockAnimationClock();
663             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
664         }
665 
666         if (DEBUG_FRAMES) {
667             final long endNanos = System.nanoTime();
668             Log.d(TAG, "Frame " + frame + ": Finished, took "
669                     + (endNanos - startNanos) * 0.000001f + " ms, latency "
670                     + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
671         }
672     }
673 
doCallbacks(int callbackType, long frameTimeNanos)674     void doCallbacks(int callbackType, long frameTimeNanos) {
675         CallbackRecord callbacks;
676         synchronized (mLock) {
677             // We use "now" to determine when callbacks become due because it's possible
678             // for earlier processing phases in a frame to post callbacks that should run
679             // in a following phase, such as an input event that causes an animation to start.
680             final long now = System.nanoTime();
681             callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
682                     now / TimeUtils.NANOS_PER_MS);
683             if (callbacks == null) {
684                 return;
685             }
686             mCallbacksRunning = true;
687 
688             // Update the frame time if necessary when committing the frame.
689             // We only update the frame time if we are more than 2 frames late reaching
690             // the commit phase.  This ensures that the frame time which is observed by the
691             // callbacks will always increase from one frame to the next and never repeat.
692             // We never want the next frame's starting frame time to end up being less than
693             // or equal to the previous frame's commit frame time.  Keep in mind that the
694             // next frame has most likely already been scheduled by now so we play it
695             // safe by ensuring the commit time is always at least one frame behind.
696             if (callbackType == Choreographer.CALLBACK_COMMIT) {
697                 final long jitterNanos = now - frameTimeNanos;
698                 Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
699                 if (jitterNanos >= 2 * mFrameIntervalNanos) {
700                     final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
701                             + mFrameIntervalNanos;
702                     if (DEBUG_JANK) {
703                         Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
704                                 + " ms which is more than twice the frame interval of "
705                                 + (mFrameIntervalNanos * 0.000001f) + " ms!  "
706                                 + "Setting frame time to " + (lastFrameOffset * 0.000001f)
707                                 + " ms in the past.");
708                         mDebugPrintNextFrameTimeDelta = true;
709                     }
710                     frameTimeNanos = now - lastFrameOffset;
711                     mLastFrameTimeNanos = frameTimeNanos;
712                 }
713             }
714         }
715         try {
716             Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
717             for (CallbackRecord c = callbacks; c != null; c = c.next) {
718                 if (DEBUG_FRAMES) {
719                     Log.d(TAG, "RunCallback: type=" + callbackType
720                             + ", action=" + c.action + ", token=" + c.token
721                             + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
722                 }
723                 c.run(frameTimeNanos);
724             }
725         } finally {
726             synchronized (mLock) {
727                 mCallbacksRunning = false;
728                 do {
729                     final CallbackRecord next = callbacks.next;
730                     recycleCallbackLocked(callbacks);
731                     callbacks = next;
732                 } while (callbacks != null);
733             }
734             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
735         }
736     }
737 
doScheduleVsync()738     void doScheduleVsync() {
739         synchronized (mLock) {
740             if (mFrameScheduled) {
741                 scheduleVsyncLocked();
742             }
743         }
744     }
745 
doScheduleCallback(int callbackType)746     void doScheduleCallback(int callbackType) {
747         synchronized (mLock) {
748             if (!mFrameScheduled) {
749                 final long now = SystemClock.uptimeMillis();
750                 if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
751                     scheduleFrameLocked(now);
752                 }
753             }
754         }
755     }
756 
scheduleVsyncLocked()757     private void scheduleVsyncLocked() {
758         mDisplayEventReceiver.scheduleVsync();
759     }
760 
isRunningOnLooperThreadLocked()761     private boolean isRunningOnLooperThreadLocked() {
762         return Looper.myLooper() == mLooper;
763     }
764 
obtainCallbackLocked(long dueTime, Object action, Object token)765     private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
766         CallbackRecord callback = mCallbackPool;
767         if (callback == null) {
768             callback = new CallbackRecord();
769         } else {
770             mCallbackPool = callback.next;
771             callback.next = null;
772         }
773         callback.dueTime = dueTime;
774         callback.action = action;
775         callback.token = token;
776         return callback;
777     }
778 
recycleCallbackLocked(CallbackRecord callback)779     private void recycleCallbackLocked(CallbackRecord callback) {
780         callback.action = null;
781         callback.token = null;
782         callback.next = mCallbackPool;
783         mCallbackPool = callback;
784     }
785 
786     /**
787      * Implement this interface to receive a callback when a new display frame is
788      * being rendered.  The callback is invoked on the {@link Looper} thread to
789      * which the {@link Choreographer} is attached.
790      */
791     public interface FrameCallback {
792         /**
793          * Called when a new display frame is being rendered.
794          * <p>
795          * This method provides the time in nanoseconds when the frame started being rendered.
796          * The frame time provides a stable time base for synchronizing animations
797          * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
798          * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
799          * time helps to reduce inter-frame jitter because the frame time is fixed at the time
800          * the frame was scheduled to start, regardless of when the animations or drawing
801          * callback actually runs.  All callbacks that run as part of rendering a frame will
802          * observe the same frame time so using the frame time also helps to synchronize effects
803          * that are performed by different callbacks.
804          * </p><p>
805          * Please note that the framework already takes care to process animations and
806          * drawing using the frame time as a stable time base.  Most applications should
807          * not need to use the frame time information directly.
808          * </p>
809          *
810          * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
811          * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
812          * to convert it to the {@link SystemClock#uptimeMillis()} time base.
813          */
doFrame(long frameTimeNanos)814         public void doFrame(long frameTimeNanos);
815     }
816 
817     private final class FrameHandler extends Handler {
FrameHandler(Looper looper)818         public FrameHandler(Looper looper) {
819             super(looper);
820         }
821 
822         @Override
handleMessage(Message msg)823         public void handleMessage(Message msg) {
824             switch (msg.what) {
825                 case MSG_DO_FRAME:
826                     doFrame(System.nanoTime(), 0);
827                     break;
828                 case MSG_DO_SCHEDULE_VSYNC:
829                     doScheduleVsync();
830                     break;
831                 case MSG_DO_SCHEDULE_CALLBACK:
832                     doScheduleCallback(msg.arg1);
833                     break;
834             }
835         }
836     }
837 
838     private final class FrameDisplayEventReceiver extends DisplayEventReceiver
839             implements Runnable {
840         private boolean mHavePendingVsync;
841         private long mTimestampNanos;
842         private int mFrame;
843 
FrameDisplayEventReceiver(Looper looper, int vsyncSource)844         public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
845             super(looper, vsyncSource);
846         }
847 
848         @Override
onVsync(long timestampNanos, int builtInDisplayId, int frame)849         public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
850             // Ignore vsync from secondary display.
851             // This can be problematic because the call to scheduleVsync() is a one-shot.
852             // We need to ensure that we will still receive the vsync from the primary
853             // display which is the one we really care about.  Ideally we should schedule
854             // vsync for a particular display.
855             // At this time Surface Flinger won't send us vsyncs for secondary displays
856             // but that could change in the future so let's log a message to help us remember
857             // that we need to fix this.
858             if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
859                 Log.d(TAG, "Received vsync from secondary display, but we don't support "
860                         + "this case yet.  Choreographer needs a way to explicitly request "
861                         + "vsync for a specific display to ensure it doesn't lose track "
862                         + "of its scheduled vsync.");
863                 scheduleVsync();
864                 return;
865             }
866 
867             // Post the vsync event to the Handler.
868             // The idea is to prevent incoming vsync events from completely starving
869             // the message queue.  If there are no messages in the queue with timestamps
870             // earlier than the frame time, then the vsync event will be processed immediately.
871             // Otherwise, messages that predate the vsync event will be handled first.
872             long now = System.nanoTime();
873             if (timestampNanos > now) {
874                 Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
875                         + " ms in the future!  Check that graphics HAL is generating vsync "
876                         + "timestamps using the correct timebase.");
877                 timestampNanos = now;
878             }
879 
880             if (mHavePendingVsync) {
881                 Log.w(TAG, "Already have a pending vsync event.  There should only be "
882                         + "one at a time.");
883             } else {
884                 mHavePendingVsync = true;
885             }
886 
887             mTimestampNanos = timestampNanos;
888             mFrame = frame;
889             Message msg = Message.obtain(mHandler, this);
890             msg.setAsynchronous(true);
891             mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
892         }
893 
894         @Override
run()895         public void run() {
896             mHavePendingVsync = false;
897             doFrame(mTimestampNanos, mFrame);
898         }
899     }
900 
901     private static final class CallbackRecord {
902         public CallbackRecord next;
903         public long dueTime;
904         public Object action; // Runnable or FrameCallback
905         public Object token;
906 
run(long frameTimeNanos)907         public void run(long frameTimeNanos) {
908             if (token == FRAME_CALLBACK_TOKEN) {
909                 ((FrameCallback)action).doFrame(frameTimeNanos);
910             } else {
911                 ((Runnable)action).run();
912             }
913         }
914     }
915 
916     private final class CallbackQueue {
917         private CallbackRecord mHead;
918 
hasDueCallbacksLocked(long now)919         public boolean hasDueCallbacksLocked(long now) {
920             return mHead != null && mHead.dueTime <= now;
921         }
922 
extractDueCallbacksLocked(long now)923         public CallbackRecord extractDueCallbacksLocked(long now) {
924             CallbackRecord callbacks = mHead;
925             if (callbacks == null || callbacks.dueTime > now) {
926                 return null;
927             }
928 
929             CallbackRecord last = callbacks;
930             CallbackRecord next = last.next;
931             while (next != null) {
932                 if (next.dueTime > now) {
933                     last.next = null;
934                     break;
935                 }
936                 last = next;
937                 next = next.next;
938             }
939             mHead = next;
940             return callbacks;
941         }
942 
addCallbackLocked(long dueTime, Object action, Object token)943         public void addCallbackLocked(long dueTime, Object action, Object token) {
944             CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
945             CallbackRecord entry = mHead;
946             if (entry == null) {
947                 mHead = callback;
948                 return;
949             }
950             if (dueTime < entry.dueTime) {
951                 callback.next = entry;
952                 mHead = callback;
953                 return;
954             }
955             while (entry.next != null) {
956                 if (dueTime < entry.next.dueTime) {
957                     callback.next = entry.next;
958                     break;
959                 }
960                 entry = entry.next;
961             }
962             entry.next = callback;
963         }
964 
removeCallbacksLocked(Object action, Object token)965         public void removeCallbacksLocked(Object action, Object token) {
966             CallbackRecord predecessor = null;
967             for (CallbackRecord callback = mHead; callback != null;) {
968                 final CallbackRecord next = callback.next;
969                 if ((action == null || callback.action == action)
970                         && (token == null || callback.token == token)) {
971                     if (predecessor != null) {
972                         predecessor.next = next;
973                     } else {
974                         mHead = next;
975                     }
976                     recycleCallbackLocked(callback);
977                 } else {
978                     predecessor = callback;
979                 }
980                 callback = next;
981             }
982         }
983     }
984 }
985