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