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