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