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

Commit 91a0bbdd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Ensure all FrameTracker invocation run on either main or ui thread" into tm-qpr-dev

parents 4b2c05a5 f7bd2bc6
Loading
Loading
Loading
Loading
+185 −118
Original line number Original line Diff line number Diff line
@@ -26,10 +26,12 @@ import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;


import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT;


import android.annotation.IntDef;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.graphics.HardwareRendererObserver;
import android.graphics.HardwareRendererObserver;
import android.os.Handler;
import android.os.Handler;
import android.os.Trace;
import android.os.Trace;
@@ -85,8 +87,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    public @interface Reasons {
    public @interface Reasons {
    }
    }


    @VisibleForTesting
    public final InteractionJankMonitor mMonitor;
    private final HardwareRendererObserver mObserver;
    private final HardwareRendererObserver mObserver;
    private SurfaceControl mSurfaceControl;
    private final int mTraceThresholdMissedFrames;
    private final int mTraceThresholdMissedFrames;
    private final int mTraceThresholdFrameTimeMillis;
    private final int mTraceThresholdFrameTimeMillis;
    private final ThreadedRendererWrapper mRendererWrapper;
    private final ThreadedRendererWrapper mRendererWrapper;
@@ -99,17 +102,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    private final Handler mHandler;
    private final Handler mHandler;
    private final ChoreographerWrapper mChoreographer;
    private final ChoreographerWrapper mChoreographer;
    private final StatsLogWrapper mStatsLog;
    private final StatsLogWrapper mStatsLog;
    private final Object mLock = InteractionJankMonitor.getInstance().getLock();
    private final boolean mDeferMonitoring;
    private final boolean mDeferMonitoring;
    private final FrameTrackerListener mListener;


    @VisibleForTesting
    @VisibleForTesting
    public final boolean mSurfaceOnly;
    public final boolean mSurfaceOnly;


    private SurfaceControl mSurfaceControl;
    private long mBeginVsyncId = INVALID_ID;
    private long mBeginVsyncId = INVALID_ID;
    private long mEndVsyncId = INVALID_ID;
    private long mEndVsyncId = INVALID_ID;
    private boolean mMetricsFinalized;
    private boolean mMetricsFinalized;
    private boolean mCancelled = false;
    private boolean mCancelled = false;
    private FrameTrackerListener mListener;
    private boolean mTracingStarted = false;
    private boolean mTracingStarted = false;
    private Runnable mWaitForFinishTimedOut;
    private Runnable mWaitForFinishTimedOut;


