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