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