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