Home | History | Annotate | Download | only in view
      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 
     17 package android.view;
     18 
     19 import android.hardware.display.DisplayManagerGlobal;
     20 import android.os.Handler;
     21 import android.os.Looper;
     22 import android.os.Message;
     23 import android.os.SystemClock;
     24 import android.os.SystemProperties;
     25 import 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  */
     69 public 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 != SurfaceControl.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