196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/*
296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Copyright (C) 2011 The Android Open Source Project
396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * you may not use this file except in compliance with the License.
696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * You may obtain a copy of the License at
796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
1096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Unless required by applicable law or agreed to in writing, software
1196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * See the License for the specific language governing permissions and
1496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * limitations under the License.
1596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */
1696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
1796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownpackage android.view;
1896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
19bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brownimport android.hardware.display.DisplayManagerGlobal;
2096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Handler;
2196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Looper;
2296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Message;
2396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemClock;
2496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemProperties;
2596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.util.Log;
265182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brownimport android.util.TimeUtils;
275182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown
285182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brownimport java.io.PrintWriter;
2996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
3096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/**
31cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * Coordinates the timing of animations, input and drawing.
32cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <p>
33cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * The choreographer receives timing pulses (such as vertical synchronization)
34cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * from the display subsystem then schedules work to occur as part of rendering
35cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * the next display frame.
36cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </p><p>
37cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * Applications typically interact with the choreographer indirectly using
38cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * higher level abstractions in the animation framework or the view hierarchy.
39cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * Here are some examples of things you can do using the higher-level APIs.
40cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </p>
41cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <ul>
42cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>To post an animation to be processed on a regular time basis synchronized with
43cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
44cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
45cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * frame, use {@link View#postOnAnimation}.</li>
46cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
47cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
48cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
49cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * next display frame, use {@link View#postInvalidateOnAnimation()} or
50cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
51cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
52cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * sync with display frame rendering, do nothing.  This already happens automatically.
53cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * {@link View#onDraw} will be called at the appropriate time.</li>
54cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </ul>
55cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <p>
56cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * However, there are a few cases where you might want to use the functions of the
57cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * choreographer directly in your application.  Here are some examples.
58cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </p>
59cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <ul>
60cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>If your application does its rendering in a different thread, possibly using GL,
61cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * or does not use the animation framework or view hierarchy at all
62cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * and you want to ensure that it is appropriately synchronized with the display, then use
63cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * {@link Choreographer#postFrameCallback}.</li>
64cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <li>... and that's about it.</li>
65cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </ul>
66cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * <p>
67cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * Each {@link Looper} thread has its own choreographer.  Other threads can
68cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * post callbacks to run on the choreographer but they will run on the {@link Looper}
69cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * to which the choreographer belongs.
70cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown * </p>
7196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */
72968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brownpublic final class Choreographer {
7396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final String TAG = "Choreographer";
7496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean DEBUG = false;
7596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The default amount of time in ms between animation frames.
7796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // When vsync is not enabled, we want to have some idea of how long we should
7896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // wait before posting the next animation message.  It is important that the
7996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // default value be less than the true inter-frame delay on all devices to avoid
8096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // situations where we might skip frames by waiting too long (we must compensate
8196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // for jitter and hardware variations).  Regardless of this value, the animation
8296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // and display loop is ultimately rate-limited by how fast new graphics buffers can
8396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // be dequeued.
8496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final long DEFAULT_FRAME_DELAY = 10;
8596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
8696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The number of milliseconds between animation frames.
8787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
8896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
8996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Thread local storage for the choreographer.
9096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final ThreadLocal<Choreographer> sThreadInstance =
9196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            new ThreadLocal<Choreographer>() {
9296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
9396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        protected Choreographer initialValue() {
9496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Looper looper = Looper.myLooper();
9596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (looper == null) {
9696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                throw new IllegalStateException("The current thread must have a looper!");
9796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
9896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            return new Choreographer(looper);
9996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
10096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    };
10196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
102ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    // Enable/disable vsync for animations and drawing.
10396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
10496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            "debug.choreographer.vsync", true);
10596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
10620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    // Enable/disable using the frame time instead of returning now.
10720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    private static final boolean USE_FRAME_TIME = SystemProperties.getBoolean(
10820c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            "debug.choreographer.frametime", true);
10920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown
1104fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown    // Set a limit to warn about skipped frames.
1114fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown    // Skipped frames imply jank.
1124fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown    private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
1134fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown            "debug.choreographer.skipwarning", 30);
1144fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown
11520c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    private static final long NANOS_PER_MS = 1000000;
11620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown
117ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private static final int MSG_DO_FRAME = 0;
118ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private static final int MSG_DO_SCHEDULE_VSYNC = 1;
119ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
12096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
121cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    // All frame callbacks posted by applications have this token.
122cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private static final Object FRAME_CALLBACK_TOKEN = new Object() {
123cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public String toString() { return "FRAME_CALLBACK_TOKEN"; }
124cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    };
125cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
12687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    private final Object mLock = new Object();
12787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
12896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private final Looper mLooper;
129968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private final FrameHandler mHandler;
130cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
131cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    // The display event receiver can only be accessed by the looper thread to which
132cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    // it is attached.  We take care to ensure that we post message to the looper
133cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    // if appropriate when interacting with the display event receiver.
1341654d0b8d9ba477a0134338838b6e5921f1aabb8Jeff Brown    private final FrameDisplayEventReceiver mDisplayEventReceiver;
135968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
136cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private CallbackRecord mCallbackPool;
13796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
138ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private final CallbackQueue[] mCallbackQueues;
139968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
140ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private boolean mFrameScheduled;
14120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    private boolean mCallbacksRunning;
14220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    private long mLastFrameTimeNanos;
14359bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown    private long mFrameIntervalNanos;
144ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown
145ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    /**
146ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * Callback type: Input callback.  Runs first.
147cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
148ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     */
149ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public static final int CALLBACK_INPUT = 0;
150ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown
151ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    /**
152ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * Callback type: Animation callback.  Runs before traversals.
153cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
154ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     */
155ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public static final int CALLBACK_ANIMATION = 1;
156ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown
157ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    /**
158ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * Callback type: Traversal callback.  Handles layout and draw.  Runs last
159ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * after all other asynchronous messages have been handled.
160cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
161ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     */
162ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public static final int CALLBACK_TRAVERSAL = 2;
163ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown
164ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
16596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
16696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private Choreographer(Looper looper) {
16796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLooper = looper;
168968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        mHandler = new FrameHandler(looper);
1691654d0b8d9ba477a0134338838b6e5921f1aabb8Jeff Brown        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
17020c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        mLastFrameTimeNanos = Long.MIN_VALUE;
171d32460c5b7bea7b06e345397fdbaca58d9732dcfJeff Brown
172bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
173ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown
174ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
175ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        for (int i = 0; i <= CALLBACK_LAST; i++) {
176ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            mCallbackQueues[i] = new CallbackQueue();
177ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        }
17896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
17996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
180bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown    private static float getRefreshRate() {
181bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown        DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
182bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown                Display.DEFAULT_DISPLAY);
183bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown        return di.refreshRate;
184bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown    }
185bd6e1500aedc5461e832f69e76341bff0e55fa2bJeff Brown
18696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
1878bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * Gets the choreographer for the calling thread.  Must be called from
1888bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * a thread that already has a {@link android.os.Looper} associated with it.
18996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
19096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return The choreographer for this thread.
19196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @throws IllegalStateException if the thread does not have a looper.
19296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
19396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static Choreographer getInstance() {
19496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sThreadInstance.get();
19596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
19696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
19796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
198cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The amount of time, in milliseconds, between each frame of the animation.
199cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
200cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * This is a requested time that the animation will attempt to honor, but the actual delay
201cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * between frames may be different, depending on system load and capabilities. This is a static
20296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
20396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
204cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p><p>
20596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
20696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
207cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
20896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
20996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return the requested time between frames, in milliseconds
210cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
21196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
21296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static long getFrameDelay() {
21396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sFrameDelay;
21496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
21596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
21696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
217cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The amount of time, in milliseconds, between each frame of the animation.
218cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
219cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * This is a requested time that the animation will attempt to honor, but the actual delay
220cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * between frames may be different, depending on system load and capabilities. This is a static
22196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
22296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
223cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p><p>
22496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
22596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
226cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
22796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
22896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param frameDelay the requested time between frames, in milliseconds
229cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
23096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
23196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static void setFrameDelay(long frameDelay) {
23296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        sFrameDelay = frameDelay;
23396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
23496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
23596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
2367ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * Subtracts typical frame delay time from a delay interval in milliseconds.
237cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
2387ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * This method can be used to compensate for animation delay times that have baked
2397ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * in assumptions about the frame delay.  For example, it's quite common for code to
2407ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * assume a 60Hz frame time and bake in a 16ms delay.  When we call
2417ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
2427ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * posting the animation callback but let the animation timer take care of the remaining
2437ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * frame delay time.
244cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p><p>
2457ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * This method is somewhat conservative about how much of the frame delay it
2467ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * subtracts.  It uses the same value returned by {@link #getFrameDelay} which by
2477ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * default is 10ms even though many parts of the system assume 16ms.  Consequently,
2487ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * we might still wait 6ms before posting an animation callback that we want to run
2497ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * on the next frame, but this is much better than waiting a whole 16ms and likely
2507ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * missing the deadline.
251cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
2527ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     *
2537ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @param delayMillis The original delay time including an assumed frame delay.
2547ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @return The adjusted delay time with the assumed frame delay subtracted out.
255cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
2567ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     */
2577ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown    public static long subtractFrameDelay(long delayMillis) {
2587ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        final long frameDelay = sFrameDelay;
2597ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
2607ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown    }
2617ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown
2625182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown    void dump(String prefix, PrintWriter writer) {
2635182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown        String innerPrefix = prefix + "  ";
2645182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown        writer.print(prefix); writer.println("Choreographer:");
2655182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown        writer.print(innerPrefix); writer.print("mFrameScheduled=");
2665182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown                writer.println(mFrameScheduled);
2675182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown        writer.print(innerPrefix); writer.print("mLastFrameTime=");
2685182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown                writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000));
2695182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown    }
2705182c780a8b42acd46a06d693ab63a0dd78c6d70Jeff Brown
2717ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown    /**
272ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * Posts a callback to run on the next frame.
273cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
274cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The callback runs once then is automatically removed.
275cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
276968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
277ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @param callbackType The callback type.
278ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @param action The callback action to run during the next frame.
2797ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @param token The callback token, or null if none.
280968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
281ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @see #removeCallbacks
282cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
283968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
284ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public void postCallback(int callbackType, Runnable action, Object token) {
285ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        postCallbackDelayed(callbackType, action, token, 0);
286968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
287968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
288968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
289cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Posts a callback to run on the next frame after the specified delay.
290cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
291cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The callback runs once then is automatically removed.
292cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
2932b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown     *
294ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @param callbackType The callback type.
295ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @param action The callback action to run during the next frame after the specified delay.
2967ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @param token The callback token, or null if none.
2972b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown     * @param delayMillis The delay time in milliseconds.
2982b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown     *
299ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @see #removeCallback
300cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
3012b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown     */
302ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public void postCallbackDelayed(int callbackType,
303ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            Runnable action, Object token, long delayMillis) {
3047ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        if (action == null) {
3057ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            throw new IllegalArgumentException("action must not be null");
3062b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown        }
307ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
308ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            throw new IllegalArgumentException("callbackType is invalid");
309ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        }
3107ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown
311cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
312cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
313cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
314cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private void postCallbackDelayedInternal(int callbackType,
315cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            Object action, Object token, long delayMillis) {
31643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        if (DEBUG) {
317ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            Log.d(TAG, "PostCallback: type=" + callbackType
318ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    + ", action=" + action + ", token=" + token
31943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    + ", delayMillis=" + delayMillis);
32043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
32143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
3227ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        synchronized (mLock) {
3237ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            final long now = SystemClock.uptimeMillis();
3247ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            final long dueTime = now + delayMillis;
325ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
3267ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown
3277ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            if (dueTime <= now) {
328ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                scheduleFrameLocked(now);
3297ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            } else {
330ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
331ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                msg.arg1 = callbackType;
332ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                msg.setAsynchronous(true);
3337ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown                mHandler.sendMessageAtTime(msg, dueTime);
3347ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            }
3352b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown        }
3362b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown    }
3372b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown
3382b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown    /**
339ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * Removes callbacks that have the specified action and token.
340968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
341ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @param callbackType The callback type.
3427ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @param action The action property of the callbacks to remove, or null to remove
3437ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * callbacks with any action.
3447ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * @param token The token property of the callbacks to remove, or null to remove
3457ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown     * callbacks with any token.
346968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
347ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @see #postCallback
348ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown     * @see #postCallbackDelayed
349cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
350968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
351ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    public void removeCallbacks(int callbackType, Runnable action, Object token) {
352ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
353ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            throw new IllegalArgumentException("callbackType is invalid");
3542b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown        }
3557ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown
356cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        removeCallbacksInternal(callbackType, action, token);
357cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
358cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
359cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private void removeCallbacksInternal(int callbackType, Object action, Object token) {
36043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        if (DEBUG) {
361ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            Log.d(TAG, "RemoveCallbacks: type=" + callbackType
362ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    + ", action=" + action + ", token=" + token);
36343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
36443ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
3657ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        synchronized (mLock) {
366ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            mCallbackQueues[callbackType].removeCallbacksLocked(action, token);
367ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            if (action != null && token == null) {
368ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action);
3697ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            }
3702b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown        }
3712b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown    }
3722b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown
37320c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    /**
374cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Posts a frame callback to run on the next frame.
375cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
376cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The callback runs once then is automatically removed.
377cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
378cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     *
379cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @param callback The frame callback to run during the next frame.
380cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     *
381cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #postFrameCallbackDelayed
382cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #removeFrameCallback
383cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     */
384cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    public void postFrameCallback(FrameCallback callback) {
385cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        postFrameCallbackDelayed(callback, 0);
386cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
387cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
388cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    /**
389cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Posts a frame callback to run on the next frame after the specified delay.
390cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
391cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The callback runs once then is automatically removed.
392cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
393cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     *
394cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @param callback The frame callback to run during the next frame.
395cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @param delayMillis The delay time in milliseconds.
396cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     *
397cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #postFrameCallback
398cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #removeFrameCallback
399cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     */
400cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
401cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        if (callback == null) {
402cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            throw new IllegalArgumentException("callback must not be null");
403cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        }
404cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
405cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        postCallbackDelayedInternal(CALLBACK_ANIMATION,
406cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                callback, FRAME_CALLBACK_TOKEN, delayMillis);
407cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
408cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
409cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    /**
410cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Removes a previously posted frame callback.
411cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     *
412cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @param callback The frame callback to remove.
41320c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     *
414cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #postFrameCallback
415cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @see #postFrameCallbackDelayed
416cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     */
417cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    public void removeFrameCallback(FrameCallback callback) {
418cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        if (callback == null) {
419cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            throw new IllegalArgumentException("callback must not be null");
420cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        }
421cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
422cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN);
423cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
424cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
425cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    /**
426cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Gets the time when the current frame started.
427cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * <p>
428cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * This method provides the time in nanoseconds when the frame started being rendered.
429cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * The frame time provides a stable time base for synchronizing animations
430cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
431cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
432cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * time helps to reduce inter-frame jitter because the frame time is fixed at the time
433cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * the frame was scheduled to start, regardless of when the animations or drawing
434cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * callback actually runs.  All callbacks that run as part of rendering a frame will
435cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * observe the same frame time so using the frame time also helps to synchronize effects
436cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * that are performed by different callbacks.
437cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p><p>
438cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Please note that the framework already takes care to process animations and
439cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * drawing using the frame time as a stable time base.  Most applications should
440cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * not need to use the frame time information directly.
441cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p><p>
44220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * This method should only be called from within a callback.
443cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * </p>
44420c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     *
44520c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base.
44620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     *
44720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * @throws IllegalStateException if no frame is in progress.
448cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
44920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     */
45020c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    public long getFrameTime() {
45120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        return getFrameTimeNanos() / NANOS_PER_MS;
45220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    }
45320c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown
45420c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    /**
45520c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * Same as {@link #getFrameTime()} but with nanosecond precision.
45620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     *
45720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * @return The frame start time, in the {@link System#nanoTime()} time base.
45820c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     *
45920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     * @throws IllegalStateException if no frame is in progress.
460cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * @hide
46120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown     */
46220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    public long getFrameTimeNanos() {
46320c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        synchronized (mLock) {
46420c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            if (!mCallbacksRunning) {
46520c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                throw new IllegalStateException("This method must only be called as "
46620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                        + "part of a callback while a frame is in progress.");
46720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            }
46820c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
46920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        }
47020c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown    }
47120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown
472ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    private void scheduleFrameLocked(long now) {
473ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown        if (!mFrameScheduled) {
474ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            mFrameScheduled = true;
4754a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown            if (USE_VSYNC) {
4764a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                if (DEBUG) {
477ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    Log.d(TAG, "Scheduling next frame on vsync.");
4784a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                }
4794a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown
4804a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                // If running on the Looper thread, then schedule the vsync immediately,
4814a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                // otherwise post a message to schedule the vsync from the UI thread
4824a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                // as soon as possible.
4834a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                if (isRunningOnLooperThreadLocked()) {
4847ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown                    scheduleVsyncLocked();
4854a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                } else {
486e0dbd002750856e55d637e883b629e09adfc8a4eJeff Brown                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
487e0dbd002750856e55d637e883b629e09adfc8a4eJeff Brown                    msg.setAsynchronous(true);
488e0dbd002750856e55d637e883b629e09adfc8a4eJeff Brown                    mHandler.sendMessageAtFrontOfQueue(msg);
4894a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                }
4904a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown            } else {
49120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                final long nextFrameTime = Math.max(
49220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                        mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
4934a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                if (DEBUG) {
494ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
4954a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown                }
496ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
497e0dbd002750856e55d637e883b629e09adfc8a4eJeff Brown                msg.setAsynchronous(true);
498ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                mHandler.sendMessageAtTime(msg, nextFrameTime);
4994a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown            }
5004a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown        }
5014a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown    }
5024a06c8008b2edd6677f9a411af79b0a4971b87feJeff Brown
503cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    void doFrame(long frameTimeNanos, int frame) {
50459bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown        final long startNanos;
50587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
506ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            if (!mFrameScheduled) {
50787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown                return; // no work to do
50887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            }
50920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown
51020c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            startNanos = System.nanoTime();
511cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            final long jitterNanos = startNanos - frameTimeNanos;
51259bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown            if (jitterNanos >= mFrameIntervalNanos) {
5134fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
5144fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
515265f1ccc5128319d81eee70ee2d2ae81573efb11Jeff Brown                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
5164fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                            + "The application may be doing too much work on its main thread.");
5174fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                }
51859bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
51959bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                if (DEBUG) {
52059bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                    Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
52159bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                            + "which is more than the frame interval of "
52259bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                            + (mFrameIntervalNanos * 0.000001f) + " ms!  "
5234fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                            + "Skipping " + skippedFrames + " frames and setting frame "
5244fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                            + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
52559bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                }
526cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                frameTimeNanos = startNanos - lastFrameOffset;
52759bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown            }
52859bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown
529cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            if (frameTimeNanos < mLastFrameTimeNanos) {
53059bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                if (DEBUG) {
53159bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
5324fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                            + "previously skipped frame.  Waiting for next vsync.");
53359bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                }
53459bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                scheduleVsyncLocked();
53559bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown                return;
53659bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown            }
53759bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown
53859bbef0cd781f4933fd8a0a85b6067f36e529e02Jeff Brown            mFrameScheduled = false;
539cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            mLastFrameTimeNanos = frameTimeNanos;
54096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
54196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
542cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
543cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
544cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
545968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
54687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (DEBUG) {
54720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            final long endNanos = System.nanoTime();
548ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            Log.d(TAG, "Frame " + frame + ": Finished, took "
54920c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                    + (endNanos - startNanos) * 0.000001f + " ms, latency "
550cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                    + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
55196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
55296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
55396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
554cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    void doCallbacks(int callbackType, long frameTimeNanos) {
555cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        CallbackRecord callbacks;
55687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
557cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            // We use "now" to determine when callbacks become due because it's possible
558cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            // for earlier processing phases in a frame to post callbacks that should run
559cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            // in a following phase, such as an input event that causes an animation to start.
56020c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            final long now = SystemClock.uptimeMillis();
56120c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
56220c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            if (callbacks == null) {
56320c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                return;
56420c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            }
56520c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown            mCallbacksRunning = true;
56696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
56720c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        try {
568cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            for (CallbackRecord c = callbacks; c != null; c = c.next) {
569ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                if (DEBUG) {
570ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    Log.d(TAG, "RunCallback: type=" + callbackType
571ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                            + ", action=" + c.action + ", token=" + c.token
572ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                            + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
573ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                }
574cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                c.run(frameTimeNanos);
575968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
57620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown        } finally {
577ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            synchronized (mLock) {
57820c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                mCallbacksRunning = false;
579ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                do {
580cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                    final CallbackRecord next = callbacks.next;
581ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    recycleCallbackLocked(callbacks);
582ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    callbacks = next;
583ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                } while (callbacks != null);
584ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            }
58587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
58696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
58796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
588968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doScheduleVsync() {
58958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        synchronized (mLock) {
590ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            if (mFrameScheduled) {
5917ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown                scheduleVsyncLocked();
5927ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            }
59358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        }
59458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
59558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
596ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown    void doScheduleCallback(int callbackType) {
5977ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        synchronized (mLock) {
598ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown            if (!mFrameScheduled) {
599ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                final long now = SystemClock.uptimeMillis();
600ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
601ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    scheduleFrameLocked(now);
602ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                }
6037ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown            }
60496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
60596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
60696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
6077ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown    private void scheduleVsyncLocked() {
6087ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        mDisplayEventReceiver.scheduleVsync();
6097ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown    }
6107ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown
61158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private boolean isRunningOnLooperThreadLocked() {
61258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        return Looper.myLooper() == mLooper;
61358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
61458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
615cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
616cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        CallbackRecord callback = mCallbackPool;
617968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (callback == null) {
618cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            callback = new CallbackRecord();
619968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        } else {
620968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mCallbackPool = callback.next;
621968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callback.next = null;
622968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
6237ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        callback.dueTime = dueTime;
6247ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        callback.action = action;
6257ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        callback.token = token;
626968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        return callback;
627968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
628968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
629cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private void recycleCallbackLocked(CallbackRecord callback) {
6307ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        callback.action = null;
6317ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        callback.token = null;
632968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        callback.next = mCallbackPool;
633968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        mCallbackPool = callback;
634968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
635968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
636cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    /**
637cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * Implement this interface to receive a callback when a new display frame is
638cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * being rendered.  The callback is invoked on the {@link Looper} thread to
639cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     * which the {@link Choreographer} is attached.
640cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown     */
641cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    public interface FrameCallback {
642cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        /**
643cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * Called when a new display frame is being rendered.
644cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * <p>
645cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * This method provides the time in nanoseconds when the frame started being rendered.
646cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * The frame time provides a stable time base for synchronizing animations
647cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
648cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
649cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * time helps to reduce inter-frame jitter because the frame time is fixed at the time
650cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * the frame was scheduled to start, regardless of when the animations or drawing
651cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * callback actually runs.  All callbacks that run as part of rendering a frame will
652cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * observe the same frame time so using the frame time also helps to synchronize effects
653cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * that are performed by different callbacks.
654cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * </p><p>
655cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * Please note that the framework already takes care to process animations and
656cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * drawing using the frame time as a stable time base.  Most applications should
657cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * not need to use the frame time information directly.
658cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * </p>
659cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         *
660cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
661cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
662cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         * to convert it to the {@link SystemClock#uptimeMillis()} time base.
663cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown         */
664cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public void doFrame(long frameTimeNanos);
665cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    }
666cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
667968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private final class FrameHandler extends Handler {
668968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public FrameHandler(Looper looper) {
669968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            super(looper);
670968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
671968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
672968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        @Override
673968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public void handleMessage(Message msg) {
674968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            switch (msg.what) {
675ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                case MSG_DO_FRAME:
67620c4f87b2916d05e860d11568d7db6b2d340e909Jeff Brown                    doFrame(System.nanoTime(), 0);
677968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
678968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                case MSG_DO_SCHEDULE_VSYNC:
679968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    doScheduleVsync();
680968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
681ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                case MSG_DO_SCHEDULE_CALLBACK:
682ebb2d8d708c5c58c79ae88ac2bd10450a856f702Jeff Brown                    doScheduleCallback(msg.arg1);
6832b6cb9a27e6f11fb30c9b9baaa5fc02f29f4072eJeff Brown                    break;
684968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
685968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
686968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
687968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
688b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
689b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            implements Runnable {
6904fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown        private boolean mHavePendingVsync;
691b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown        private long mTimestampNanos;
692b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown        private int mFrame;
693b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown
69496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public FrameDisplayEventReceiver(Looper looper) {
69596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            super(looper);
69696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
69796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
69896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
699e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
700e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // Ignore vsync from secondary display.
701e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // This can be problematic because the call to scheduleVsync() is a one-shot.
702e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // We need to ensure that we will still receive the vsync from the primary
703e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // display which is the one we really care about.  Ideally we should schedule
704e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // vsync for a particular display.
705e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // At this time Surface Flinger won't send us vsyncs for secondary displays
706e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // but that could change in the future so let's log a message to help us remember
707e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            // that we need to fix this.
7083866f0d581ceaa165710feeee9f37fe1b0d7067dMathias Agopian            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
709e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                Log.d(TAG, "Received vsync from secondary display, but we don't support "
710e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                        + "this case yet.  Choreographer needs a way to explicitly request "
711e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                        + "vsync for a specific display to ensure it doesn't lose track "
712e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                        + "of its scheduled vsync.");
713e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                scheduleVsync();
714e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown                return;
715e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown            }
716e87bf030766198bf5e1fe846167dba766e27fb3fJeff Brown
717b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            // Post the vsync event to the Handler.
718b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            // The idea is to prevent incoming vsync events from completely starving
719b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            // the message queue.  If there are no messages in the queue with timestamps
720b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            // earlier than the frame time, then the vsync event will be processed immediately.
721b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            // Otherwise, messages that predate the vsync event will be handled first.
7224fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown            long now = System.nanoTime();
7234fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown            if (timestampNanos > now) {
7244fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
7254fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                        + " ms in the future!  Check that graphics HAL is generating vsync "
7264fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                        + "timestamps using the correct timebase.");
7274fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown                timestampNanos = now;
7284fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown            }
7294fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown
730ba726113e525823f2594507d098f2d99426655f6Jeff Brown            if (mHavePendingVsync) {
731ba726113e525823f2594507d098f2d99426655f6Jeff Brown                Log.w(TAG, "Already have a pending vsync event.  There should only be "
732ba726113e525823f2594507d098f2d99426655f6Jeff Brown                        + "one at a time.");
733ba726113e525823f2594507d098f2d99426655f6Jeff Brown            } else {
734ba726113e525823f2594507d098f2d99426655f6Jeff Brown                mHavePendingVsync = true;
735ba726113e525823f2594507d098f2d99426655f6Jeff Brown            }
7364fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown
737b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            mTimestampNanos = timestampNanos;
738b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            mFrame = frame;
739b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            Message msg = Message.obtain(mHandler, this);
740b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            msg.setAsynchronous(true);
741b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
742b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown        }
743b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown
744b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown        @Override
745b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown        public void run() {
7464fdf9c6e2a177845bb4cc20f69b83555de209144Jeff Brown            mHavePendingVsync = false;
747b080660dfd69ea6f0a034946b2ff8d94e97a2537Jeff Brown            doFrame(mTimestampNanos, mFrame);
74896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
74996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
750968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
751cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown    private static final class CallbackRecord {
752cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public CallbackRecord next;
7537ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        public long dueTime;
754cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public Object action; // Runnable or FrameCallback
7557ae9d5faad5816f7e567ec1ec77e78d746cf7e5cJeff Brown        public Object token;
756cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown
757cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public void run(long frameTimeNanos) {
758cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            if (token == FRAME_CALLBACK_TOKEN) {
759cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                ((FrameCallback)action).doFrame(frameTimeNanos);
760cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            } else {
761cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                ((Runnable)action).run();
762cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            }
763cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        }
764968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
76543ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
76643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown    private final class CallbackQueue {
767cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        private CallbackRecord mHead;
76843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
76943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        public boolean hasDueCallbacksLocked(long now) {
77043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            return mHead != null && mHead.dueTime <= now;
77143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
77243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
773cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public CallbackRecord extractDueCallbacksLocked(long now) {
774cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord callbacks = mHead;
77543ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            if (callbacks == null || callbacks.dueTime > now) {
77643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                return null;
77743ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
77843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
779cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord last = callbacks;
780cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord next = last.next;
78143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            while (next != null) {
78243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                if (next.dueTime > now) {
78343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    last.next = null;
78443ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    break;
78543ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                }
78643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                last = next;
78743ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                next = next.next;
78843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
78943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            mHead = next;
79043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            return callbacks;
79143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
79243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
793cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public void addCallbackLocked(long dueTime, Object action, Object token) {
794cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
795cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord entry = mHead;
79643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            if (entry == null) {
79743ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                mHead = callback;
79843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                return;
79943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
80043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            if (dueTime < entry.dueTime) {
80143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                callback.next = entry;
80243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                mHead = callback;
80343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                return;
80443ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
80543ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            while (entry.next != null) {
80643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                if (dueTime < entry.next.dueTime) {
80743ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    callback.next = entry.next;
80843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    break;
80943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                }
81043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                entry = entry.next;
81143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
81243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            entry.next = callback;
81343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
81443ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown
815cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown        public void removeCallbacksLocked(Object action, Object token) {
816cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            CallbackRecord predecessor = null;
817cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown            for (CallbackRecord callback = mHead; callback != null;) {
818cae804901eb5761e42d5bac7cdd6f15d37e3ceb3Jeff Brown                final CallbackRecord next = callback.next;
81943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                if ((action == null || callback.action == action)
82043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                        && (token == null || callback.token == token)) {
82143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    if (predecessor != null) {
82243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                        predecessor.next = next;
82343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    } else {
82443ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                        mHead = next;
82543ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    }
82643ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    recycleCallbackLocked(callback);
82743ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                } else {
82843ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                    predecessor = callback;
82943ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                }
83043ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown                callback = next;
83143ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown            }
83243ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown        }
83343ea54bdc343a913f62885304796e4ab1bca4ef1Jeff Brown    }
83496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown}
835