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