@@ -142,16 +145,52 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            this.jankType = jankType;
            this.jankType = jankType;
            this.isFirstFrame = isFirstFrame;
            this.isFirstFrame = isFirstFrame;
        }
        }

        @Override
        public String toString() {
            StringBuilder str = new StringBuilder();
            switch (jankType) {
                case JANK_NONE:
                    str.append("JANK_NONE");
                    break;
                case JANK_APP_DEADLINE_MISSED:
                    str.append("JANK_APP_DEADLINE_MISSED");
                    break;
                case JANK_SURFACEFLINGER_DEADLINE_MISSED:
                    str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
                    break;
                case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
                    str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
                    break;
                case DISPLAY_HAL:
                    str.append("DISPLAY_HAL");
                    break;
                case PREDICTION_ERROR:
                    str.append("PREDICTION_ERROR");
                    break;
                case SURFACE_FLINGER_SCHEDULING:
                    str.append("SURFACE_FLINGER_SCHEDULING");
                    break;
                default:
                    str.append("UNKNOWN: ").append(jankType);
                    break;
            }
            str.append(", ").append(frameVsyncId);
            str.append(", ").append(totalDurationNanos);
            return str.toString();
        }
    }
    }


    public FrameTracker(@NonNull Session session, @NonNull Handler handler,
    public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session,
            @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
            @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer,
            @Nullable ViewRootWrapper viewRootWrapper,
            @NonNull SurfaceControlWrapper surfaceControlWrapper,
            @NonNull SurfaceControlWrapper surfaceControlWrapper,
            @NonNull ChoreographerWrapper choreographer,
            @NonNull ChoreographerWrapper choreographer,
            @Nullable FrameMetricsWrapper metrics,
            @Nullable FrameMetricsWrapper metrics,
            @NonNull StatsLogWrapper statsLog,
            @NonNull StatsLogWrapper statsLog,
            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
            @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
            @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
        mMonitor = monitor;
        mSurfaceOnly = config.isSurfaceOnly();
        mSurfaceOnly = config.isSurfaceOnly();
        mSession = session;
        mSession = session;
        mHandler = handler;
        mHandler = handler;
@@ -186,17 +225,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
            mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
                @Override
                @Override
                public void surfaceCreated(SurfaceControl.Transaction t) {
                public void surfaceCreated(SurfaceControl.Transaction t) {
                    synchronized (mLock) {
                    getHandler().runWithScissors(() -> {
                        if (mSurfaceControl == null) {
                        if (mSurfaceControl == null) {
                            mSurfaceControl = mViewRoot.getSurfaceControl();
                            mSurfaceControl = mViewRoot.getSurfaceControl();
                            if (mBeginVsyncId != INVALID_ID) {
                            if (mBeginVsyncId != INVALID_ID) {
                                mSurfaceControlWrapper.addJankStatsListener(
                                // Previous begin invocation is not successfully, begin it again.
                                        FrameTracker.this, mSurfaceControl);
                                begin();
                                markEvent("FT#deferMonitoring");
                                postTraceStartMarker();
                            }
                            }
                            }
                        }
                        }
                    }, EXECUTOR_TASK_TIMEOUT);
                }
                }


                @Override
                @Override
@@ -208,8 +245,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener


                    // Wait a while to give the system a chance for the remaining
                    // Wait a while to give the system a chance for the remaining
                    // frames to arrive, then force finish the session.
                    // frames to arrive, then force finish the session.
                    mHandler.postDelayed(() -> {
                    getHandler().postDelayed(() -> {
                        synchronized (mLock) {
                        if (DEBUG) {
                        if (DEBUG) {
                            Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
                            Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
                                    + ", finalized=" + mMetricsFinalized
                                    + ", finalized=" + mMetricsFinalized
@@ -220,7 +256,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                            end(REASON_END_SURFACE_DESTROYED);
                            end(REASON_END_SURFACE_DESTROYED);
                            finish();
                            finish();
                        }
                        }
                        }
                    }, 50);
                    }, 50);
                }
                }
            };
            };
@@ -230,35 +265,42 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        }
        }
    }
    }


    @VisibleForTesting
    public Handler getHandler() {
        return mHandler;
    }

    /**
    /**
     * Begin a trace session of the CUJ.
     * Begin a trace session of the CUJ.
     */
     */
    @UiThread
    public void begin() {
    public void begin() {
        synchronized (mLock) {
        final long currentVsync = mChoreographer.getVsyncId();
        final long currentVsync = mChoreographer.getVsyncId();
        // In normal case, we should begin at the next frame,
        // In normal case, we should begin at the next frame,
        // the id of the next frame is not simply increased by 1,
        // the id of the next frame is not simply increased by 1,
        // but we can exclude the current frame at least.
        // but we can exclude the current frame at least.
        if (mBeginVsyncId == INVALID_ID) {
            mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
            mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
        }
        if (mSurfaceControl != null) {
            if (DEBUG) {
            if (DEBUG) {
                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
                        + ", defer=" + mDeferMonitoring);
                        + ", defer=" + mDeferMonitoring + ", current=" + currentVsync);
            }
            }
            if (mSurfaceControl != null) {
            if (mDeferMonitoring && currentVsync < mBeginVsyncId) {
                if (mDeferMonitoring) {
                markEvent("FT#deferMonitoring");
                markEvent("FT#deferMonitoring");
                // Normal case, we begin the instrument from the very beginning,
                // Normal case, we begin the instrument from the very beginning,
                // will exclude the first frame.
                // will exclude the first frame.
                    postTraceStartMarker();
                postTraceStartMarker(this::beginInternal);
            } else {
            } else {
                // If we don't begin the instrument from the very beginning,
                // If we don't begin the instrument from the very beginning,
                // there is no need to skip the frame where the begin invocation happens.
                // there is no need to skip the frame where the begin invocation happens.
                beginInternal();
                beginInternal();
            }
            }
                mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
        } else {
            }
            if (DEBUG) {
            if (!mSurfaceOnly) {
                Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ="
                mRendererWrapper.addObserver(mObserver);
                        + mSession.getName());
            }
            }
        }
        }
    }
    }
