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