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 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_END;
import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT;

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

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

    @VisibleForTesting
    public final boolean mSurfaceOnly;

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

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

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

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

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

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

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

    /**
     * End the trace session of the CUJ.
     */
    @UiThread
    public boolean end(@Reasons int reason) {
        synchronized (mLock) {
        if (mCancelled || mEndVsyncId != INVALID_ID) return false;
        mEndVsyncId = mChoreographer.getVsyncId();
        // 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());
                finish();
            };
                mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
            getHandler().postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
            notifyCujEvent(ACTION_SESSION_END);
            return true;
        }
    }
    }

    /**
     * Cancel the trace session of the CUJ.
     */
    @UiThread
    public boolean cancel(@Reasons int reason) {
        synchronized (mLock) {
        final boolean cancelFromEnd =
                reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
        if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
        mCancelled = true;
        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) {
            Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        }
@@ -350,7 +393,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        notifyCujEvent(ACTION_SESSION_CANCEL);
        return true;
    }
    }

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

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

@@ -384,10 +426,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                }
            }
            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);
    }

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

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

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

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

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

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

        // 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++;
                boolean missedFrame = false;
                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++;
                    missedFrame = true;
                }
@@ -505,7 +561,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 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++;
                    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
                // to ignore it for now.
                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) {
                maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
                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.
     */
    @VisibleForTesting
    @UiThread
    public void removeObservers() {
        mSurfaceControlWrapper.removeJankStatsListener(this);
        if (!mSurfaceOnly) {
@@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     * Trigger the prefetto daemon.
     */
    public void triggerPerfetto() {
        InteractionJankMonitor.getInstance().trigger(mSession);
        mMonitor.trigger(mSession);
    }

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

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

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

File changed.

Preview size limit exceeded, changes collapsed.

+34 −5
Original line number 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.never;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

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

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

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

    private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
        InteractionJankMonitor monitor = mock(InteractionJankMonitor.class);
        Handler handler = mRule.getActivity().getMainThreadHandler();
        Session session = new Session(cuj, postfix);
        Configuration config = mock(Configuration.class);
        when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
        when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
        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(
                new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
                new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper,
                        mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog,
                        /* traceThresholdMissedFrames= */ 1,
                        /* traceThresholdFrameTimeMillis= */ -1,
                        /* FrameTrackerListener= */ null, config));
        doNothing().when(frameTracker).triggerPerfetto();
        doNothing().when(frameTracker).postTraceStartMarker();
        doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture());
        return frameTracker;
    }

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

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

        // 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);
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mRenderer, only()).addObserver(any());

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // 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);
        tracker.begin();
        mRunnableArgumentCaptor.getValue().run();
        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());

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

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

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

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

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

    private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
@@ -604,12 +627,18 @@ public class FrameTrackerTest {
                .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
        final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        doNothing().when(tracker).postCallback(captor.capture());
        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[] {
                new JankData(vsyncId, jankType)
        });
        captor.getValue().run();
    }
}
+10 −5
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ public class InteractionJankMonitorTest {
    @Test
    public void testBeginEnd() {
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        FrameTracker tracker = createMockedFrameTracker(null);
        FrameTracker tracker = createMockedFrameTracker(monitor, null);
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).begin();
        doReturn(true).when(tracker).end(anyInt());
@@ -134,7 +134,7 @@ public class InteractionJankMonitorTest {
    public void testBeginTimeout() {
        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
        InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
        FrameTracker tracker = createMockedFrameTracker(null);
        FrameTracker tracker = createMockedFrameTracker(monitor, null);
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).begin();
        doReturn(true).when(tracker).cancel(anyInt());
@@ -180,7 +180,8 @@ public class InteractionJankMonitorTest {
        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));
        doReturn(false).when(session).logToStatsd();

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

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

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

        Configuration configuration = mock(Configuration.class);
        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,
                new FrameMetricsWrapper(), new StatsLogWrapper(),
                /* traceThresholdMissedFrames= */ 1,
                /* traceThresholdFrameTimeMillis= */ -1, listener, configuration));

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

        return tracker;
    }