@@ -267,27 +309,29 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     * Start trace section at appropriate time.
     * Start trace section at appropriate time.
     */
     */
    @VisibleForTesting
    @VisibleForTesting
    public void postTraceStartMarker() {
    public void postTraceStartMarker(Runnable action) {
        mChoreographer.mChoreographer.postCallback(
        mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, action, null);
                Choreographer.CALLBACK_INPUT, this::beginInternal, null);
    }
    }


    @UiThread
    private void beginInternal() {
    private void beginInternal() {
        synchronized (mLock) {
        if (mCancelled || mEndVsyncId != INVALID_ID) {
        if (mCancelled || mEndVsyncId != INVALID_ID) {
            return;
            return;
        }
        }
        mTracingStarted = true;
        mTracingStarted = true;
        markEvent("FT#begin");
        markEvent("FT#begin");
        Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
        if (!mSurfaceOnly) {
            mRendererWrapper.addObserver(mObserver);
        }
        }
    }
    }


    /**
    /**
     * End the trace session of the CUJ.
     * End the trace session of the CUJ.
     */
     */
    @UiThread
    public boolean end(@Reasons int reason) {
    public boolean end(@Reasons int reason) {
        synchronized (mLock) {
        if (mCancelled || mEndVsyncId != INVALID_ID) return false;
        if (mCancelled || mEndVsyncId != INVALID_ID) return false;
        mEndVsyncId = mChoreographer.getVsyncId();
        mEndVsyncId = mChoreographer.getVsyncId();
        // Cancel the session if:
        // Cancel the session if:
@@ -314,24 +358,23 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
                Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
                finish();
                finish();
            };
            };
                mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
            getHandler().postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
            notifyCujEvent(ACTION_SESSION_END);
            notifyCujEvent(ACTION_SESSION_END);
            return true;
            return true;
        }
        }
    }
    }
    }


    /**
    /**
     * Cancel the trace session of the CUJ.
     * Cancel the trace session of the CUJ.
     */
     */
    @UiThread
    public boolean cancel(@Reasons int reason) {
    public boolean cancel(@Reasons int reason) {
        synchronized (mLock) {
        final boolean cancelFromEnd =
        final boolean cancelFromEnd =
                reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
                reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
        if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
        if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
        mCancelled = true;
        mCancelled = true;
        markEvent("FT#cancel#" + reason);
        markEvent("FT#cancel#" + reason);
            // We don't need to end the trace section if it never begun.
        // We don't need to end the trace section if it has never begun.
        if (mTracingStarted) {
        if (mTracingStarted) {
            Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
            Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        }
        }
@@ -350,7 +393,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        notifyCujEvent(ACTION_SESSION_CANCEL);
        notifyCujEvent(ACTION_SESSION_CANCEL);
        return true;
        return true;
    }
    }
    }


    private void markEvent(String desc) {
    private void markEvent(String desc) {
        Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
        Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
@@ -364,8 +406,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener


    @Override
    @Override
    public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
    public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
        synchronized (mLock) {
        postCallback(() -> {
            if (mCancelled) {
            if (mCancelled || mMetricsFinalized) {
                return;
                return;
            }
            }


@@ -384,10 +426,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                }
                }
            }
            }
            processJankInfos();
            processJankInfos();
        });
    }
    }

    /**
     * For easier argument capture.
     */
    @VisibleForTesting
    public void postCallback(Runnable callback) {
        getHandler().post(callback);
    }
    }


    private @Nullable JankInfo findJankInfo(long frameVsyncId) {
    @Nullable
    private JankInfo findJankInfo(long frameVsyncId) {
        return mJankInfos.get((int) frameVsyncId);
        return mJankInfos.get((int) frameVsyncId);
    }
    }


