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