Loading core/java/com/android/internal/jank/FrameTracker.java +185 −118 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 Loading @@ -220,7 +256,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener end(REASON_END_SURFACE_DESTROYED); finish(); } } }, 50); } }; Loading @@ -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()); } } } Loading @@ -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: Loading @@ -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); } Loading @@ -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)); Loading @@ -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; } Loading @@ -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); } Loading @@ -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; } Loading @@ -426,9 +477,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener frameVsyncId, totalDurationNanos, isFirstFrame)); } processJankInfos(); } }); } @UiThread private boolean hasReceivedCallbacksAfterEnd() { if (mEndVsyncId == INVALID_ID) { return false; Loading @@ -451,6 +503,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return false; } @UiThread private void processJankInfos() { if (mMetricsFinalized) { return; Loading @@ -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. Loading @@ -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; } Loading @@ -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; } Loading @@ -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()); } } } Loading Loading @@ -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) { Loading @@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener * Trigger the prefetto daemon. */ public void triggerPerfetto() { InteractionJankMonitor.getInstance().trigger(mSession); mMonitor.trigger(mSession); } /** Loading Loading @@ -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); } Loading core/java/com/android/internal/jank/InteractionJankMonitor.java +146 −82 File changed.Preview size limit exceeded, changes collapsed. Show changes core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +34 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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); Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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(); } } core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +10 −5 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading @@ -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()); Loading Loading @@ -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(); Loading @@ -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()); Loading @@ -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; } Loading Loading
core/java/com/android/internal/jank/FrameTracker.java +185 −118 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 Loading @@ -220,7 +256,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener end(REASON_END_SURFACE_DESTROYED); finish(); } } }, 50); } }; Loading @@ -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()); } } } Loading @@ -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: Loading @@ -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); } Loading @@ -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)); Loading @@ -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; } Loading @@ -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); } Loading @@ -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; } Loading @@ -426,9 +477,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener frameVsyncId, totalDurationNanos, isFirstFrame)); } processJankInfos(); } }); } @UiThread private boolean hasReceivedCallbacksAfterEnd() { if (mEndVsyncId == INVALID_ID) { return false; Loading @@ -451,6 +503,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return false; } @UiThread private void processJankInfos() { if (mMetricsFinalized) { return; Loading @@ -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. Loading @@ -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; } Loading @@ -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; } Loading @@ -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()); } } } Loading Loading @@ -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) { Loading @@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener * Trigger the prefetto daemon. */ public void triggerPerfetto() { InteractionJankMonitor.getInstance().trigger(mSession); mMonitor.trigger(mSession); } /** Loading Loading @@ -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); } Loading
core/java/com/android/internal/jank/InteractionJankMonitor.java +146 −82 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +34 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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); Loading @@ -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; } Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -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(); } }
core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +10 −5 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading @@ -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()); Loading Loading @@ -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(); Loading @@ -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()); Loading @@ -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; } Loading