@@ -400,8 +451,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener


    @Override
    @Override
    public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
    public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
        synchronized (mLock) {
        postCallback(() -> {
            if (mCancelled) {
            if (mCancelled || mMetricsFinalized) {
                return;
                return;
            }
            }


@@ -426,9 +477,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        frameVsyncId, totalDurationNanos, isFirstFrame));
                        frameVsyncId, totalDurationNanos, isFirstFrame));
            }
            }
            processJankInfos();
            processJankInfos();
        }
        });
    }
    }


    @UiThread
    private boolean hasReceivedCallbacksAfterEnd() {
    private boolean hasReceivedCallbacksAfterEnd() {
        if (mEndVsyncId == INVALID_ID) {
        if (mEndVsyncId == INVALID_ID) {
            return false;
            return false;
@@ -451,6 +503,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        return false;
        return false;
    }
    }


    @UiThread
    private void processJankInfos() {
    private void processJankInfos() {
        if (mMetricsFinalized) {
        if (mMetricsFinalized) {
            return;
            return;
@@ -467,9 +520,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
                : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
    }
    }


    @UiThread
    private void finish() {
    private void finish() {
        mHandler.removeCallbacks(mWaitForFinishTimedOut);
        getHandler().removeCallbacks(mWaitForFinishTimedOut);
        mWaitForFinishTimedOut = null;
        mWaitForFinishTimedOut = null;
        if (mMetricsFinalized || mCancelled) return;
        markEvent("FT#finish#" + mJankInfos.size());
        mMetricsFinalized = true;
        mMetricsFinalized = true;


        // The tracing has been ended, remove the observer, see if need to trigger perfetto.
        // The tracing has been ended, remove the observer, see if need to trigger perfetto.
@@ -496,7 +552,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                totalFramesCount++;
                totalFramesCount++;
                boolean missedFrame = false;
                boolean missedFrame = false;
                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
                    Log.w(TAG, "Missed App frame:" + info.jankType);
                    Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName());
                    missedAppFramesCount++;
                    missedAppFramesCount++;
                    missedFrame = true;
                    missedFrame = true;
                }
                }
@@ -505,7 +561,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
                        || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
                        || (info.jankType & PREDICTION_ERROR) != 0) {
                        || (info.jankType & PREDICTION_ERROR) != 0) {
                    Log.w(TAG, "Missed SF frame:" + info.jankType);
                    Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName());
                    missedSfFramesCount++;
                    missedSfFramesCount++;
                    missedFrame = true;
                    missedFrame = true;
                }
                }
@@ -520,13 +576,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                // TODO (b/174755489): Early latch currently gets fired way too often, so we have
                // TODO (b/174755489): Early latch currently gets fired way too often, so we have
                // to ignore it for now.
                // to ignore it for now.
                if (!mSurfaceOnly && !info.hwuiCallbackFired) {
                if (!mSurfaceOnly && !info.hwuiCallbackFired) {
                    Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
                    Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId
                            + ", CUJ=" + mSession.getName());
                }
                }
            }
            }
            if (!mSurfaceOnly && info.hwuiCallbackFired) {
            if (!mSurfaceOnly && info.hwuiCallbackFired) {
                maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
                maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
                if (!info.surfaceControlCallbackFired) {
                if (!info.surfaceControlCallbackFired) {
                    Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
                    Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
                            + ", CUJ=" + mSession.getName());
                }
                }
            }
            }
        }
        }
