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