Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b5ca025 authored by Rachel Lee's avatar Rachel Lee
Browse files

Disallow accessing data outside of vsync callback

The `FrameData` and `FrameTimeline`s are not valid
outside of the vsync callback. Throw exception if someone tries to call
the methods outside.

Test: atest ChoreographerTest
Bug: 258694738
Change-Id: I4c3733392ebee39eeb4b2043eb2a28e739c22d6e
parent c76126e3
Loading
Loading
Loading
Loading
+39 −4
Original line number Diff line number Diff line
@@ -888,7 +888,7 @@ public final class Choreographer {

    void doCallbacks(int callbackType, long frameIntervalNanos) {
        CallbackRecord callbacks;
        long frameTimeNanos = mFrameData.getFrameTimeNanos();
        long frameTimeNanos = mFrameData.mFrameTimeNanos;
        synchronized (mLock) {
            // We use "now" to determine when callbacks become due because it's possible
            // for earlier processing phases in a frame to post callbacks that should run
@@ -1042,11 +1042,23 @@ public final class Choreographer {
        private long mVsyncId = FrameInfo.INVALID_VSYNC_ID;
        private long mExpectedPresentationTimeNanos = -1;
        private long mDeadlineNanos = -1;
        private boolean mInCallback = false;

        FrameTimeline() {
            // Intentionally empty; defined so that it is not API/public by default.
        }

        void setInCallback(boolean inCallback) {
            mInCallback = inCallback;
        }

        private void checkInCallback() {
            if (!mInCallback) {
                throw new IllegalStateException(
                        "FrameTimeline is not valid outside of the vsync callback");
            }
        }

        void update(long vsyncId, long expectedPresentationTimeNanos, long deadlineNanos) {
            mVsyncId = vsyncId;
            mExpectedPresentationTimeNanos = expectedPresentationTimeNanos;
@@ -1058,6 +1070,7 @@ public final class Choreographer {
         * produced by HWUI with the timeline data stored in Surface Flinger.
         */
        public long getVsyncId() {
            checkInCallback();
            return mVsyncId;
        }

@@ -1066,6 +1079,7 @@ public final class Choreographer {
         * presented.
         */
        public long getExpectedPresentationTimeNanos() {
            checkInCallback();
            return mExpectedPresentationTimeNanos;
        }

@@ -1073,6 +1087,7 @@ public final class Choreographer {
         * The time in  {@link System#nanoTime()} timebase which this frame needs to be ready by.
         */
        public long getDeadlineNanos() {
            checkInCallback();
            return mDeadlineNanos;
        }
    }
@@ -1087,6 +1102,7 @@ public final class Choreographer {
        private final FrameTimeline[] mFrameTimelines =
                new FrameTimeline[DisplayEventReceiver.VsyncEventData.FRAME_TIMELINES_LENGTH];
        private int mPreferredFrameTimelineIndex;
        private boolean mInCallback = false;

        FrameData() {
            for (int i = 0; i < mFrameTimelines.length; i++) {
@@ -1096,6 +1112,7 @@ public final class Choreographer {

        /** The time in nanoseconds when the frame started being rendered. */
        public long getFrameTimeNanos() {
            checkInCallback();
            return mFrameTimeNanos;
        }

@@ -1103,15 +1120,31 @@ public final class Choreographer {
        @NonNull
        @SuppressLint("ArrayReturn") // For API consistency and speed.
        public FrameTimeline[] getFrameTimelines() {
            checkInCallback();
            return mFrameTimelines;
        }

        /** The platform-preferred frame timeline. */
        @NonNull
        public FrameTimeline getPreferredFrameTimeline() {
            checkInCallback();
            return mFrameTimelines[mPreferredFrameTimelineIndex];
        }

        void setInCallback(boolean inCallback) {
            mInCallback = inCallback;
            for (int i = 0; i < mFrameTimelines.length; i++) {
                mFrameTimelines[i].setInCallback(inCallback);
            }
        }

        private void checkInCallback() {
            if (!mInCallback) {
                throw new IllegalStateException(
                        "FrameData is not valid outside of the vsync callback");
            }
        }

        /**
         * Update the frame data with a {@code DisplayEventReceiver.VsyncEventData} received from
         * native.
@@ -1142,16 +1175,16 @@ public final class Choreographer {
                long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) {
            int newPreferredIndex = 0;
            final long minimumDeadline =
                    getPreferredFrameTimeline().getDeadlineNanos() + jitterNanos;
                    mFrameTimelines[mPreferredFrameTimelineIndex].mDeadlineNanos + jitterNanos;
            // Look for a non-past deadline timestamp in the existing frame data. Otherwise, binder
            // query for new frame data. Note that binder is relatively slow, O(ms), so it is
            // only called when the existing frame data does not hold a valid frame.
            while (newPreferredIndex < mFrameTimelines.length - 1
                    && mFrameTimelines[newPreferredIndex].getDeadlineNanos() < minimumDeadline) {
                    && mFrameTimelines[newPreferredIndex].mDeadlineNanos < minimumDeadline) {
                newPreferredIndex++;
            }

            long newPreferredDeadline = mFrameTimelines[newPreferredIndex].getDeadlineNanos();
            long newPreferredDeadline = mFrameTimelines[newPreferredIndex].mDeadlineNanos;
            if (newPreferredDeadline < minimumDeadline) {
                DisplayEventReceiver.VsyncEventData latestVsyncEventData =
                        displayEventReceiver.getLatestVsyncEventData();
@@ -1288,11 +1321,13 @@ public final class Choreographer {
        }

        void run(FrameData frameData) {
            frameData.setInCallback(true);
            if (token == VSYNC_CALLBACK_TOKEN) {
                ((VsyncCallback) action).onVsync(frameData);
            } else {
                run(frameData.getFrameTimeNanos());
            }
            frameData.setInCallback(false);
        }
    }