@@ -586,6 +644,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     * Remove all the registered listeners, observers and callbacks.
     * Remove all the registered listeners, observers and callbacks.
     */
     */
    @VisibleForTesting
    @VisibleForTesting
    @UiThread
    public void removeObservers() {
    public void removeObservers() {
        mSurfaceControlWrapper.removeJankStatsListener(this);
        mSurfaceControlWrapper.removeJankStatsListener(this);
        if (!mSurfaceOnly) {
        if (!mSurfaceOnly) {
@@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     * Trigger the prefetto daemon.
     * Trigger the prefetto daemon.
     */
     */
    public void triggerPerfetto() {
    public void triggerPerfetto() {
        InteractionJankMonitor.getInstance().trigger(mSession);
        mMonitor.trigger(mSession);
    }
    }


    /**
    /**
@@ -666,10 +725,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
            mViewRoot = viewRoot;
            mViewRoot = viewRoot;
        }
        }


        /**
         * {@link ViewRootImpl#addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)}
         * @param callback {@link ViewRootImpl.SurfaceChangedCallback}
         */
        public void addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
        public void addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
            mViewRoot.addSurfaceChangedCallback(callback);
            mViewRoot.addSurfaceChangedCallback(callback);
        }
        }


        /**
         * {@link ViewRootImpl#removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)}
         * @param callback {@link ViewRootImpl.SurfaceChangedCallback}
         */
        public void removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
        public void removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
            mViewRoot.removeSurfaceChangedCallback(callback);
            mViewRoot.removeSurfaceChangedCallback(callback);
        }
        }
+146 −82

File changed.

Preview size limit exceeded, changes collapsed.

+34 −5
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


@@ -83,6 +84,7 @@ public class FrameTrackerTest {
    private StatsLogWrapper mStatsLog;
    private StatsLogWrapper mStatsLog;
    private ArgumentCaptor<OnJankDataListener> mListenerCapture;
    private ArgumentCaptor<OnJankDataListener> mListenerCapture;
    private SurfaceControl mSurfaceControl;
    private SurfaceControl mSurfaceControl;
    private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;


    @Before
    @Before
    public void setup() {
    public void setup() {
@@ -99,6 +101,8 @@ public class FrameTrackerTest {
        mSurfaceControl = new SurfaceControl.Builder().setName("Surface").build();
        mSurfaceControl = new SurfaceControl.Builder().setName("Surface").build();
        mViewRootWrapper = mock(ViewRootWrapper.class);
        mViewRootWrapper = mock(ViewRootWrapper.class);
        when(mViewRootWrapper.getSurfaceControl()).thenReturn(mSurfaceControl);
        when(mViewRootWrapper.getSurfaceControl()).thenReturn(mSurfaceControl);
        doNothing().when(mViewRootWrapper).addSurfaceChangedCallback(any());
        doNothing().when(mViewRootWrapper).removeSurfaceChangedCallback(any());
        mSurfaceControlWrapper = mock(SurfaceControlWrapper.class);
        mSurfaceControlWrapper = mock(SurfaceControlWrapper.class);


        mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class);
        mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class);
@@ -109,23 +113,29 @@ public class FrameTrackerTest {


        mChoreographer = mock(ChoreographerWrapper.class);
        mChoreographer = mock(ChoreographerWrapper.class);
        mStatsLog = mock(StatsLogWrapper.class);
        mStatsLog = mock(StatsLogWrapper.class);
        mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
    }
    }


    private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
    private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
        InteractionJankMonitor monitor = mock(InteractionJankMonitor.class);
        Handler handler = mRule.getActivity().getMainThreadHandler();
        Handler handler = mRule.getActivity().getMainThreadHandler();
        Session session = new Session(cuj, postfix);
        Session session = new Session(cuj, postfix);
        Configuration config = mock(Configuration.class);
        Configuration config = mock(Configuration.class);
        when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
        when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
        when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
        when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
        when(config.shouldDeferMonitor()).thenReturn(true);
        when(config.shouldDeferMonitor()).thenReturn(true);
        View view = mRule.getActivity().getWindow().getDecorView();
        Handler spyHandler = spy(new Handler(handler.getLooper()));
        when(config.getView()).thenReturn(surfaceOnly ? null : view);
        when(config.getHandler()).thenReturn(spyHandler);
        FrameTracker frameTracker = Mockito.spy(
        FrameTracker frameTracker = Mockito.spy(
                new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
                new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper,
                        mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog,
                        mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog,
                        /* traceThresholdMissedFrames= */ 1,
                        /* traceThresholdMissedFrames= */ 1,
                        /* traceThresholdFrameTimeMillis= */ -1,
                        /* traceThresholdFrameTimeMillis= */ -1,
                        /* FrameTrackerListener= */ null, config));
                        /* FrameTrackerListener= */ null, config));
        doNothing().when(frameTracker).triggerPerfetto();
        doNothing().when(frameTracker).triggerPerfetto();
        doNothing().when(frameTracker).postTraceStartMarker();
        doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture());
        return frameTracker;
        return frameTracker;
    }
    }


