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