@@ -140,6 +150,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame with a long duration - should not be taken into account
        // send first frame with a long duration - should not be taken into account
@@ -173,6 +184,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame - not janky
        // send first frame - not janky
@@ -208,6 +220,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame - janky
        // send first frame - janky
@@ -243,6 +256,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame - not janky
        // send first frame - not janky
@@ -278,6 +292,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame - not janky
        // send first frame - not janky
@@ -319,6 +334,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // send first frame - not janky
        // send first frame - not janky
@@ -332,7 +348,7 @@ public class FrameTrackerTest {
        tracker.end(FrameTracker.REASON_END_NORMAL);
        tracker.end(FrameTracker.REASON_END_NORMAL);


        // Send incomplete callback for 102L
        // Send incomplete callback for 102L
        sendSfFrame(102L, JANK_NONE);
        sendSfFrame(tracker, 102L, JANK_NONE);


        // Send janky but complete callbck fo 103L
        // Send janky but complete callbck fo 103L
        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
@@ -356,6 +372,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer).addObserver(any());
        verify(mRenderer).addObserver(any());


        // First frame - not janky
        // First frame - not janky
@@ -380,6 +397,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // end the trace session
        // end the trace session
@@ -403,6 +421,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());
        verify(mRenderer, only()).addObserver(any());


        // end the trace session at the same vsync id, end vsync id will less than the begin one.
        // end the trace session at the same vsync id, end vsync id will less than the begin one.
@@ -444,6 +463,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());


        // First frame - not janky
        // First frame - not janky
@@ -479,6 +499,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());


        // First frame - janky
        // First frame - janky
@@ -514,6 +535,7 @@ public class FrameTrackerTest {


        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());


        // First frame - not janky
        // First frame - not janky
@@ -548,6 +570,7 @@ public class FrameTrackerTest {
                CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
                CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        when(mChoreographer.getVsyncId()).thenReturn(100L);
        tracker.begin();
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
        sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
@@ -594,7 +617,7 @@ public class FrameTrackerTest {
        if (!tracker.mSurfaceOnly) {
        if (!tracker.mSurfaceOnly) {
            sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame);
            sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame);
        }
        }
        sendSfFrame(vsyncId, jankType);
        sendSfFrame(tracker, vsyncId, jankType);
    }
    }


    private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
    private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
@@ -604,12 +627,18 @@ public class FrameTrackerTest {
                .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
                .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
        final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        doNothing().when(tracker).postCallback(captor.capture());
        tracker.onFrameMetricsAvailable(0);
        tracker.onFrameMetricsAvailable(0);
        captor.getValue().run();
    }
    }


    private void sendSfFrame(long vsyncId, @JankType int jankType) {
    private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) {
        final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        doNothing().when(tracker).postCallback(captor.capture());
        mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
        mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
                new JankData(vsyncId, jankType)
                new JankData(vsyncId, jankType)
        });
        });
        captor.getValue().run();
    }
    }
}
}
+10 −5
Original line number Original line Diff line number Diff line
@@ -93,7 +93,7 @@ public class InteractionJankMonitorTest {
    @Test
    @Test
    public void testBeginEnd() {
    public void testBeginEnd() {
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        FrameTracker tracker = createMockedFrameTracker(null);
        FrameTracker tracker = createMockedFrameTracker(monitor, null);
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).begin();
        doNothing().when(tracker).begin();
        doReturn(true).when(tracker).end(anyInt());
        doReturn(true).when(tracker).end(anyInt());
@@ -134,7 +134,7 @@ public class InteractionJankMonitorTest {
    public void testBeginTimeout() {
    public void testBeginTimeout() {
        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        FrameTracker tracker = createMockedFrameTracker(null);
        FrameTracker tracker = createMockedFrameTracker(monitor, null);
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).begin();
        doNothing().when(tracker).begin();
        doReturn(true).when(tracker).cancel(anyInt());
        doReturn(true).when(tracker).cancel(anyInt());
@@ -180,7 +180,8 @@ public class InteractionJankMonitorTest {
        return monitor;
        return monitor;
    }
    }


    private FrameTracker createMockedFrameTracker(FrameTracker.FrameTrackerListener listener) {
    private FrameTracker createMockedFrameTracker(InteractionJankMonitor monitor,
            FrameTracker.FrameTrackerListener listener) {
        Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX));
        Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX));
        doReturn(false).when(session).logToStatsd();
        doReturn(false).when(session).logToStatsd();


@@ -190,6 +191,7 @@ public class InteractionJankMonitorTest {


        ViewRootWrapper viewRoot = spy(new ViewRootWrapper(mView.getViewRootImpl()));
        ViewRootWrapper viewRoot = spy(new ViewRootWrapper(mView.getViewRootImpl()));
        doNothing().when(viewRoot).addSurfaceChangedCallback(any());
        doNothing().when(viewRoot).addSurfaceChangedCallback(any());
        doNothing().when(viewRoot).removeSurfaceChangedCallback(any());


        SurfaceControlWrapper surfaceControl = mock(SurfaceControlWrapper.class);
        SurfaceControlWrapper surfaceControl = mock(SurfaceControlWrapper.class);
        doNothing().when(surfaceControl).addJankStatsListener(any(), any());
        doNothing().when(surfaceControl).addJankStatsListener(any(), any());
@@ -200,15 +202,18 @@ public class InteractionJankMonitorTest {


        Configuration configuration = mock(Configuration.class);
        Configuration configuration = mock(Configuration.class);
        when(configuration.isSurfaceOnly()).thenReturn(false);
        when(configuration.isSurfaceOnly()).thenReturn(false);
        when(configuration.getView()).thenReturn(mView);
        when(configuration.getHandler()).thenReturn(mView.getHandler());


        FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
        FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(),
                threadedRenderer, viewRoot, surfaceControl, choreographer,
                threadedRenderer, viewRoot, surfaceControl, choreographer,
                new FrameMetricsWrapper(), new StatsLogWrapper(),
                new FrameMetricsWrapper(), new StatsLogWrapper(),
                /* traceThresholdMissedFrames= */ 1,
                /* traceThresholdMissedFrames= */ 1,
                /* traceThresholdFrameTimeMillis= */ -1, listener, configuration));
                /* traceThresholdFrameTimeMillis= */ -1, listener, configuration));


        doNothing().when(tracker).postTraceStartMarker();
        doNothing().when(tracker).postTraceStartMarker(any());
        doNothing().when(tracker).triggerPerfetto();
        doNothing().when(tracker).triggerPerfetto();
        doReturn(configuration.getHandler()).when(tracker).getHandler();


        return tracker;
        return tracker;
    }
    }